blob: ac6e393616b3beac6551cf30b25d559e8bf55ee4 [file] [log] [blame]
/* st_buffering.c
*
* This file contrains the functions for controlling the
* internal circular buffer within the sound trigger HAL.
* The purpose is to keep all the different read and write
* commands from different threads synchronized.
*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_TAG "sound_trigger_hw"
#define ATRACE_TAG (ATRACE_TAG_HAL)
/* #define LOG_NDEBUG 0 */
#define LOG_NDDEBUG 0
#include <errno.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#include <cutils/trace.h>
#include <system/thread_defs.h>
#include <hardware/sound_trigger.h>
#include "st_buffering.h"
#include "st_hw_common.h"
static inline uint8_t* circ_buff_move_ptr_fwd(uint8_t *circ_buff_start,
uint8_t *ptr, uint32_t bytes_num, uint32_t size)
{
return ((ptr + bytes_num) < (circ_buff_start + size)) ?
(ptr + bytes_num) : (ptr + bytes_num - size);
}
st_buffer_t* st_buffer_init(uint32_t size)
{
int status = 0;
st_buffer_t *buf_obj = NULL;
buf_obj = calloc(1, sizeof(st_buffer_t));
if (!buf_obj) {
ALOGE("%s: failed to allocate buf_obj", __func__);
status = -ENOMEM;
goto exit;
}
buf_obj->size = size;
buf_obj->buf_start = calloc(1, size);
if (!buf_obj->buf_start) {
ALOGE("%s: failed to allocate buf_obj->buf_start", __func__);
status = -ENOMEM;
goto exit;
}
pthread_mutex_init(&buf_obj->lock, NULL);
st_buffer_reset(buf_obj);
exit:
if (status && buf_obj) {
free(buf_obj);
buf_obj = NULL;
}
return buf_obj;
}
void st_buffer_deinit(st_buffer_t *buf_obj)
{
if (buf_obj) {
if (buf_obj->buf_start) {
free(buf_obj->buf_start);
buf_obj->buf_start = NULL;
}
pthread_mutex_destroy(&buf_obj->lock);
free(buf_obj);
}
}
void st_buffer_reset(st_buffer_t *buf_obj)
{
pthread_mutex_lock(&buf_obj->lock);
buf_obj->buf_end = buf_obj->buf_start + buf_obj->size;
buf_obj->wr_ptr = buf_obj->buf_start;
buf_obj->rd_ptr = buf_obj->buf_start;
buf_obj->unread_bytes = 0;
pthread_mutex_unlock(&buf_obj->lock);
}
uint8_t* st_buffer_get_wr_ptr(st_buffer_t *buf_obj)
{
return buf_obj->wr_ptr;
}
int st_buffer_write(st_buffer_t *buf_obj, uint8_t *src, uint32_t size)
{
uint32_t bytes_to_tail = 0, free_bytes = 0;
int status = 0;
pthread_mutex_lock(&buf_obj->lock);
free_bytes = buf_obj->size - buf_obj->unread_bytes;
bytes_to_tail = buf_obj->buf_end - buf_obj->wr_ptr;
if (bytes_to_tail < size) {
memcpy(buf_obj->wr_ptr, src, bytes_to_tail);
memcpy(buf_obj->buf_start, src + bytes_to_tail,
(size - bytes_to_tail));
} else {
memcpy(buf_obj->wr_ptr, src, size);
}
buf_obj->wr_ptr = circ_buff_move_ptr_fwd(buf_obj->buf_start,
buf_obj->wr_ptr, size, buf_obj->size);
if (free_bytes < size) {
ALOGW("%s: buffer overflow: buf_size %d, free_bytes %d",
__func__, size, free_bytes);
buf_obj->rd_ptr = buf_obj->wr_ptr;
buf_obj->unread_bytes = buf_obj->size;
status = -EOVERFLOW;
} else {
buf_obj->unread_bytes += size;
}
pthread_mutex_unlock(&buf_obj->lock);
return status;
}
int st_buffer_read(st_buffer_t *buf_obj, uint8_t *dst, uint32_t size,
uint8_t **rd_ptr, bool flush)
{
uint32_t bytes_to_tail = 0;
pthread_mutex_lock(&buf_obj->lock);
/*
* There are 2 types of clients accessing the buffer:
*
* 1. Read Only - These clients pass in their own rd_ptr. When reading,
* the data is not flushed from the buffer. Second stage sessions
* are an example of this type of client.
*
* 2. Read and Flush - These clients do not pass in their own rd_ptr
* because they can use buf_obj->rd_ptr. When reading, the data is
* also flushed from the buffer. App clients are an example of this
* type of client.
*/
if (!rd_ptr) {
if (flush) {
rd_ptr = &buf_obj->rd_ptr;
} else {
ALOGE("%s: Error. Need to pass in rd_ptr when not flushing",
__func__);
pthread_mutex_unlock(&buf_obj->lock);
return -EINVAL;
}
} else if (flush) {
ALOGE("%s: Error. Cannot pass in rd_ptr and flush",
__func__);
pthread_mutex_unlock(&buf_obj->lock);
return -EINVAL;
}
if (buf_obj->buf_end < (*rd_ptr + size)) {
bytes_to_tail = buf_obj->buf_end - *rd_ptr;
memcpy(dst, *rd_ptr, bytes_to_tail);
memcpy(dst + bytes_to_tail, buf_obj->buf_start,
size - bytes_to_tail);
} else {
memcpy(dst, *rd_ptr, size);
}
*rd_ptr = circ_buff_move_ptr_fwd(buf_obj->buf_start,
*rd_ptr, size, buf_obj->size);
/* Allow threads to read without removing the data from the buffer. */
if (flush)
buf_obj->unread_bytes -= size;
pthread_mutex_unlock(&buf_obj->lock);
return 0;
}
void st_buffer_flush(st_buffer_t *buf_obj, uint32_t size)
{
pthread_mutex_lock(&buf_obj->lock);
buf_obj->rd_ptr = circ_buff_move_ptr_fwd(buf_obj->buf_start,
buf_obj->rd_ptr, size, buf_obj->size);
buf_obj->unread_bytes -= size;
pthread_mutex_unlock(&buf_obj->lock);
}