/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "alsa_soundcard.h"
#include "alsa_snd_render.h"
#include "osal_time.h"

#define HDF_LOG_TAG HDF_AUDIO_HAL_RENDER

#define MAX_PERIOD_SIZE                (8 * 1024)
#define MIN_PERIOD_SIZE                (4 * 1024)
#define AUDIO_RENDER_RECOVER_DELAY     (10 * 1000)
#define POLL_EVENT_DEF false
#define AUDIO_BUFFER_TIME_DEF 500000
#define AUDIO_PERIOD_TIME_DEF 100000
#define AUDIO_PERIOD_TIME_RATIO 4
#define BIT_COUNT_OF_BYTE       8
#define PCM_WAIT_TIMEOUT_MS     100
#ifdef SUPPORT_ALSA_CHMAP
#define CHMAP_NAME_LENGHT_MAX      256

/* channel map list type */
#define CHANNEL_MAP_TYPE_FIXED    "FIXED"  /* fixed channel position */
#define CHANNEL_MAP_TYPE_VAR      "VAR"    /* freely swappable channel position */
#define CHANNEL_MAP_TYPE_PAIRED   "PAIRED" /* pair-wise swappable channel position */
#endif

static struct AlsaRender *g_alsaRenderList = NULL;
static void RegisterRenderImpl(struct AlsaRender *renderIns);

void RenderSetPriData(struct AlsaRender *renderIns, RenderPriData data)
{
    renderIns->priData = data;
}

RenderPriData RenderGetPriData(struct AlsaRender *renderIns)
{
    return renderIns->priData;
}

static int32_t CreateRenderIns(void)
{
    if (g_alsaRenderList == NULL) {
        g_alsaRenderList = (struct AlsaRender *)OsalMemCalloc(MAX_CARD_NUM * sizeof(struct AlsaRender));
        if (g_alsaRenderList == NULL) {
            AUDIO_FUNC_LOGE("Failed to allocate memory!");
            return HDF_FAILURE;
        }
    }
    return HDF_SUCCESS;
}

static int32_t RenderFreeMemory(void)
{
    if (g_alsaRenderList != NULL) {
        for (int32_t i = 0; i < MAX_CARD_NUM; i++) {
            if (g_alsaRenderList[i].soundCard.cardStatus != 0) {
                AUDIO_FUNC_LOGE("refCount is not zero, Sound card in use!");
                return HDF_ERR_DEVICE_BUSY;
            }

            if (g_alsaRenderList[i].priData != NULL) {
                OsalMemFree(g_alsaRenderList[i].priData);
                g_alsaRenderList[i].priData = NULL;
            }
        }
        AudioMemFree((void **)&g_alsaRenderList);
        g_alsaRenderList = NULL;
    }

    return HDF_SUCCESS;
}

static int32_t SetHWParamsSub(
    snd_pcm_t *handle, snd_pcm_hw_params_t *params, const struct AudioPcmHwParams *hwParams, snd_pcm_access_t access)
{
    snd_pcm_format_t pcmFormat = SND_PCM_FORMAT_S16_LE;
    CHECK_NULL_PTR_RETURN_DEFAULT(handle);
    CHECK_NULL_PTR_RETURN_DEFAULT(params);

    /* set hardware resampling,enable alsa-lib resampling */
    int32_t ret = snd_pcm_hw_params_set_rate_resample(handle, params, 1);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Resampling setup failed for playback: %{public}s", snd_strerror(ret));
        return HDF_FAILURE;
    }
    /* set the interleaved read/write format */
    ret = snd_pcm_hw_params_set_access(handle, params, access);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Access type not available for playback: %{public}s", snd_strerror(ret));
        return HDF_FAILURE;
    }
    ret = SndConverAlsaPcmFormat(hwParams, &pcmFormat);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("SndConverAlsaPcmFormat error.");
        return HDF_FAILURE;
    }
    /* set the sample format */
    ret = snd_pcm_hw_params_set_format(handle, params, pcmFormat);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Sample format not available for playback: %{public}s, format: %{public}d",
            snd_strerror(ret), pcmFormat);
        return HDF_FAILURE;
    }
    /* set the count of channels */
    ret = snd_pcm_hw_params_set_channels(handle, params, hwParams->channels);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Channels count (%{public}u) not available for playbacks: %{public}s", hwParams->channels,
            snd_strerror(ret));
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

