/* 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; }