/* Copyright (c) 2015, 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.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define LOG_TAG "WifiHAL"
#include <utils/Log.h>
typedef unsigned char u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#include "ring_buffer.h"
enum rb_bool {
RB_TRUE = 0,
RB_FALSE = 1
};
typedef struct rb_entry_s {
u8 *data;
unsigned int last_wr_index;
u8 full;
} rb_entry_t;
typedef struct ring_buf_cb {
unsigned int rd_buf_no; // Current buffer number to be read from
unsigned int wr_buf_no; // Current buffer number to be written into
unsigned int cur_rd_buf_idx; // Read index within the current read buffer
unsigned int cur_wr_buf_idx; // Write index within the current write buffer
rb_entry_t *bufs; // Array of buffer pointers
unsigned int max_num_bufs; // Maximum number of buffers that should be used
size_t each_buf_size; // Size of each buffer in bytes
pthread_mutex_t rb_rw_lock;
/* Threshold vars */
unsigned int num_min_bytes;
void (*threshold_cb)(void *);
void *cb_ctx;
u32 total_bytes_written;
u32 total_bytes_read;
u32 total_bytes_overwritten;
u32 cur_valid_bytes;
enum rb_bool threshold_reached;
} rbc_t;
#define RB_MIN(x, y) ((x) < (y)?(x):(y))
inline void rb_lock(pthread_mutex_t *lock)
{
int error = pthread_mutex_lock(lock);
if (error)
ALOGE("Failed to acquire lock with err %d", error);
// TODO Handle the lock failure
}
inline void rb_unlock(pthread_mutex_t *lock)
{
int error = pthread_mutex_unlock(lock);
if (error)
ALOGE("Failed to release lock with err %d", error);
// TODO Handle the unlock failure
}
void * ring_buffer_init(size_t size_of_buf, int num_bufs)
{
struct ring_buf_cb *rbc;
int status;
rbc = (struct ring_buf_cb *)malloc(sizeof(struct ring_buf_cb));
if (rbc == NULL) {
ALOGE("Failed to alloc rbc");
return NULL;
}
memset(rbc, 0, sizeof(struct ring_buf_cb));
rbc->bufs = (rb_entry_t *)malloc(num_bufs * sizeof(rb_entry_t));
if (rbc->bufs == NULL) {
free(rbc);
ALOGE("Failed to alloc rbc->bufs");
return NULL;
}
memset(rbc->bufs, 0, (num_bufs * sizeof(rb_entry_t)));
rbc->each_buf_size = size_of_buf;
rbc->max_num_bufs = num_bufs;
status = pthread_mutex_init(&rbc->rb_rw_lock, NULL);
if (status != 0) {
ALOGE("Failed to initialize rb_rw_lock");
// TODO handle lock initialization failure
}
rbc->threshold_reached = RB_FALSE;
return rbc;
}
void ring_buffer_deinit(void *ctx)
{
rbc_t *rbc = (rbc_t *)ctx;
int status;
unsigned int buf_no;
status = pthread_mutex_destroy(&rbc->rb_rw_lock);
if (status != 0) {
ALOGE("Failed to destroy rb_rw_lock");
// TODO handle the lock destroy failure
}
for (buf_no = 0; buf_no < rbc->max_num_bufs; buf_no++) {
free(rbc->bufs[buf_no].data);
}
free(rbc->bufs);
free(rbc);
}
/*
* record_length : 0 - byte boundary
* : >0 - Ensures to write record_length no.of bytes to the same buffer.
*/
enum rb_status rb_write (void *ctx, u8 *buf, size_t length, int overwrite,
size_t record_length)
{
rbc_t *rbc = (rbc_t *)ctx;
unsigned int bytes_written = 0; // bytes written into rb so far
unsigned int push_in_rd_ptr = 0; // push required in read pointer because of
// write in current buffer
unsigned int total_push_in_rd_ptr = 0; // Total amount of push in read pointer in this write
if (record_length > rbc->each_buf_size) {
return RB_FAILURE;
}
if (overwrite == 0) {
/* Check if the complete RB is full. If the current wr_buf is also
* full, it indicates that the complete RB is full
*/
if (rbc->bufs[rbc->wr_buf_no].full == 1)
return RB_FULL;
/* Check whether record fits in current buffer */
if (rbc->wr_buf_no == rbc->rd_buf_no) {
if ((rbc->cur_wr_buf_idx == rbc->cur_rd_buf_idx) &&
rbc->cur_valid_bytes) {
return RB_FULL;
} else if (rbc->cur_wr_buf_idx < rbc->cur_rd_buf_idx) {
if (record_length >
(rbc->cur_rd_buf_idx - rbc->cur_wr_buf_idx)) {
return RB_FULL;
}
} else {
if (record_length > (rbc->each_buf_size - rbc->cur_wr_buf_idx)) {
/* Check if the next buffer is not full to write this record into
* next buffer
*/
unsigned int next_buf_no = rbc->wr_buf_no + 1;
if (next_buf_no >= rbc->max_num_bufs) {
next_buf_no = 0;
}
if (rbc->bufs[next_buf_no].full == 1) {
return RB_FULL;
}
}
}
} else if (record_length > (rbc->each_buf_size - rbc->cur_wr_buf_idx)) {
/* Check if the next buffer is not full to write this record into
* next buffer
*/
unsigned int next_buf_no = rbc->wr_buf_no + 1;
if (next_buf_no >= rbc->max_num_bufs) {
next_buf_no = 0;
}
if (rbc->bufs[next_buf_no].full == 1) {
return RB_FULL;
}
}
}
/* Go to next buffer if the current buffer is not enough to write the
* complete record
*/
if (record_length > (rbc->each_buf_size - rbc->cur_wr_buf_idx)) {
rbc->bufs[rbc->wr_buf_no].full = 1;
rbc->bufs[rbc->wr_buf_no].last_wr_index = rbc->cur_wr_buf_idx;
rbc->wr_buf_no++;
if (rbc->wr_buf_no == rbc->max_num_bufs) {
rbc->wr_buf_no = 0;
}
rbc->cur_wr_buf_idx = 0;
}
/* In each iteration of below loop, the data that can be fit into
* buffer @wr_buf_no will be copied from input buf */
while (bytes_written < length) {
unsigned int cur_copy_len;
/* Allocate a buffer if no buf available @ wr_buf_no */
if (rbc->bufs[rbc->wr_buf_no].data == NULL) {
rbc->bufs[rbc->wr_buf_no].data = (u8 *)malloc(rbc->each_buf_size);
if (rbc->bufs[rbc->wr_buf_no].data == NULL) {
ALOGE("Failed to alloc write buffer");
return RB_RETRY;
}
}
/* Take the minimum of the remaining length that needs to be written
* from buf and the maximum length that can be written into current
* buffer in ring buffer
*/
cur_copy_len = RB_MIN((rbc->each_buf_size - rbc->cur_wr_buf_idx),
(length - bytes_written));
rb_lock(&rbc->rb_rw_lock);
/* Push the read pointer in case of overrun */
if (rbc->rd_buf_no == rbc->wr_buf_no) {
if ((rbc->cur_rd_buf_idx > rbc->cur_wr_buf_idx) ||
((rbc->cur_rd_buf_idx == rbc->cur_wr_buf_idx) &&
rbc->cur_valid_bytes)) {
/* If read ptr is ahead of write pointer and if the
* gap is not enough to fit the cur_copy_len bytes then
* push the read pointer so that points to the start of
* old bytes after this write
*/
if ((rbc->cur_rd_buf_idx - rbc->cur_wr_buf_idx) <
cur_copy_len) {
push_in_rd_ptr += cur_copy_len -
(rbc->cur_rd_buf_idx - rbc->cur_wr_buf_idx);
rbc->cur_rd_buf_idx = rbc->cur_wr_buf_idx + cur_copy_len;
if (rbc->cur_rd_buf_idx >=
rbc->bufs[rbc->rd_buf_no].last_wr_index) {
rbc->cur_rd_buf_idx = 0;
rbc->rd_buf_no++;
if (rbc->rd_buf_no == rbc->max_num_bufs) {
rbc->rd_buf_no = 0;
ALOGV("Pushing read to the start of ring buffer");
}
/* the previous buffer might have little more empty room
* after overwriting the remaining bytes
*/
rbc->bufs[rbc->wr_buf_no].full = 0;
}
}
}
}
rb_unlock(&rbc->rb_rw_lock);
/* don't use lock while doing memcpy, so that we don't block the read
* context for too long. There is no harm while writing the memory if
* locking is properly done while upgrading the pointers */
memcpy((rbc->bufs[rbc->wr_buf_no].data + rbc->cur_wr_buf_idx),
(buf + bytes_written),
cur_copy_len);
rb_lock(&rbc->rb_rw_lock);
/* Update the write idx by the amount of write done in this iteration */
rbc->cur_wr_buf_idx += cur_copy_len;
if (rbc->cur_wr_buf_idx == rbc->each_buf_size) {
/* Increment the wr_buf_no as the current buffer is full */
rbc->bufs[rbc->wr_buf_no].full = 1;
rbc->bufs[rbc->wr_buf_no].last_wr_index = rbc->cur_wr_buf_idx;
rbc->wr_buf_no++;
if (rbc->wr_buf_no == rbc->max_num_bufs) {
ALOGV("Write rolling over to the start of ring buffer");
rbc->wr_buf_no = 0;
}
/* Reset the write index to zero as this is a new buffer */
rbc->cur_wr_buf_idx = 0;
}
if ((rbc->cur_valid_bytes + (cur_copy_len - push_in_rd_ptr)) >
(rbc->max_num_bufs * rbc->each_buf_size)) {
/* The below is only a precautionary print and ideally should never
* come */
ALOGE("Something going wrong in ring buffer");
} else {
/* Increase the valid bytes count by number of bytes written without
* overwriting the old bytes */
rbc->cur_valid_bytes += cur_copy_len - push_in_rd_ptr;
}
total_push_in_rd_ptr += push_in_rd_ptr;
push_in_rd_ptr = 0;
rb_unlock(&rbc->rb_rw_lock);
bytes_written += cur_copy_len;
}
rb_lock(&rbc->rb_rw_lock);
rbc->total_bytes_written += bytes_written - total_push_in_rd_ptr;
rbc->total_bytes_overwritten += total_push_in_rd_ptr;
/* check if valid bytes is going more than threshold */
if ((rbc->threshold_reached == RB_FALSE) &&
(rbc->cur_valid_bytes >= rbc->num_min_bytes) &&
((length == record_length) || !record_length) &&
rbc->threshold_cb) {
/* Release the lock before calling threshold_cb as it might call rb_read
* in this same context in order to avoid dead lock
*/
rbc->threshold_reached = RB_TRUE;
rb_unlock(&rbc->rb_rw_lock);
rbc->threshold_cb(rbc->cb_ctx);
} else {
rb_unlock(&rbc->rb_rw_lock);
}
return RB_SUCCESS;
}
size_t rb_read (void *ctx, u8 *buf, size_t max_length)
{
rbc_t *rbc = (rbc_t *)ctx;
unsigned int bytes_read = 0;
unsigned int no_more_bytes_available = 0;
rb_lock(&rbc->rb_rw_lock);
while (bytes_read < max_length) {
unsigned int cur_cpy_len;
if (rbc->bufs[rbc->rd_buf_no].data == NULL) {
break;
}
/* if read and write are on same buffer, work with rd, wr indices */
if (rbc->rd_buf_no == rbc->wr_buf_no) {
if (rbc->cur_rd_buf_idx < rbc->cur_wr_buf_idx) {
/* Check if all the required bytes are available, if not
* read only the available bytes in the current buffer and
* break out after reading current buffer
*/
if ((rbc->cur_wr_buf_idx - rbc->cur_rd_buf_idx) <
(max_length - bytes_read)) {
cur_cpy_len = rbc->cur_wr_buf_idx - rbc->cur_rd_buf_idx;
no_more_bytes_available = 1;
} else {
cur_cpy_len = max_length - bytes_read;
}
} else {
/* When there are no bytes available to read cur_rd_buf_idx
* will be euqal to cur_wr_buf_idx. Handle this scenario using
* cur_valid_bytes */
if (rbc->cur_valid_bytes <= bytes_read) {
/* Suppress possible static analyzer's warning */
cur_cpy_len = 0;
break;
}
cur_cpy_len = RB_MIN((rbc->each_buf_size - rbc->cur_rd_buf_idx),
(max_length - bytes_read));
}
} else {
/* Check if all remaining_length bytes can be read from this
* buffer, if not read only the available bytes in the current
* buffer and go to next buffer using the while loop.
*/
cur_cpy_len = RB_MIN((rbc->each_buf_size - rbc->cur_rd_buf_idx),
(max_length - bytes_read));
}
memcpy((buf + bytes_read),
(rbc->bufs[rbc->rd_buf_no].data + rbc->cur_rd_buf_idx),
cur_cpy_len);
/* Update the read index */
rbc->cur_rd_buf_idx += cur_cpy_len;
if (rbc->cur_rd_buf_idx == rbc->each_buf_size) {
/* Increment rd_buf_no as the current buffer is completely read */
if (rbc->rd_buf_no != rbc->wr_buf_no) {
free(rbc->bufs[rbc->rd_buf_no].data);
rbc->bufs[rbc->rd_buf_no].data = NULL;
}
rbc->rd_buf_no++;
if (rbc->rd_buf_no == rbc->max_num_bufs) {
ALOGV("Read rolling over to the start of ring buffer");
rbc->rd_buf_no = 0;
}
/* Reset the read index as this is a new buffer */
rbc->cur_rd_buf_idx = 0;
}
bytes_read += cur_cpy_len;
if (no_more_bytes_available) {
break;
}
}
rbc->total_bytes_read += bytes_read;
if (rbc->cur_valid_bytes < bytes_read) {
/* The below is only a precautionary print and ideally should never
* come */
ALOGE("Something going wrong in ring buffer");
} else {
rbc->cur_valid_bytes -= bytes_read;
}
/* check if valid bytes is going less than threshold */
if (rbc->threshold_reached == RB_TRUE) {
if (rbc->cur_valid_bytes < rbc->num_min_bytes) {
rbc->threshold_reached = RB_FALSE;
}
}
rb_unlock(&rbc->rb_rw_lock);
return bytes_read;
}
u8 *rb_get_read_buf(void *ctx, size_t *length)
{
rbc_t *rbc = (rbc_t *)ctx;
unsigned int cur_read_len = 0;
u8 *buf;
/* If no buffer is available for reading */
if (rbc->bufs[rbc->rd_buf_no].data == NULL) {
*length = 0;
return NULL;
}
rb_lock(&rbc->rb_rw_lock);
if ((rbc->bufs[rbc->rd_buf_no].full == 1) &&
(rbc->cur_rd_buf_idx == rbc->bufs[rbc->rd_buf_no].last_wr_index)) {
if (rbc->wr_buf_no != rbc->rd_buf_no) {
free(rbc->bufs[rbc->rd_buf_no].data);
rbc->bufs[rbc->rd_buf_no].data = NULL;
}
rbc->bufs[rbc->rd_buf_no].full = 0;
rbc->rd_buf_no++;
if (rbc->rd_buf_no == rbc->max_num_bufs) {
rbc->rd_buf_no = 0;
}
rbc->cur_rd_buf_idx = 0;
}
if (rbc->wr_buf_no == rbc->rd_buf_no) {
/* If read and write are happening on the same buffer currently, use
* rd and wr indices within the buffer */
if ((rbc->cur_rd_buf_idx == rbc->cur_wr_buf_idx) &&
(rbc->cur_valid_bytes == 0)) {
/* No bytes available for reading */
*length = 0;
rb_unlock(&rbc->rb_rw_lock);
return NULL;
} else if (rbc->cur_rd_buf_idx < rbc->cur_wr_buf_idx) {
/* write is just ahead of read in this buffer */
cur_read_len = rbc->cur_wr_buf_idx - rbc->cur_rd_buf_idx;
} else {
/* write is rolled over and just behind the read */
cur_read_len = rbc->bufs[rbc->rd_buf_no].last_wr_index - rbc->cur_rd_buf_idx;
}
} else {
if (rbc->cur_rd_buf_idx == 0) {
/* The complete buffer can be read out */
cur_read_len = rbc->bufs[rbc->rd_buf_no].last_wr_index;
} else {
/* Read the remaining bytes in this buffer */
cur_read_len = rbc->bufs[rbc->rd_buf_no].last_wr_index - rbc->cur_rd_buf_idx;
}
}
if ((rbc->bufs[rbc->rd_buf_no].full == 1) &&
(rbc->cur_rd_buf_idx == 0)) {
/* Pluck out the complete buffer and send it out */
buf = rbc->bufs[rbc->rd_buf_no].data;
rbc->bufs[rbc->rd_buf_no].data = NULL;
/* Move to the next buffer */
rbc->bufs[rbc->rd_buf_no].full = 0;
rbc->rd_buf_no++;
if (rbc->rd_buf_no == rbc->max_num_bufs) {
ALOGV("Read rolling over to the start of ring buffer");
rbc->rd_buf_no = 0;
}
} else {
/* We cannot give out the complete buffer, so allocate a new memory and
* and copy the data into it.
*/
buf = (u8 *)malloc(cur_read_len);
if (buf == NULL) {
ALOGE("Failed to alloc buffer for partial buf read");
*length = 0;
rb_unlock(&rbc->rb_rw_lock);
return NULL;
}
memcpy(buf,
(rbc->bufs[rbc->rd_buf_no].data + rbc->cur_rd_buf_idx),
cur_read_len);
/* Update the read index */
if (rbc->bufs[rbc->rd_buf_no].full == 1) {
if (rbc->wr_buf_no != rbc->rd_buf_no) {
free(rbc->bufs[rbc->rd_buf_no].data);
rbc->bufs[rbc->rd_buf_no].data = NULL;
}
rbc->bufs[rbc->rd_buf_no].full = 0;
rbc->rd_buf_no++;
if (rbc->rd_buf_no == rbc->max_num_bufs) {
rbc->rd_buf_no = 0;
}
rbc->cur_rd_buf_idx = 0;
} else {
rbc->cur_rd_buf_idx += cur_read_len;
}
}
rbc->total_bytes_read += cur_read_len;
if (rbc->cur_valid_bytes < cur_read_len) {
/* The below is only a precautionary print and ideally should never
* come */
ALOGE("Something going wrong in ring buffer");
} else {
rbc->cur_valid_bytes -= cur_read_len;
}
/* check if valid bytes is going less than threshold */
if (rbc->threshold_reached == RB_TRUE) {
if (rbc->cur_valid_bytes < rbc->num_min_bytes) {
rbc->threshold_reached = RB_FALSE;
}
}
rb_unlock(&rbc->rb_rw_lock);
*length = cur_read_len;
return buf;
}
void rb_config_threshold(void *ctx,
unsigned int num_min_bytes,
threshold_call_back callback,
void *cb_ctx)
{
rbc_t *rbc = (rbc_t *)ctx;
rbc->num_min_bytes = num_min_bytes;
rbc->threshold_cb = callback;
rbc->cb_ctx = cb_ctx;
}
void rb_get_stats(void *ctx, struct rb_stats *rbs)
{
rbc_t *rbc = (rbc_t *)ctx;
rbs->total_bytes_written = rbc->total_bytes_written;
rbs->total_bytes_read = rbc->total_bytes_read;
rbs->cur_valid_bytes = rbc->cur_valid_bytes;
rbs->each_buf_size = rbc->each_buf_size;
rbs->max_num_bufs = rbc->max_num_bufs;
}