static int32_t SetHWRate(snd_pcm_t *handle, snd_pcm_hw_params_t *params, uint32_t *rate)
{
    int dir = 0; /* dir Value range (-1,0,1) */
    CHECK_NULL_PTR_RETURN_DEFAULT(handle);
    CHECK_NULL_PTR_RETURN_DEFAULT(params);
    CHECK_NULL_PTR_RETURN_DEFAULT(rate);

    /* set the stream rate */
    uint32_t rRate = *rate;
    int32_t ret = snd_pcm_hw_params_set_rate_near(handle, params, &rRate, &dir);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Rate %{public}uHz not available for playback: %{public}s.", *rate, snd_strerror(ret));
        return HDF_FAILURE;
    }

    if (rRate != *rate) {
        ret = snd_pcm_hw_params_set_rate_near(handle, params, &rRate, &dir);
        if (ret < 0) {
            AUDIO_FUNC_LOGE("Rate %{public}uHz not available for playback: %{public}s.", *rate, snd_strerror(ret));
            return HDF_FAILURE;
        }
    }
    /* Update to hardware supported rate */
    *rate = rRate;

    return HDF_SUCCESS;
}

static int32_t SetHWParams(struct AlsaSoundCard *cardIns, snd_pcm_access_t access)
{
    int dir = 0; /* dir Value range (-1,0,1) */
    snd_pcm_uframes_t size;
    snd_pcm_hw_params_t *hwParams = NULL;
    struct AlsaRender *renderIns = (struct AlsaRender*)cardIns;
    CHECK_NULL_PTR_RETURN_DEFAULT(cardIns->pcmHandle);

    snd_pcm_hw_params_alloca(&hwParams);
    if (snd_pcm_hw_params_any(cardIns->pcmHandle, hwParams) < 0) {
        AUDIO_FUNC_LOGE("No configurations available");
        return HDF_FAILURE;
    }
    if (SetHWParamsSub(cardIns->pcmHandle, hwParams, &cardIns->hwParams, access) != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("SetHWParamsSub failed!");
        return HDF_FAILURE;
    }
    if (SetHWRate(cardIns->pcmHandle, hwParams, &(cardIns->hwParams.rate)) != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("SetHWRate failed!");
        return HDF_FAILURE;
    }
    snd_pcm_hw_params_get_buffer_time_max(hwParams, &renderIns->bufferTime, &dir);
    if (renderIns->bufferTime > AUDIO_BUFFER_TIME_DEF) {
        renderIns->bufferTime = AUDIO_BUFFER_TIME_DEF;
    }
    renderIns->periodTime = renderIns->bufferTime / AUDIO_PERIOD_TIME_RATIO;
    if (snd_pcm_hw_params_set_buffer_time_near(cardIns->pcmHandle, hwParams, &renderIns->bufferTime, &dir) < 0) {
        AUDIO_FUNC_LOGE("Set buffer time %{public}u failed", renderIns->bufferTime);
        return HDF_FAILURE;
    }
    if (snd_pcm_hw_params_get_buffer_size(hwParams, &size) < 0) {
        AUDIO_FUNC_LOGE("Unable to get buffer size for playback");
        return HDF_FAILURE;
    }
    renderIns->bufferSize = size;
    if (snd_pcm_hw_params_set_period_time_near(cardIns->pcmHandle, hwParams, &renderIns->periodTime, &dir) < 0) {
        AUDIO_FUNC_LOGE("Set period time %{public}u failed", renderIns->bufferTime);
        return HDF_FAILURE;
    }
    if (snd_pcm_hw_params_get_period_size(hwParams, &size, &dir) < 0) {
        AUDIO_FUNC_LOGE("Unable to get period size for playback");
        return HDF_FAILURE;
    }
    renderIns->periodSize = size;
    if (snd_pcm_hw_params(cardIns->pcmHandle, hwParams) < 0) {
        AUDIO_FUNC_LOGE("Unable to set hw params for playback");
        return HDF_FAILURE;
    }

    cardIns->canPause = snd_pcm_hw_params_can_pause(hwParams);
    return HDF_SUCCESS;
}

