/****************************************************************************** * * Copyright (C) 2015 The Android Open Source Project * * 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. * ***************************************************************************** * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore */ /** ******************************************************************************* * @file * ih264_list.c * * @brief * Contains functions for buf queue * * @author * Harish * * @par List of Functions: * ih264_list_size() * ih264_list_lock() * ih264_list_unlock() * ih264_list_yield() * ih264_list_free() * ih264_list_init() * ih264_list_reset() * ih264_list_deinit() * ih264_list_terminate() * ih264_list_queue() * ih264_list_dequeue() * * @remarks * None * ******************************************************************************* */ /*****************************************************************************/ /* File Includes */ /*****************************************************************************/ #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include "ih264_typedefs.h" #include "ithread.h" #include "ih264_platform_macros.h" #include "ih264_macros.h" #include "ih264_debug.h" #include "ih264_error.h" #include "ih264_list.h" /** ******************************************************************************* * * @brief Returns size for buf queue context. Does not include buf queue buffer * requirements * * @par Description * Returns size for buf queue context. Does not include buf queue buffer * requirements. Buffer size required to store the bufs should be allocated in * addition to the value returned here. * * @returns Size of the buf queue context * * @remarks * ******************************************************************************* */ WORD32 ih264_list_size(WORD32 num_entries, WORD32 entry_size) { WORD32 size; WORD32 clz; size = sizeof(list_t); size += ithread_get_mutex_lock_size(); /* Use next power of two number of entries*/ clz = CLZ(num_entries); num_entries = 1 << (32 - clz); size += num_entries * entry_size; return size; } /** ******************************************************************************* * * @brief * Locks the list context * * @par Description * Locks the list context by calling ithread_mutex_lock() * * @param[in] ps_list * Job Queue context * * @returns IH264_FAIL if mutex lock fails else IH264_SUCCESS * * @remarks * ******************************************************************************* */ IH264_ERROR_T ih264_list_lock(list_t *ps_list) { WORD32 retval; retval = ithread_mutex_lock(ps_list->pv_mutex); if(retval) { return IH264_FAIL; } return IH264_SUCCESS; } /** ******************************************************************************* * * @brief * Unlocks the list context * * @par Description * Unlocks the list context by calling ithread_mutex_unlock() * * @param[in] ps_list * Job Queue context * * @returns IH264_FAIL if mutex unlock fails else IH264_SUCCESS * * @remarks * ******************************************************************************* */ IH264_ERROR_T ih264_list_unlock(list_t *ps_list) { WORD32 retval; retval = ithread_mutex_unlock(ps_list->pv_mutex); if(retval) { return IH264_FAIL; } return IH264_SUCCESS; } /** ******************************************************************************* * * @brief * Yields the thread * * @par Description * Unlocks the list context by calling * ih264_list_unlock(), ithread_yield() and then ih264_list_lock() * list is unlocked before to ensure the list can be accessed by other threads * If unlock is not done before calling yield then no other thread can access * the list functions and update list. * * @param[in] ps_list * Job Queue context * * @returns IH264_FAIL if mutex lock unlock or yield fails else IH264_SUCCESS * * @remarks * ******************************************************************************* */ IH264_ERROR_T ih264_list_yield(list_t *ps_list) { IH264_ERROR_T ret = IH264_SUCCESS; IH264_ERROR_T rettmp; rettmp = ih264_list_unlock(ps_list); RETURN_IF((rettmp != IH264_SUCCESS), rettmp); ithread_yield(); if(ps_list->i4_yeild_interval_us > 0) ithread_usleep(ps_list->i4_yeild_interval_us); rettmp = ih264_list_lock(ps_list); RETURN_IF((rettmp != IH264_SUCCESS), rettmp); return ret; } /** ******************************************************************************* * * @brief free the buf queue pointers * * @par Description * Frees the list context * * @param[in] pv_buf * Memory for buf queue buffer and buf queue context * * @returns Pointer to buf queue context * * @remarks * Since it will be called only once by master thread this is not thread safe. * ******************************************************************************* */ IH264_ERROR_T ih264_list_free(list_t *ps_list) { WORD32 ret; ret = ithread_mutex_destroy(ps_list->pv_mutex); if(0 == ret) return IH264_SUCCESS; else return IH264_FAIL; } /** ******************************************************************************* * * @brief Initialize the buf queue * * @par Description * Initializes the list context and sets write and read pointers to start of * buf queue buffer * * @param[in] pv_buf * Memoy for buf queue buffer and buf queue context * * @param[in] buf_size * Size of the total memory allocated * * @returns Pointer to buf queue context * * @remarks * Since it will be called only once by master thread this is not thread safe. * ******************************************************************************* */ void* ih264_list_init(void *pv_buf, WORD32 buf_size, WORD32 num_entries, WORD32 entry_size, WORD32 yeild_interval_us) { list_t *ps_list; UWORD8 *pu1_buf; pu1_buf = (UWORD8 *)pv_buf; ps_list = (list_t *)pu1_buf; pu1_buf += sizeof(list_t); buf_size -= sizeof(list_t); ps_list->pv_mutex = pu1_buf; pu1_buf += ithread_get_mutex_lock_size(); buf_size -= ithread_get_mutex_lock_size(); if (buf_size <= 0) return NULL; ithread_mutex_init(ps_list->pv_mutex); /* Ensure num_entries is power of two */ ASSERT(0 == (num_entries & (num_entries - 1))); /* Ensure remaining buffer is large enough to hold given number of entries */ ASSERT((num_entries * entry_size) <= buf_size); ps_list->pv_buf_base = pu1_buf; ps_list->i4_terminate = 0; ps_list->i4_entry_size = entry_size; ps_list->i4_buf_rd_idx = 0; ps_list->i4_buf_wr_idx = 0; ps_list->i4_log2_buf_max_idx = 32 - CLZ(num_entries); ps_list->i4_buf_max_idx = num_entries; ps_list->i4_yeild_interval_us = yeild_interval_us; return ps_list; } /** ******************************************************************************* * * @brief * Resets the list context * * @par Description * Resets the list context by initializing buf queue context elements * * @param[in] ps_list * Job Queue context * * @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS * * @remarks * ******************************************************************************* */ IH264_ERROR_T ih264_list_reset(list_t *ps_list) { IH264_ERROR_T ret = IH264_SUCCESS; ret = ih264_list_lock(ps_list); RETURN_IF((ret != IH264_SUCCESS), ret); ps_list->i4_terminate = 0; ps_list->i4_buf_rd_idx = 0; ps_list->i4_buf_wr_idx = 0; ret = ih264_list_unlock(ps_list); RETURN_IF((ret != IH264_SUCCESS), ret); return ret; } /** ******************************************************************************* * * @brief * Deinitializes the list context * * @par Description * Deinitializes the list context by calling ih264_list_reset() * and then destrying the mutex created * * @param[in] ps_list * Job Queue context * * @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS * * @remarks * ******************************************************************************* */ IH264_ERROR_T ih264_list_deinit(list_t *ps_list) { WORD32 retval; IH264_ERROR_T ret = IH264_SUCCESS; ret = ih264_list_reset(ps_list); RETURN_IF((ret != IH264_SUCCESS), ret); retval = ithread_mutex_destroy(ps_list->pv_mutex); if(retval) { return IH264_FAIL; } return IH264_SUCCESS; } /** ******************************************************************************* * * @brief * Terminates the list * * @par Description * Terminates the list by setting a flag in context. * * @param[in] ps_list * Job Queue context * * @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS * * @remarks * ******************************************************************************* */ IH264_ERROR_T ih264_list_terminate(list_t *ps_list) { IH264_ERROR_T ret = IH264_SUCCESS; ret = ih264_list_lock(ps_list); RETURN_IF((ret != IH264_SUCCESS), ret); ps_list->i4_terminate = 1; ret = ih264_list_unlock(ps_list); RETURN_IF((ret != IH264_SUCCESS), ret); return ret; } /** ******************************************************************************* * * @brief Adds a buf to the queue * * @par Description * Adds a buf to the queue and updates wr address to next location. * Format/content of the buf structure is abstracted and hence size of the buf * buffer is being passed. * * @param[in] ps_list * Job Queue context * * @param[in] pv_buf * Pointer to the location that contains details of the buf to be added * * @param[in] buf_size * Size of the buf buffer * * @param[in] blocking * To signal if the write is blocking or non-blocking. * * @returns * * @remarks * Job Queue buffer is assumed to be allocated to handle worst case number of bufs * Wrap around is not supported * ******************************************************************************* */ IH264_ERROR_T ih264_list_queue(list_t *ps_list, void *pv_buf, WORD32 blocking) { IH264_ERROR_T ret = IH264_SUCCESS; IH264_ERROR_T rettmp; WORD32 diff; void *pv_buf_wr; volatile WORD32 *pi4_wr_idx, *pi4_rd_idx; WORD32 buf_size = ps_list->i4_entry_size; rettmp = ih264_list_lock(ps_list); RETURN_IF((rettmp != IH264_SUCCESS), rettmp); while(1) { /* Ensure wr idx does not go beyond rd idx by more than number of entries */ pi4_wr_idx = &ps_list->i4_buf_wr_idx; pi4_rd_idx = &ps_list->i4_buf_rd_idx; diff = *pi4_wr_idx - *pi4_rd_idx; if(diff < ps_list->i4_buf_max_idx) { WORD32 wr_idx; wr_idx = ps_list->i4_buf_wr_idx & (ps_list->i4_buf_max_idx - 1); pv_buf_wr = (UWORD8 *)ps_list->pv_buf_base + wr_idx * buf_size; memcpy(pv_buf_wr, pv_buf, buf_size); ps_list->i4_buf_wr_idx++; break; } else { /* wr is ahead, so wait for rd to consume */ if(blocking) { ih264_list_yield(ps_list); } else { ret = IH264_FAIL; break; } } } ps_list->i4_terminate = 0; rettmp = ih264_list_unlock(ps_list); RETURN_IF((rettmp != IH264_SUCCESS), rettmp); return ret; } /** ******************************************************************************* * * @brief Gets next from the Job queue * * @par Description * Gets next buf from the buf queue and updates rd address to next location. * Format/content of the buf structure is abstracted and hence size of the buf * buffer is being passed. If it is a blocking call and if there is no new buf * then this functions unlocks the mutex and calls yield and then locks it back. * and continues till a buf is available or terminate is set * * @param[in] ps_list * Job Queue context * * @param[out] pv_buf * Pointer to the location that contains details of the buf to be written * * @param[in] buf_size * Size of the buf buffer * * @param[in] blocking * To signal if the read is blocking or non-blocking. * * @returns * * @remarks * Job Queue buffer is assumed to be allocated to handle worst case number of bufs * Wrap around is not supported * ******************************************************************************* */ IH264_ERROR_T ih264_list_dequeue(list_t *ps_list, void *pv_buf, WORD32 blocking) { IH264_ERROR_T ret = IH264_SUCCESS; IH264_ERROR_T rettmp; WORD32 buf_size = ps_list->i4_entry_size; WORD32 diff; void *pv_buf_rd; volatile WORD32 *pi4_wr_idx, *pi4_rd_idx; rettmp = ih264_list_lock(ps_list); RETURN_IF((rettmp != IH264_SUCCESS), rettmp); while(1) { /* Ensure wr idx is ahead of rd idx and * wr idx does not go beyond rd idx by more than number of entries */ pi4_wr_idx = &ps_list->i4_buf_wr_idx; pi4_rd_idx = &ps_list->i4_buf_rd_idx; diff = *pi4_wr_idx - *pi4_rd_idx; if(diff > 0) { WORD32 rd_idx; rd_idx = ps_list->i4_buf_rd_idx & (ps_list->i4_buf_max_idx - 1); pv_buf_rd = (UWORD8 *)ps_list->pv_buf_base + rd_idx * buf_size; memcpy(pv_buf, pv_buf_rd, buf_size); ps_list->i4_buf_rd_idx++; break; } else { /* If terminate is signaled then break */ if(ps_list->i4_terminate) { ret = IH264_FAIL; break; } /* wr is ahead, so wait for rd to consume */ if(blocking) { ih264_list_yield(ps_list); } else { ret = IH264_FAIL; break; } } } rettmp = ih264_list_unlock(ps_list); RETURN_IF((rettmp != IH264_SUCCESS), rettmp); return ret; }