1/******************************************************************************
2 *
3 * Copyright (C) 2015 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *****************************************************************************
18 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
19*/
20/**
21*******************************************************************************
22* @file
23*  ih264_list.c
24*
25* @brief
26*  Contains functions for buf queue
27*
28* @author
29*  Harish
30*
31* @par List of Functions:
32*  ih264_list_size()
33*  ih264_list_lock()
34*  ih264_list_unlock()
35*  ih264_list_yield()
36*  ih264_list_free()
37*  ih264_list_init()
38*  ih264_list_reset()
39*  ih264_list_deinit()
40*  ih264_list_terminate()
41*  ih264_list_queue()
42*  ih264_list_dequeue()
43*
44* @remarks
45*  None
46*
47*******************************************************************************
48*/
49/*****************************************************************************/
50/* File Includes                                                             */
51/*****************************************************************************/
52#include <stdio.h>
53#include <stddef.h>
54#include <stdlib.h>
55#include <string.h>
56#include <assert.h>
57
58#include "ih264_typedefs.h"
59#include "ithread.h"
60#include "ih264_platform_macros.h"
61#include "ih264_macros.h"
62#include "ih264_debug.h"
63#include "ih264_error.h"
64#include "ih264_list.h"
65
66/**
67*******************************************************************************
68*
69* @brief Returns size for buf queue context. Does not include buf queue buffer
70* requirements
71*
72* @par   Description
73* Returns size for buf queue context. Does not include buf queue buffer
74* requirements. Buffer size required to store the bufs should be allocated in
75* addition to the value returned here.
76*
77* @returns Size of the buf queue context
78*
79* @remarks
80*
81*******************************************************************************
82*/
83WORD32 ih264_list_size(WORD32 num_entries, WORD32 entry_size)
84{
85    WORD32 size;
86    WORD32 clz;
87    size = sizeof(list_t);
88    size += ithread_get_mutex_lock_size();
89
90    /* Use next power of two number of entries*/
91    clz = CLZ(num_entries);
92    num_entries = 1 << (32 - clz);
93
94    size  += num_entries * entry_size;
95    return size;
96}
97
98/**
99*******************************************************************************
100*
101* @brief
102*   Locks the list context
103*
104* @par   Description
105*   Locks the list context by calling ithread_mutex_lock()
106*
107* @param[in] ps_list
108*   Job Queue context
109*
110* @returns IH264_FAIL if mutex lock fails else IH264_SUCCESS
111*
112* @remarks
113*
114*******************************************************************************
115*/
116IH264_ERROR_T ih264_list_lock(list_t *ps_list)
117{
118    WORD32 retval;
119    retval = ithread_mutex_lock(ps_list->pv_mutex);
120    if(retval)
121    {
122        return IH264_FAIL;
123    }
124    return IH264_SUCCESS;
125}
126
127/**
128*******************************************************************************
129*
130* @brief
131*   Unlocks the list context
132*
133* @par   Description
134*   Unlocks the list context by calling ithread_mutex_unlock()
135*
136* @param[in] ps_list
137*   Job Queue context
138*
139* @returns IH264_FAIL if mutex unlock fails else IH264_SUCCESS
140*
141* @remarks
142*
143*******************************************************************************
144*/
145
146IH264_ERROR_T ih264_list_unlock(list_t *ps_list)
147{
148    WORD32 retval;
149    retval = ithread_mutex_unlock(ps_list->pv_mutex);
150    if(retval)
151    {
152        return IH264_FAIL;
153    }
154    return IH264_SUCCESS;
155
156}
157/**
158*******************************************************************************
159*
160* @brief
161*   Yields the thread
162*
163* @par   Description
164*   Unlocks the list context by calling
165* ih264_list_unlock(), ithread_yield() and then ih264_list_lock()
166* list is unlocked before to ensure the list can be accessed by other threads
167* If unlock is not done before calling yield then no other thread can access
168* the list functions and update list.
169*
170* @param[in] ps_list
171*   Job Queue context
172*
173* @returns IH264_FAIL if mutex lock unlock or yield fails else IH264_SUCCESS
174*
175* @remarks
176*
177*******************************************************************************
178*/
179IH264_ERROR_T ih264_list_yield(list_t *ps_list)
180{
181
182    IH264_ERROR_T ret = IH264_SUCCESS;
183
184    IH264_ERROR_T rettmp;
185    rettmp = ih264_list_unlock(ps_list);
186    RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
187
188    ithread_yield();
189
190    if(ps_list->i4_yeild_interval_us > 0)
191        ithread_usleep(ps_list->i4_yeild_interval_us);
192
193    rettmp = ih264_list_lock(ps_list);
194    RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
195    return ret;
196}
197
198
199/**
200*******************************************************************************
201*
202* @brief free the buf queue pointers
203*
204* @par   Description
205* Frees the list context
206*
207* @param[in] pv_buf
208* Memory for buf queue buffer and buf queue context
209*
210* @returns Pointer to buf queue context
211*
212* @remarks
213* Since it will be called only once by master thread this is not thread safe.
214*
215*******************************************************************************
216*/
217IH264_ERROR_T ih264_list_free(list_t *ps_list)
218{
219    WORD32 ret;
220    ret = ithread_mutex_destroy(ps_list->pv_mutex);
221
222    if(0 == ret)
223        return IH264_SUCCESS;
224    else
225        return IH264_FAIL;
226}
227
228/**
229*******************************************************************************
230*
231* @brief Initialize the buf queue
232*
233* @par   Description
234* Initializes the list context and sets write and read pointers to start of
235* buf queue buffer
236*
237* @param[in] pv_buf
238* Memoy for buf queue buffer and buf queue context
239*
240* @param[in] buf_size
241* Size of the total memory allocated
242*
243* @returns Pointer to buf queue context
244*
245* @remarks
246* Since it will be called only once by master thread this is not thread safe.
247*
248*******************************************************************************
249*/
250void* ih264_list_init(void *pv_buf,
251                      WORD32 buf_size,
252                      WORD32 num_entries,
253                      WORD32 entry_size,
254                      WORD32 yeild_interval_us)
255{
256    list_t *ps_list;
257    UWORD8 *pu1_buf;
258
259    pu1_buf = (UWORD8 *)pv_buf;
260
261    ps_list = (list_t *)pu1_buf;
262    pu1_buf += sizeof(list_t);
263    buf_size -= sizeof(list_t);
264
265    ps_list->pv_mutex = pu1_buf;
266    pu1_buf += ithread_get_mutex_lock_size();
267    buf_size -= ithread_get_mutex_lock_size();
268
269    if (buf_size <= 0)
270      return NULL;
271
272    ithread_mutex_init(ps_list->pv_mutex);
273
274    /* Ensure num_entries is power of two */
275    ASSERT(0 == (num_entries & (num_entries - 1)));
276
277    /* Ensure remaining buffer is large enough to hold given number of entries */
278    ASSERT((num_entries * entry_size) <= buf_size);
279
280    ps_list->pv_buf_base = pu1_buf;
281    ps_list->i4_terminate = 0;
282    ps_list->i4_entry_size = entry_size;
283    ps_list->i4_buf_rd_idx = 0;
284    ps_list->i4_buf_wr_idx = 0;
285    ps_list->i4_log2_buf_max_idx = 32 - CLZ(num_entries);
286    ps_list->i4_buf_max_idx = num_entries;
287    ps_list->i4_yeild_interval_us = yeild_interval_us;
288
289    return ps_list;
290}
291/**
292*******************************************************************************
293*
294* @brief
295*   Resets the list context
296*
297* @par   Description
298*   Resets the list context by initializing buf queue context elements
299*
300* @param[in] ps_list
301*   Job Queue context
302*
303* @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS
304*
305* @remarks
306*
307*******************************************************************************
308*/
309IH264_ERROR_T ih264_list_reset(list_t *ps_list)
310{
311    IH264_ERROR_T ret = IH264_SUCCESS;
312    ret = ih264_list_lock(ps_list);
313    RETURN_IF((ret != IH264_SUCCESS), ret);
314
315    ps_list->i4_terminate  = 0;
316    ps_list->i4_buf_rd_idx = 0;
317    ps_list->i4_buf_wr_idx = 0;
318
319    ret = ih264_list_unlock(ps_list);
320    RETURN_IF((ret != IH264_SUCCESS), ret);
321
322    return ret;
323}
324
325/**
326*******************************************************************************
327*
328* @brief
329*   Deinitializes the list context
330*
331* @par   Description
332*   Deinitializes the list context by calling ih264_list_reset()
333* and then destrying the mutex created
334*
335* @param[in] ps_list
336*   Job Queue context
337*
338* @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS
339*
340* @remarks
341*
342*******************************************************************************
343*/
344IH264_ERROR_T ih264_list_deinit(list_t *ps_list)
345{
346    WORD32 retval;
347    IH264_ERROR_T ret = IH264_SUCCESS;
348
349    ret = ih264_list_reset(ps_list);
350    RETURN_IF((ret != IH264_SUCCESS), ret);
351
352    retval = ithread_mutex_destroy(ps_list->pv_mutex);
353    if(retval)
354    {
355        return IH264_FAIL;
356    }
357
358    return IH264_SUCCESS;
359}
360
361
362/**
363*******************************************************************************
364*
365* @brief
366*   Terminates the list
367*
368* @par   Description
369*   Terminates the list by setting a flag in context.
370*
371* @param[in] ps_list
372*   Job Queue context
373*
374* @returns IH264_FAIL if lock unlock fails else IH264_SUCCESS
375*
376* @remarks
377*
378*******************************************************************************
379*/
380
381IH264_ERROR_T ih264_list_terminate(list_t *ps_list)
382{
383    IH264_ERROR_T ret = IH264_SUCCESS;
384    ret = ih264_list_lock(ps_list);
385    RETURN_IF((ret != IH264_SUCCESS), ret);
386
387    ps_list->i4_terminate = 1;
388
389    ret = ih264_list_unlock(ps_list);
390    RETURN_IF((ret != IH264_SUCCESS), ret);
391    return ret;
392}
393
394
395/**
396*******************************************************************************
397*
398* @brief Adds a buf to the queue
399*
400* @par   Description
401* Adds a buf to the queue and updates wr address to next location.
402* Format/content of the buf structure is abstracted and hence size of the buf
403* buffer is being passed.
404*
405* @param[in] ps_list
406*   Job Queue context
407*
408* @param[in] pv_buf
409*   Pointer to the location that contains details of the buf to be added
410*
411* @param[in] buf_size
412*   Size of the buf buffer
413*
414* @param[in] blocking
415*   To signal if the write is blocking or non-blocking.
416*
417* @returns
418*
419* @remarks
420* Job Queue buffer is assumed to be allocated to handle worst case number of bufs
421* Wrap around is not supported
422*
423*******************************************************************************
424*/
425IH264_ERROR_T ih264_list_queue(list_t *ps_list, void *pv_buf, WORD32 blocking)
426{
427    IH264_ERROR_T ret = IH264_SUCCESS;
428    IH264_ERROR_T rettmp;
429
430    WORD32 diff;
431    void *pv_buf_wr;
432
433    volatile WORD32 *pi4_wr_idx, *pi4_rd_idx;
434    WORD32 buf_size = ps_list->i4_entry_size;
435
436
437    rettmp = ih264_list_lock(ps_list);
438    RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
439
440
441
442    while(1)
443    {
444        /* Ensure wr idx does not go beyond rd idx by more than number of entries
445         */
446        pi4_wr_idx = &ps_list->i4_buf_wr_idx;
447        pi4_rd_idx = &ps_list->i4_buf_rd_idx;
448        diff = *pi4_wr_idx - *pi4_rd_idx;
449
450        if(diff < ps_list->i4_buf_max_idx)
451        {
452            WORD32 wr_idx;
453            wr_idx = ps_list->i4_buf_wr_idx & (ps_list->i4_buf_max_idx - 1);
454            pv_buf_wr = (UWORD8 *)ps_list->pv_buf_base + wr_idx * buf_size;
455
456            memcpy(pv_buf_wr, pv_buf, buf_size);
457            ps_list->i4_buf_wr_idx++;
458            break;
459        }
460        else
461        {
462            /* wr is ahead, so wait for rd to consume */
463            if(blocking)
464            {
465                ih264_list_yield(ps_list);
466            }
467            else
468            {
469                ret = IH264_FAIL;
470                break;
471            }
472        }
473
474    }
475    ps_list->i4_terminate = 0;
476
477    rettmp = ih264_list_unlock(ps_list);
478    RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
479
480    return ret;
481}
482/**
483*******************************************************************************
484*
485* @brief Gets next from the Job queue
486*
487* @par   Description
488* Gets next buf from the buf queue and updates rd address to next location.
489* Format/content of the buf structure is abstracted and hence size of the buf
490* buffer is being passed. If it is a blocking call and if there is no new buf
491* then this functions unlocks the mutex and calls yield and then locks it back.
492* and continues till a buf is available or terminate is set
493*
494* @param[in] ps_list
495*   Job Queue context
496*
497* @param[out] pv_buf
498*   Pointer to the location that contains details of the buf to be written
499*
500* @param[in] buf_size
501*   Size of the buf buffer
502*
503* @param[in] blocking
504*   To signal if the read is blocking or non-blocking.
505*
506* @returns
507*
508* @remarks
509* Job Queue buffer is assumed to be allocated to handle worst case number of bufs
510* Wrap around is not supported
511*
512*******************************************************************************
513*/
514IH264_ERROR_T ih264_list_dequeue(list_t *ps_list, void *pv_buf, WORD32 blocking)
515{
516    IH264_ERROR_T ret = IH264_SUCCESS;
517    IH264_ERROR_T rettmp;
518    WORD32 buf_size = ps_list->i4_entry_size;
519    WORD32 diff;
520
521    void *pv_buf_rd;
522    volatile WORD32 *pi4_wr_idx, *pi4_rd_idx;
523
524    rettmp = ih264_list_lock(ps_list);
525    RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
526
527    while(1)
528    {
529        /* Ensure wr idx is ahead of rd idx and
530         * wr idx does not go beyond rd idx by more than number of entries
531         */
532        pi4_wr_idx = &ps_list->i4_buf_wr_idx;
533        pi4_rd_idx = &ps_list->i4_buf_rd_idx;
534        diff = *pi4_wr_idx - *pi4_rd_idx;
535
536
537        if(diff > 0)
538        {
539            WORD32 rd_idx;
540            rd_idx = ps_list->i4_buf_rd_idx & (ps_list->i4_buf_max_idx - 1);
541            pv_buf_rd = (UWORD8 *)ps_list->pv_buf_base + rd_idx * buf_size;
542
543            memcpy(pv_buf, pv_buf_rd, buf_size);
544            ps_list->i4_buf_rd_idx++;
545            break;
546        }
547        else
548        {
549            /* If terminate is signaled then break */
550            if(ps_list->i4_terminate)
551            {
552                ret = IH264_FAIL;
553                break;
554            }
555            /* wr is ahead, so wait for rd to consume */
556            if(blocking)
557            {
558                ih264_list_yield(ps_list);
559            }
560            else
561            {
562                ret = IH264_FAIL;
563                break;
564            }
565        }
566
567    }
568
569
570    rettmp = ih264_list_unlock(ps_list);
571    RETURN_IF((rettmp != IH264_SUCCESS), rettmp);
572
573    return ret;
574}
575