static int32_t SetSWParams(struct AlsaSoundCard *cardIns)
{
    snd_pcm_sw_params_t *swParams = NULL;
    snd_pcm_t *handle = cardIns->pcmHandle;
    struct AlsaRender *renderIns = (struct AlsaRender *)cardIns;
    CHECK_NULL_PTR_RETURN_DEFAULT(handle);

    snd_pcm_sw_params_alloca(&swParams);

    /* get the current swparams */
    int32_t ret = snd_pcm_sw_params_current(handle, swParams);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Unable to determine current swparams for playback: %{public}s", snd_strerror(ret));
        return HDF_FAILURE;
    }
    /* start the transfer when the buffer is almost full: */
    /* (buffer_size / avail_min) * avail_min */
    if (renderIns->periodSize == 0) {
        AUDIO_FUNC_LOGE("g_periodSize=0");
        return HDF_FAILURE;
    }
    ret = snd_pcm_sw_params_set_start_threshold(handle, swParams,
        (renderIns->bufferSize / renderIns->periodSize) * renderIns->periodSize);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Unable to set start threshold mode for playback: %{public}s", snd_strerror(ret));
        return HDF_FAILURE;
    }
    /* allow the transfer when at least period_size samples can be processed */
    /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
    ret = snd_pcm_sw_params_set_avail_min(handle, swParams,
        renderIns->periodEvent ? renderIns->bufferSize : renderIns->periodSize);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Unable to set avail min for playback: %{public}s", snd_strerror(ret));
        return HDF_FAILURE;
    }

    /* enable period events when requested */
    if (renderIns->periodEvent) {
        int32_t val = 1; /* val 0 = disable period event, 1 = enable period event */
        ret = snd_pcm_sw_params_set_period_event(handle, swParams, val);
        if (ret < 0) {
            AUDIO_FUNC_LOGE("Unable to set period event: %{public}s", snd_strerror(ret));
            return HDF_FAILURE;
        }
    }

    /* write the parameters to the playback device */
    ret = snd_pcm_sw_params(handle, swParams);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Unable to set sw params for playback: %{public}s", snd_strerror(ret));
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

static int32_t ResetRenderParams(struct AlsaSoundCard *cardIns, snd_pcm_access_t access)
{
    CHECK_NULL_PTR_RETURN_DEFAULT(cardIns);

    int32_t ret = SetHWParams(cardIns, access);
    if (ret != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("Setting of hwparams failed.");
        return ret;
    }

    ret = SetSWParams(cardIns);
    if (ret != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("Setting of swparams failed.");
        return ret;
    }

    return HDF_SUCCESS;
}

static struct AlsaRender *GetRenderInsByName(const char *adapterName)
{
    struct AlsaRender *renderIns = NULL;
    struct AlsaSoundCard *alsaSnd = NULL;

    /*
    fine the instance with the corresponding adapter name, or create one if none.
    */
    for (int32_t i = 0; i < MAX_CARD_NUM; i++) {
        alsaSnd = (struct AlsaSoundCard *)&g_alsaRenderList[i];
        if (alsaSnd->cardStatus) {
            if (0 == strcmp(alsaSnd->adapterName, adapterName)) {
                return &g_alsaRenderList[i];
            }
        }
    }

    for (int32_t i = 0; i < MAX_CARD_NUM; i++) {
        renderIns = &g_alsaRenderList[i];
        alsaSnd = (struct AlsaSoundCard *)&g_alsaRenderList[i];
        if (alsaSnd->cardStatus == 0) {
            (void)memset_s(renderIns, sizeof(struct AlsaRender), 0, sizeof(struct AlsaRender));
            int32_t ret = strncpy_s(alsaSnd->adapterName, MAX_CARD_NAME_LEN + 1, adapterName, strlen(adapterName));
            if (ret != 0) {
                AUDIO_FUNC_LOGE("strncpy_s failed!");
                return NULL;
            }
            alsaSnd->cardStatus++;
            renderIns->periodEvent = POLL_EVENT_DEF;
            renderIns->periodTime = AUDIO_PERIOD_TIME_DEF;
            renderIns->bufferTime = AUDIO_BUFFER_TIME_DEF;
            renderIns->descPins = PIN_NONE;
            renderIns->resample = 1;
            return renderIns;
        }
    }
    AUDIO_FUNC_LOGE("Failed to AddCardIns!");
    return NULL;
}

struct AlsaRender *RenderCreateInstance(const char* adapterName)
{
    struct AlsaRender *renderIns = NULL;
    if (adapterName == NULL || strlen(adapterName) == 0) {
        AUDIO_FUNC_LOGE("Invalid adapterName!");
        return NULL;
    }

