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