1/******************************************************************************
2*
3* Copyright (C) 2012 Ittiam Systems Pvt Ltd, Bangalore
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/**
19*******************************************************************************
20* @file
21*  ihevcd_job_queue.c
22*
23* @brief
24*  Contains functions for job queue
25*
26* @author
27*  Harish
28*
29* @par List of Functions:
30*
31* @remarks
32*  None
33*
34*******************************************************************************
35*/
36/*****************************************************************************/
37/* File Includes                                                             */
38/*****************************************************************************/
39#include <stdio.h>
40#include <stddef.h>
41#include <stdlib.h>
42#include <string.h>
43#include <assert.h>
44
45#include "ihevc_typedefs.h"
46#include "iv.h"
47#include "ivd.h"
48#include "ihevcd_cxa.h"
49#include "ithread.h"
50#include "ihevc_platform_macros.h"
51
52#include "ihevc_macros.h"
53#include "ihevcd_error.h"
54#include "ihevcd_job_queue.h"
55
56/**
57*******************************************************************************
58*
59* @brief Returns size for job queue context. Does not include job queue buffer
60* requirements
61*
62* @par   Description
63* Returns size for job queue context. Does not include job queue buffer
64* requirements. Buffer size required to store the jobs should be allocated in
65* addition to the value returned here.
66*
67* @returns Size of the job queue context
68*
69* @remarks
70*
71*******************************************************************************
72*/
73WORD32 ihevcd_jobq_ctxt_size()
74{
75    WORD32 size;
76    size = sizeof(jobq_t);
77    size += ithread_get_mutex_lock_size();
78    return size;
79}
80
81/**
82*******************************************************************************
83*
84* @brief
85*   Locks the jobq conext
86*
87* @par   Description
88*   Locks the jobq conext by calling ithread_mutex_lock()
89*
90* @param[in] ps_jobq
91*   Job Queue context
92*
93* @returns IHEVCD_FAIL if mutex lock fails else IHEVCD_SUCCESS
94*
95* @remarks
96*
97*******************************************************************************
98*/
99IHEVCD_ERROR_T ihevcd_jobq_lock(jobq_t *ps_jobq)
100{
101    WORD32 retval;
102    retval = ithread_mutex_lock(ps_jobq->pv_mutex);
103    if(retval)
104    {
105        return (IHEVCD_ERROR_T)IHEVCD_FAIL;
106    }
107    return (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
108}
109
110/**
111*******************************************************************************
112*
113* @brief
114*   Unlocks the jobq conext
115*
116* @par   Description
117*   Unlocks the jobq conext by calling ithread_mutex_unlock()
118*
119* @param[in] ps_jobq
120*   Job Queue context
121*
122* @returns IHEVCD_FAIL if mutex unlock fails else IHEVCD_SUCCESS
123*
124* @remarks
125*
126*******************************************************************************
127*/
128
129IHEVCD_ERROR_T ihevcd_jobq_unlock(jobq_t *ps_jobq)
130{
131    WORD32 retval;
132    retval = ithread_mutex_unlock(ps_jobq->pv_mutex);
133    if(retval)
134    {
135        return (IHEVCD_ERROR_T)IHEVCD_FAIL;
136    }
137    return (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
138
139}
140/**
141*******************************************************************************
142*
143* @brief
144*   Yeilds the thread
145*
146* @par   Description
147*   Unlocks the jobq conext by calling
148* ihevcd_jobq_unlock(), ithread_yield() and then ihevcd_jobq_lock()
149* jobq is unlocked before to ensure the jobq can be accessed by other threads
150* If unlock is not done before calling yield then no other thread can access
151* the jobq functions and update jobq.
152*
153* @param[in] ps_jobq
154*   Job Queue context
155*
156* @returns IHEVCD_FAIL if mutex lock unlock or yield fails else IHEVCD_SUCCESS
157*
158* @remarks
159*
160*******************************************************************************
161*/
162IHEVCD_ERROR_T ihevcd_jobq_yield(jobq_t *ps_jobq)
163{
164
165    IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
166
167    IHEVCD_ERROR_T rettmp;
168    rettmp = ihevcd_jobq_unlock(ps_jobq);
169    RETURN_IF((rettmp != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), rettmp);
170
171    //NOP(1024 * 8);
172    ithread_yield();
173
174    rettmp = ihevcd_jobq_lock(ps_jobq);
175    RETURN_IF((rettmp != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), rettmp);
176    return ret;
177}
178
179
180/**
181*******************************************************************************
182*
183* @brief free the job queue pointers
184*
185* @par   Description
186* Frees the jobq context
187*
188* @param[in] pv_buf
189* Memoy for job queue buffer and job queue context
190*
191* @returns Pointer to job queue context
192*
193* @remarks
194* Since it will be called only once by master thread this is not thread safe.
195*
196*******************************************************************************
197*/
198IHEVCD_ERROR_T ihevcd_jobq_free(jobq_t *ps_jobq)
199{
200    WORD32 ret;
201    ret = ithread_mutex_destroy(ps_jobq->pv_mutex);
202
203    if(0 == ret)
204        return (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
205    else
206        return (IHEVCD_ERROR_T)IHEVCD_FAIL;
207}
208
209/**
210*******************************************************************************
211*
212* @brief Initialize the job queue
213*
214* @par   Description
215* Initializes the jobq context and sets write and read pointers to start of
216* job queue buffer
217*
218* @param[in] pv_buf
219* Memoy for job queue buffer and job queue context
220*
221* @param[in] buf_size
222* Size of the total memory allocated
223*
224* @returns Pointer to job queue context
225*
226* @remarks
227* Since it will be called only once by master thread this is not thread safe.
228*
229*******************************************************************************
230*/
231void* ihevcd_jobq_init(void *pv_buf, WORD32 buf_size)
232{
233    jobq_t *ps_jobq;
234    UWORD8 *pu1_buf;
235    pu1_buf = (UWORD8 *)pv_buf;
236
237    ps_jobq = (jobq_t *)pu1_buf;
238    pu1_buf += sizeof(jobq_t);
239    buf_size -= sizeof(jobq_t);
240
241    ps_jobq->pv_mutex = pu1_buf;
242    pu1_buf += ithread_get_mutex_lock_size();
243    buf_size -= ithread_get_mutex_lock_size();
244
245    if(buf_size <= 0)
246        return NULL;
247
248    ithread_mutex_init(ps_jobq->pv_mutex);
249
250    ps_jobq->pv_buf_base = pu1_buf;
251    ps_jobq->pv_buf_wr = pu1_buf;
252    ps_jobq->pv_buf_rd = pu1_buf;
253    ps_jobq->pv_buf_end = pu1_buf + buf_size;
254    ps_jobq->i4_terminate = 0;
255
256
257    return ps_jobq;
258}
259/**
260*******************************************************************************
261*
262* @brief
263*   Resets the jobq conext
264*
265* @par   Description
266*   Resets the jobq conext by initilizing job queue context elements
267*
268* @param[in] ps_jobq
269*   Job Queue context
270*
271* @returns IHEVCD_FAIL if lock unlock fails else IHEVCD_SUCCESS
272*
273* @remarks
274*
275*******************************************************************************
276*/
277IHEVCD_ERROR_T ihevcd_jobq_reset(jobq_t *ps_jobq)
278{
279    IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
280    ret = ihevcd_jobq_lock(ps_jobq);
281    RETURN_IF((ret != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), ret);
282
283    ps_jobq->pv_buf_wr      = ps_jobq->pv_buf_base;
284    ps_jobq->pv_buf_rd      = ps_jobq->pv_buf_base;
285    ps_jobq->i4_terminate   = 0;
286    ret = ihevcd_jobq_unlock(ps_jobq);
287    RETURN_IF((ret != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), ret);
288
289    return ret;
290}
291
292/**
293*******************************************************************************
294*
295* @brief
296*   Deinitializes the jobq conext
297*
298* @par   Description
299*   Deinitializes the jobq conext by calling ihevc_jobq_reset()
300* and then destrying the mutex created
301*
302* @param[in] ps_jobq
303*   Job Queue context
304*
305* @returns IHEVCD_FAIL if lock unlock fails else IHEVCD_SUCCESS
306*
307* @remarks
308*
309*******************************************************************************
310*/
311IHEVCD_ERROR_T ihevcd_jobq_deinit(jobq_t *ps_jobq)
312{
313    WORD32 retval;
314    IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
315
316    ret = ihevcd_jobq_reset(ps_jobq);
317    RETURN_IF((ret != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), ret);
318
319    retval = ithread_mutex_destroy(ps_jobq->pv_mutex);
320    if(retval)
321    {
322        return (IHEVCD_ERROR_T)IHEVCD_FAIL;
323    }
324
325    return (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
326}
327
328
329/**
330*******************************************************************************
331*
332* @brief
333*   Terminates the jobq
334*
335* @par   Description
336*   Terminates the jobq by setting a flag in context.
337*
338* @param[in] ps_jobq
339*   Job Queue context
340*
341* @returns IHEVCD_FAIL if lock unlock fails else IHEVCD_SUCCESS
342*
343* @remarks
344*
345*******************************************************************************
346*/
347
348IHEVCD_ERROR_T ihevcd_jobq_terminate(jobq_t *ps_jobq)
349{
350    IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
351    ret = ihevcd_jobq_lock(ps_jobq);
352    RETURN_IF((ret != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), ret);
353
354    ps_jobq->i4_terminate = 1;
355
356    ret = ihevcd_jobq_unlock(ps_jobq);
357    RETURN_IF((ret != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), ret);
358    return ret;
359}
360
361
362/**
363*******************************************************************************
364*
365* @brief Adds a job to the queue
366*
367* @par   Description
368* Adds a job to the queue and updates wr address to next location.
369* Format/content of the job structure is abstracted and hence size of the job
370* buffer is being passed.
371*
372* @param[in] ps_jobq
373*   Job Queue context
374*
375* @param[in] pv_job
376*   Pointer to the location that contains details of the job to be added
377*
378* @param[in] job_size
379*   Size of the job buffer
380*
381* @param[in] blocking
382*   To signal if the write is blocking or non-blocking.
383*
384* @returns
385*
386* @remarks
387* Job Queue buffer is assumed to be allocated to handle worst case number of jobs
388* Wrap around is not supported
389*
390*******************************************************************************
391*/
392IHEVCD_ERROR_T ihevcd_jobq_queue(jobq_t *ps_jobq, void *pv_job, WORD32 job_size, WORD32 blocking)
393{
394    IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
395    IHEVCD_ERROR_T rettmp;
396    UWORD8 *pu1_buf;
397    UNUSED(blocking);
398
399    rettmp = ihevcd_jobq_lock(ps_jobq);
400    RETURN_IF((rettmp != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), rettmp);
401
402    pu1_buf = (UWORD8 *)ps_jobq->pv_buf_wr;
403    if((UWORD8 *)ps_jobq->pv_buf_end >= (pu1_buf + job_size))
404    {
405        memcpy(ps_jobq->pv_buf_wr, pv_job, job_size);
406        ps_jobq->pv_buf_wr = (UWORD8 *)ps_jobq->pv_buf_wr + job_size;
407        ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
408    }
409    else
410    {
411        /* Handle wrap around case */
412        /* Wait for pv_buf_rd to consume first job_size number of bytes
413         * from the beginning of job queue
414         */
415        ret = (IHEVCD_ERROR_T)IHEVCD_FAIL;
416    }
417
418    ps_jobq->i4_terminate = 0;
419
420    rettmp = ihevcd_jobq_unlock(ps_jobq);
421    RETURN_IF((rettmp != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), rettmp);
422
423    return ret;
424}
425/**
426*******************************************************************************
427*
428* @brief Gets next from the Job queue
429*
430* @par   Description
431* Gets next job from the job queue and updates rd address to next location.
432* Format/content of the job structure is abstracted and hence size of the job
433* buffer is being passed. If it is a blocking call and if there is no new job
434* then this functions unlocks the mutext and calls yield and then locks it back.
435* and continues till a job is available or terminate is set
436*
437* @param[in] ps_jobq
438*   Job Queue context
439*
440* @param[out] pv_job
441*   Pointer to the location that contains details of the job to be written
442*
443* @param[in] job_size
444*   Size of the job buffer
445*
446* @param[in] blocking
447*   To signal if the read is blocking or non-blocking.
448*
449* @returns
450*
451* @remarks
452* Job Queue buffer is assumed to be allocated to handle worst case number of jobs
453* Wrap around is not supported
454*
455*******************************************************************************
456*/
457IHEVCD_ERROR_T ihevcd_jobq_dequeue(jobq_t *ps_jobq, void *pv_job, WORD32 job_size, WORD32 blocking)
458{
459    IHEVCD_ERROR_T ret;
460    IHEVCD_ERROR_T rettmp;
461    volatile UWORD8 *pu1_buf;
462
463    rettmp = ihevcd_jobq_lock(ps_jobq);
464    RETURN_IF((rettmp != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), rettmp);
465    pu1_buf = (UWORD8 *)ps_jobq->pv_buf_rd;
466
467
468    if((UWORD8 *)ps_jobq->pv_buf_end >= (pu1_buf + job_size))
469    {
470        while(1)
471        {
472            pu1_buf = (UWORD8 *)ps_jobq->pv_buf_rd;
473            if((UWORD8 *)ps_jobq->pv_buf_wr >= (pu1_buf + job_size))
474            {
475                memcpy(pv_job, ps_jobq->pv_buf_rd, job_size);
476                ps_jobq->pv_buf_rd = (UWORD8 *)ps_jobq->pv_buf_rd + job_size;
477                ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS;
478                break;
479            }
480            else
481            {
482                /* If all the entries have been dequeued, then break and return */
483                if(1 == ps_jobq->i4_terminate)
484                {
485                    ret = (IHEVCD_ERROR_T)IHEVCD_FAIL;
486                    break;
487                }
488
489                if(1 == blocking)
490                {
491                    ihevcd_jobq_yield(ps_jobq);
492
493                }
494                else
495                {
496                    /* If there is no job available,
497                     * and this is non blocking call then return fail */
498                    ret = (IHEVCD_ERROR_T)IHEVCD_FAIL;
499                }
500            }
501        }
502    }
503    else
504    {
505        /* Handle wrap around case */
506        /* Wait for pv_buf_rd to consume first job_size number of bytes
507         * from the beginning of job queue
508         */
509        ret = (IHEVCD_ERROR_T)IHEVCD_FAIL;
510    }
511    rettmp = ihevcd_jobq_unlock(ps_jobq);
512    RETURN_IF((rettmp != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), rettmp);
513
514    return ret;
515}
516