    int32_t ret = CreateRenderIns();
    if (ret != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("Failed to create render instance.");
        return NULL;
    }

    renderIns = GetRenderInsByName(adapterName);
    if (renderIns == NULL) {
        AUDIO_FUNC_LOGE("get render instance failed.");
        return NULL;
    }
    RegisterRenderImpl(renderIns);

    ret = SndSaveCardListInfo(SND_PCM_STREAM_PLAYBACK);
    if (ret != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("Failed to save card device info.");
        return NULL;
    }

    ret = SndMatchSelAdapter(&renderIns->soundCard, adapterName);
    if (ret != HDF_SUCCESS) {
        SndCloseHandle(&renderIns->soundCard);
        RenderFreeMemory();
        return NULL;
    }

    RenderOverrideFunc(renderIns);
    return renderIns;
}

struct AlsaRender *RenderGetInstance(const char *adapterName)
{
    if (adapterName == NULL || strlen(adapterName) == 0) {
        AUDIO_FUNC_LOGE("Invalid adapterName!");
        return NULL;
    }

    if (g_alsaRenderList == NULL) {
        AUDIO_FUNC_LOGE("g_alsaRenderList is NULL!");
        return NULL;
    }

    for (int32_t i = 0; i < MAX_CARD_NUM; i++) {
        if (strcmp(g_alsaRenderList[i].soundCard.adapterName, adapterName) == 0) {
            return &(g_alsaRenderList[i]);
        }
    }

    return NULL;
}

