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