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