#ifdef SUPPORT_ALSA_CHMAP
static int32_t GetChannelsNameFromUser(struct AlsaSoundCard *cardIns, const char *channelsName)
{
    if (channelsName == NULL) {
        AUDIO_FUNC_LOGE("channelsName is NULL!");
        return HDF_ERR_INVALID_PARAM;
    }

    if (cardIns->hwParams.channelsName == NULL) {
        cardIns->hwParams.channelsName = (char *)OsalMemCalloc(CHMAP_NAME_LENGHT_MAX);
        if (cardIns->hwParams.channelsName == NULL) {
            AUDIO_FUNC_LOGE("Failed to allocate memory!");
            return HDF_ERR_MALLOC_FAIL;
        }
    }

    (void)memset_s(cardIns->hwParams.channelsName, CHMAP_NAME_LENGHT_MAX, 0, CHMAP_NAME_LENGHT_MAX);
    int32_t ret = strncpy_s(cardIns->hwParams.channelsName, CHMAP_NAME_LENGHT_MAX - 1,
        channelsName, strlen(channelsName));
    if (ret != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("strncpy_s failed!");
        AudioMemFree((void **)&(cardIns->hwParams.channelsName));
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}
#endif

 static int32_t SaveHwParams(struct AlsaSoundCard *cardIns, const struct AudioHwRenderParam *handleData)
{
    CHECK_NULL_PTR_RETURN_DEFAULT(cardIns);
    CHECK_NULL_PTR_RETURN_DEFAULT(handleData);

    cardIns->hwParams.streamType = AUDIO_RENDER_STREAM;
    cardIns->hwParams.channels = handleData->frameRenderMode.attrs.channelCount;
    cardIns->hwParams.rate = handleData->frameRenderMode.attrs.sampleRate;
    cardIns->hwParams.periodSize = handleData->frameRenderMode.periodSize;
    cardIns->hwParams.periodCount = handleData->frameRenderMode.periodCount;
    cardIns->hwParams.format = handleData->frameRenderMode.attrs.format;
    cardIns->hwParams.period = handleData->frameRenderMode.attrs.period;
    cardIns->hwParams.frameSize = handleData->frameRenderMode.attrs.frameSize;
    cardIns->hwParams.isBigEndian = handleData->frameRenderMode.attrs.isBigEndian;
    cardIns->hwParams.isSignedData = handleData->frameRenderMode.attrs.isSignedData;
    cardIns->hwParams.startThreshold = handleData->frameRenderMode.attrs.startThreshold;
    cardIns->hwParams.stopThreshold = handleData->frameRenderMode.attrs.stopThreshold;
    cardIns->hwParams.silenceThreshold = handleData->frameRenderMode.attrs.silenceThreshold;
#ifdef SUPPORT_ALSA_CHMAP
    /* param 2 by handleData->frameRenderMode.attrs.channelsName, sample channelsName is "FL, FR" */
    if (GetChannelsNameFromUser(cardIns, "FL, FR") != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("GetChannelsNameFromUser failed");
        return HDF_FAILURE;
    }
#endif

    return HDF_SUCCESS;
}

#ifdef SUPPORT_ALSA_CHMAP
static void PrintChannels(const snd_pcm_chmap_t *map)
{
    char tmp[CHMAP_NAME_LENGHT_MAX] = {0};
    if (snd_pcm_chmap_print(map, sizeof(tmp), tmp) > 0) {
        HDF_LOGI("print_channels: %{public}s.", tmp);
    }
}

static int32_t QueryChmaps(snd_pcm_t *pcm)
{
    snd_pcm_chmap_query_t **pChmap = NULL;
    snd_pcm_chmap_query_t *chmap = NULL;
    const char *champType = NULL;
    snd_pcm_chmap_query_t **hwChmap = snd_pcm_query_chmaps(pcm);
    if (hwChmap == NULL) {
        AUDIO_FUNC_LOGE("This sound card has no chmap component, cannot query maps.");
        return HDF_FAILURE;
    }

    for (pChmap = hwChmap; (chmap = *pChmap) != NULL; pChmap++) {
        champType = snd_pcm_chmap_type_name(chmap->type);
        HDF_LOGI("Channel Type = %{public}s, Channels = %{public}d.", champType, chmap->map.channels);
        if (strncmp(champType, CHANNEL_MAP_TYPE_FIXED, strlen(CHANNEL_MAP_TYPE_FIXED)) == 0) {
            HDF_LOGW("Fixed channel type does not support modification temporarily!");
        }
        PrintChannels(&chmap->map);
    }

    snd_pcm_free_chmaps(hwChmap);
    return HDF_SUCCESS;
}

static int32_t SetChmap(snd_pcm_t *pcm, struct AudioPcmHwParams *hwRenderParams)
{
    if (hwRenderParams == NULL || hwRenderParams->channelsName == NULL) {
        AUDIO_FUNC_LOGE("Parameter is NULL!");
        return HDF_FAILURE;
    }

    snd_pcm_chmap_t *chmap = snd_pcm_chmap_parse_string(hwRenderParams->channelsName);
    if (chmap == NULL) {
        AUDIO_FUNC_LOGE("parse chmap error!");
        return HDF_FAILURE;
    }

    if (snd_pcm_set_chmap(pcm, chmap) < 0) {
        AUDIO_FUNC_LOGE("Cannot set chmap!");
        free((void *)chmap);
        return HDF_ERR_NOT_SUPPORT;
    }
    free((void *)chmap);

    chmap = snd_pcm_get_chmap(pcm);
    if (chmap == NULL) {
        AUDIO_FUNC_LOGE("Cannot get chmap!");
        return HDF_ERR_NOT_SUPPORT;
    }

    PrintChannels(chmap);
    free((void *)chmap);
    return HDF_SUCCESS;
}

static int32_t RenderHwParamsChmaps(struct AlsaSoundCard *cardIns)
{
    if (QueryChmaps(cardIns->pcmHandle) != HDF_SUCCESS) {
        AUDIO_FUNC_LOGW("QueryChmaps failed.");
        return HDF_SUCCESS;
    }
    if (SetChmap(cardIns->pcmHandle, &cardIns->hwParams) != HDF_SUCCESS) {
        AUDIO_FUNC_LOGW("SetChmap failed.");
    }

    return HDF_SUCCESS;
}
#endif

int32_t RenderSetParams(struct AlsaRender *renderIns, const struct AudioHwRenderParam *handleData)
{
    struct AlsaSoundCard *cardIns = (struct AlsaSoundCard *)renderIns;
    CHECK_NULL_PTR_RETURN_DEFAULT(renderIns);
    CHECK_NULL_PTR_RETURN_DEFAULT(handleData);

    SaveHwParams(&renderIns->soundCard, handleData);
    int32_t ret = SetHWParams(cardIns, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Setting of hwparams failed.");
        return HDF_FAILURE;
    }

    ret = SetSWParams(cardIns);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Setting of swparams failed.");
        return HDF_FAILURE;
    }

#ifdef SUPPORT_ALSA_CHMAP
    ret = RenderHwParamsChmaps(&renderIns->soundCard);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("Setting of chmaps failed.");
    }
