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 */
impeg2_jobq_ctxt_size()70 WORD32 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 */
impeg2_jobq_lock(jobq_t * ps_jobq)96 IV_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 
impeg2_jobq_unlock(jobq_t * ps_jobq)126 IV_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 */
impeg2_jobq_yield(jobq_t * ps_jobq)159 IV_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 */
impeg2_jobq_free(jobq_t * ps_jobq)195 IV_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 */
impeg2_jobq_init(void * pv_buf,WORD32 i4_buf_size)228 void* 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 */
impeg2_jobq_reset(jobq_t * ps_jobq)274 IV_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 */
impeg2_jobq_deinit(jobq_t * ps_jobq)308 IV_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 
impeg2_jobq_terminate(jobq_t * ps_jobq)345 IV_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 */
impeg2_jobq_queue(jobq_t * ps_jobq,void * pv_job,WORD32 i4_job_size,WORD32 i4_blocking,WORD32 i4_lock)389 IV_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 */
impeg2_jobq_dequeue(jobq_t * ps_jobq,void * pv_job,WORD32 i4_job_size,WORD32 i4_blocking,WORD32 i4_lock)463 IV_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