#endif
    snd_pcm_format_t fmt;
    SndConverAlsaPcmFormat(&cardIns->hwParams, &fmt);
    int bitsPerSample = snd_pcm_format_physical_width(fmt);
    cardIns->hwParams.bitsPerFrame = bitsPerSample * cardIns->hwParams.channels;

    return HDF_SUCCESS;
}

static int32_t RenderWritei(snd_pcm_t *pcm, const struct AudioHwRenderParam *handleData,
    const struct AudioPcmHwParams *hwParams)
{
    int32_t ret = HDF_SUCCESS;
    int32_t tryNum = AUDIO_ALSALIB_RETYR;

    /* Check whether the PCM status is normal */
    snd_pcm_state_t state = snd_pcm_state(pcm);
    if (state == SND_PCM_STATE_SETUP) {
        ret = snd_pcm_prepare(pcm);
        if (ret < 0) {
            AUDIO_FUNC_LOGE("snd_pcm_prepare fail: %{public}s", snd_strerror(ret));
            return HDF_FAILURE;
        }
    }

    size_t sbufFrameSize = (size_t)handleData->frameRenderMode.bufferFrameSize;
    char *dataBuf = handleData->frameRenderMode.buffer;
    int32_t offset = hwParams->bitsPerFrame / BIT_COUNT_OF_BYTE;
    while (sbufFrameSize > 0) {
        long frames = snd_pcm_writei(pcm, dataBuf, sbufFrameSize);
        if (frames > 0) {
            sbufFrameSize -= frames;
            dataBuf += frames * offset;
        } else if (frames == -EAGAIN) {
            snd_pcm_wait(pcm, PCM_WAIT_TIMEOUT_MS);
            tryNum--;
            if (tryNum == 0) {
                return HDF_SUCCESS;
            }
        } else if (frames == -EBADFD) {
            /* not #SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING */
            AUDIO_FUNC_LOGE("render PCM is not in the right state: %{public}s", snd_strerror(frames));
            snd_pcm_prepare(pcm);
            return HDF_FAILURE;
        } else {
            /* -ESTRPIPE: a suspend event occurred,
             * stream is suspended and waiting for an application recovery.
             * -EPIPE: an underrun occurred.
             */
            AUDIO_FUNC_LOGI("err: %{public}s", snd_strerror(ret));
            ret = snd_pcm_recover(pcm, frames, 0); // 0 for open render recover log.
            if (ret < 0) {
                AUDIO_FUNC_LOGE("snd_pcm_writei failed: %{public}s", snd_strerror(ret));
                return HDF_FAILURE;
            }
        }
    }

    return HDF_SUCCESS;
}

static int32_t RenderWriteiMmap(struct AlsaSoundCard *cardIns, const struct AudioHwRenderParam *handleData)
{
    uint32_t looper = 0;
    int32_t count = 0;
    struct AudioMmapBufferDescriptor *mmapBufDesc = NULL;
    CHECK_NULL_PTR_RETURN_DEFAULT(cardIns);
    CHECK_NULL_PTR_RETURN_DEFAULT(handleData);

    cardIns->mmapFlag = false;
    int32_t ret = ResetRenderParams(cardIns, SND_PCM_ACCESS_MMAP_INTERLEAVED);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("AudioSetParamsMmap failed!");
        return HDF_FAILURE;
    }

    uint32_t frameSize = cardIns->hwParams.channels * cardIns->hwParams.format;
    if (frameSize == 0) {
        AUDIO_FUNC_LOGE("frame size = 0!");
        return HDF_FAILURE;
    }
    mmapBufDesc = (struct AudioMmapBufferDescriptor *)&(handleData->frameRenderMode.mmapBufDesc);
    uint32_t totalSize = (uint32_t)mmapBufDesc->totalBufferFrames * frameSize;
    uint32_t lastBuffSize = ((totalSize % MIN_PERIOD_SIZE) == 0) ? MIN_PERIOD_SIZE : (totalSize % MIN_PERIOD_SIZE);
    uint32_t loopTimes = (lastBuffSize == MIN_PERIOD_SIZE) ?
        (totalSize / MIN_PERIOD_SIZE) : (totalSize / MIN_PERIOD_SIZE + 1);
    while (looper < loopTimes) {
        uint32_t copyLen = (looper < (loopTimes - 1)) ? MIN_PERIOD_SIZE : lastBuffSize;
        snd_pcm_uframes_t frames = (snd_pcm_uframes_t)(copyLen / frameSize);
        ret = snd_pcm_mmap_writei(
            cardIns->pcmHandle, (char *)mmapBufDesc->memoryAddress + mmapBufDesc->offset, frames);
        if (ret == -EAGAIN) {
            count++;
            if (count > AUDIO_ALSALIB_MMAP_MAX) {
                AUDIO_FUNC_LOGE("loop > max !");
                return HDF_FAILURE;
            }
            continue;
        }
        count = 0;
        if (ret < 0) {
            AUDIO_FUNC_LOGE("Write error: %{public}s\n", snd_strerror(ret));
            return HDF_FAILURE;
        }
        looper++;
        mmapBufDesc->offset += copyLen;
        cardIns->mmapFrames += (uint64_t)frames;
    }

    return HDF_SUCCESS;
}

static int32_t RenderOpenImpl(struct AlsaRender *renderIns)
{
    struct AlsaSoundCard *cardIns = (struct AlsaSoundCard *)renderIns;
    CHECK_NULL_PTR_RETURN_DEFAULT(renderIns);

    if (SndisBusy(&renderIns->soundCard)) {
        AUDIO_FUNC_LOGE("Resource busy!!");
        return HDF_ERR_DEVICE_BUSY;
    }

    int32_t ret = snd_pcm_open(&cardIns->pcmHandle, cardIns->devName,
        SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("snd_pcm_open fail: %{public}s!", snd_strerror(ret));
        RenderFreeMemory();
        return HDF_FAILURE;
    }

    ret = snd_pcm_nonblock(cardIns->pcmHandle, 1);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("snd_pcm_nonblock fail: %{public}s!", snd_strerror(ret));
        SndCloseHandle(&renderIns->soundCard);
        RenderFreeMemory();
        return HDF_FAILURE;
    }

    ret = SndOpenMixer(&renderIns->soundCard);
    if (ret != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("SndOpenMixer failed");
        SndCloseHandle(&renderIns->soundCard);
        RenderFreeMemory();
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

static int32_t RenderCloseImpl(struct AlsaRender *renderIns)
{
    CHECK_NULL_PTR_RETURN_DEFAULT(renderIns);
#ifdef SUPPORT_ALSA_CHMAP
    AudioMemFree((void **)&renderIns->soundCard.hwParams.channelsName);
#endif
    SndCloseHandle(&renderIns->soundCard);
    RenderFreeMemory();
    return HDF_SUCCESS;
}

int32_t RenderWriteImpl(struct AlsaRender *renderIns, const struct AudioHwRenderParam *handleData)
{
    int32_t ret = HDF_SUCCESS;
    struct AlsaSoundCard *cardIns = (struct AlsaSoundCard*)renderIns;
    CHECK_NULL_PTR_RETURN_DEFAULT(renderIns);

    if (cardIns->pauseState) {
        AUDIO_FUNC_LOGE("Currently in pause, please check!");
        return HDF_FAILURE;
    }

    if (!cardIns->mmapFlag) {
        ret = ResetRenderParams(cardIns, SND_PCM_ACCESS_RW_INTERLEAVED);
        if (ret < 0) {
            AUDIO_FUNC_LOGE("ResetRenderParams failed!");
            return HDF_FAILURE;
        }
        cardIns->mmapFlag = true;
    }

    ret = RenderWritei(cardIns->pcmHandle, handleData, &cardIns->hwParams);
    if (ret != HDF_SUCCESS) {
        AUDIO_FUNC_LOGE("RenderWritei failed!");
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

int32_t RenderGetMmapPositionImpl(struct AlsaRender *renderIns)
{
    return renderIns->soundCard.mmapFrames;
}

int32_t RenderMmapWriteImpl(struct AlsaRender *renderIns, const struct AudioHwRenderParam *handleData)
{
    struct AlsaSoundCard *cardIns = (struct AlsaSoundCard *)renderIns;
    CHECK_NULL_PTR_RETURN_DEFAULT(renderIns);
    CHECK_NULL_PTR_RETURN_DEFAULT(handleData);

    if (cardIns->pauseState) {
        AUDIO_FUNC_LOGE("Currently in pause, please check!");
        return HDF_FAILURE;
    }

    cardIns->mmapFlag = false;
    int32_t ret = ResetRenderParams(cardIns, SND_PCM_ACCESS_MMAP_INTERLEAVED);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("ResetRenderParams failed!");
        return HDF_FAILURE;
    }

    ret = RenderWriteiMmap(cardIns, handleData);
    if (ret < 0) {
        AUDIO_FUNC_LOGE("RenderWriteiMmap error!");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

static int32_t RenderInitImpl(struct AlsaRender* renderIns)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderSelectSceneImpl(struct AlsaRender *renderIns, enum AudioPortPin descPins,
    const struct PathDeviceInfo *deviceInfo)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderStartImpl(struct AlsaRender *renderIns)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderStopImpl(struct AlsaRender *renderIns)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderGetVolThresholdImpl(struct AlsaRender *renderIns, long *volMin, long *volMax)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderGetVolumeImpl(struct AlsaRender *renderIns, long *volume)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderSetVolumeImpl(struct AlsaRender *renderIns, long volume)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderGetGainThresholdImpl(struct AlsaRender *renderIns, float *gainMin, float *gainMax)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderGetGainImpl(struct AlsaRender *renderIns, float *volume)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderSetGainImpl(struct AlsaRender *renderIns, float volume)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static bool RenderGetMuteImpl(struct AlsaRender *renderIns)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return false;
}

static int32_t RenderSetMuteImpl(struct AlsaRender *renderIns, bool muteFlag)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderSetPauseStateImpl(struct AlsaRender *renderIns, bool pauseFlag)
{
    int enable = pauseFlag ? AUDIO_ALSALIB_IOCTRL_PAUSE : AUDIO_ALSALIB_IOCTRL_RESUME;
    struct AlsaSoundCard *cardIns = (struct AlsaSoundCard *)renderIns;

    if (cardIns->canPause) {
        int32_t ret = snd_pcm_pause(cardIns->pcmHandle, enable);
        if (ret < 0) {
            AUDIO_FUNC_LOGE("snd_pcm_pause failed!");
            return HDF_FAILURE;
        }
    } else {
        if (enable == AUDIO_ALSALIB_IOCTRL_PAUSE) {
            snd_pcm_drain(cardIns->pcmHandle);
        } else {
            snd_pcm_prepare(cardIns->pcmHandle);
        }
    }
    cardIns->pauseState = pauseFlag;

    return HDF_SUCCESS;
}

static int32_t RenderGetChannelModeImpl(struct AlsaRender *renderIns, enum AudioChannelMode *mode)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static int32_t RenderSetChannelModeImpl(struct AlsaRender *renderIns, enum AudioChannelMode mode)
{
    AUDIO_FUNC_LOGE("Not yet realized");
    return HDF_SUCCESS;
}

static void RegisterRenderImpl(struct AlsaRender *renderIns)
{
    if (renderIns == NULL) {
        AUDIO_FUNC_LOGE("renderIns is NULL!");
        return;
    }

    renderIns->Init = RenderInitImpl;
    renderIns->Open = RenderOpenImpl;
    renderIns->SelectScene = RenderSelectSceneImpl;
    renderIns->Start = RenderStartImpl;
    renderIns->Stop = RenderStopImpl;
    renderIns->Close = RenderCloseImpl;
    renderIns->Write = RenderWriteImpl;
    renderIns->MmapWrite = RenderMmapWriteImpl;
    renderIns->GetMmapPosition = RenderGetMmapPositionImpl;
    renderIns->GetVolThreshold = RenderGetVolThresholdImpl;
    renderIns->GetVolume = RenderGetVolumeImpl;
    renderIns->SetVolume = RenderSetVolumeImpl;
    renderIns->GetGainThreshold = RenderGetGainThresholdImpl;
    renderIns->GetGain = RenderGetGainImpl;
    renderIns->SetGain = RenderSetGainImpl;
    renderIns->GetMute = RenderGetMuteImpl;
    renderIns->SetMute = RenderSetMuteImpl;
    renderIns->SetPauseState = RenderSetPauseStateImpl;
    renderIns->GetChannelMode = RenderGetChannelModeImpl;
    renderIns->SetChannelMode = RenderSetChannelModeImpl;
}