--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+
+// Debug API
+
+void btif_debug_init();
+void btif_debug_dump(int fd);
+
+// Debug helpers
+
+// Timestamp in us
+uint64_t btif_debug_ts();
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+
+#define BTSNOOZ_CURRENT_VERSION 0x01
+
+// The preamble is stored un-encrypted as the first part
+// of the file.
+typedef struct btsnooz_preamble_t {
+ uint8_t version;
+ uint64_t last_ts;
+} __attribute__((__packed__)) btsnooz_preamble_t;
+
+// One header for each HCI packet
+typedef struct btsnooz_hdr_t {
+ uint16_t len;
+ uint32_t delta;
+ uint8_t type;
+} __attribute__((__packed__)) btsnooz_hdr_t;
+
+// Initializes btsnoop memory logging and registers
+void btif_debug_btsnoop_init();
+
+// Writes btsnoop data base64 encoded to fd
+void btif_debug_btsnoop_dump(int fd);
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2015 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <hardware/bluetooth.h>
+
+typedef enum {
+ BTIF_DEBUG_CONNECTED = 1,
+ BTIF_DEBUG_DISCONNECTED
+} btif_debug_conn_state_t;
+
+// Report a connection state change
+void btif_debug_conn_state(const bt_bdaddr_t bda, const btif_debug_conn_state_t state,
+ const uint8_t status);
+
+void btif_debug_conn_dump(int fd);
#define LOG_TAG "bluedroid"
#include "btif_api.h"
+#include "btif_debug.h"
#include "bt_utils.h"
+#include "btsnoop_mem.h"
/************************************************************************************
** Constants & Macros
/* init btif */
btif_init_bluetooth();
+ btif_debug_init();
+
return BT_STATUS_SUCCESS;
}
return BT_STATUS_SUCCESS;
}
+static void dump(int fd)
+{
+ btif_debug_dump(fd);
+}
+
static const void* get_profile_interface (const char *profile_id)
{
ALOGI("get_profile_interface %s", profile_id);
config_hci_snoop_log,
set_os_callouts,
read_energy_info,
+ dump,
};
const bt_interface_t* bluetooth__get_bluetooth_interface ()
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <unistd.h>
+#include "bt_target.h"
+#include "btif_debug.h"
+#include "btif_debug_btsnoop.h"
+#include "btif_debug_conn.h"
+
+void btif_debug_init() {
+#if defined(BTSNOOP_MEM) && (BTSNOOP_MEM == TRUE)
+ btif_debug_btsnoop_init();
+#endif
+}
+
+void btif_debug_dump(int fd) {
+ btif_debug_conn_dump(fd);
+#if defined(BTSNOOP_MEM) && (BTSNOOP_MEM == TRUE)
+ btif_debug_btsnoop_dump(fd);
+#endif
+
+ fsync(fd);
+ close(fd);
+}
+
+uint64_t btif_debug_ts() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000000LL) + tv.tv_usec;
+}
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <resolv.h>
+#include <zlib.h>
+#include "bt_target.h"
+#include "btif_debug.h"
+#include "btif_debug_btsnoop.h"
+#include "btsnoop_mem.h"
+#include "ringbuffer.h"
+
+// Total btsnoop memory log buffer size
+#ifndef BTSNOOP_MEM_BUFFER_SIZE
+#define BTSNOOP_MEM_BUFFER_SIZE 131072
+#endif
+
+// Block size for copying buffers (for compression/encoding etc.)
+#define BLOCK_SIZE 16384
+
+// Maximum line length in bugreport (should be multiple of 4 for base64 output)
+#define MAX_LINE_LENGTH 128
+
+static ringbuffer_t *s_buffer = NULL;
+static uint64_t s_last_ts = 0;
+
+static void btsnoop_cb(const uint8_t type, const uint16_t len, const uint8_t *p_data) {
+ btsnooz_hdr_t hdr;
+
+ // Make room in the ring buffer
+
+ while (ringbuffer_available(s_buffer) < (len + sizeof(btsnooz_hdr_t))) {
+ ringbuffer_pop(s_buffer, (uint8_t*)&hdr, sizeof(btsnooz_hdr_t));
+ ringbuffer_delete(s_buffer, hdr.len-1);
+ }
+
+ // Insert data
+
+ const uint64_t now = btif_debug_ts();
+
+ hdr.type = type;
+ hdr.len = len;
+ hdr.delta = s_last_ts ? now - s_last_ts : 0;
+ s_last_ts = now;
+
+ ringbuffer_insert(s_buffer, (uint8_t*)&hdr, sizeof(btsnooz_hdr_t));
+ ringbuffer_insert(s_buffer, p_data, len-1);
+}
+
+static bool btsnoop_compress(ringbuffer_t *rb_in, ringbuffer_t *rb_out) {
+ if (rb_in == NULL || rb_out == NULL)
+ return false;
+
+ z_stream zs = {0};
+ if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
+ return false;
+
+ bool rc = true;
+ uint8_t b_in[BLOCK_SIZE];
+ uint8_t b_out[BLOCK_SIZE];
+
+ while(ringbuffer_size(rb_in) > 0) {
+ zs.avail_in = ringbuffer_pop(rb_in, b_in, BLOCK_SIZE);
+ zs.next_in = b_in;
+
+ do {
+ zs.avail_out = BLOCK_SIZE;
+ zs.next_out = b_out;
+
+ int err = deflate(&zs, ringbuffer_size(rb_in) == 0 ? Z_FINISH : Z_NO_FLUSH);
+ if (err == Z_STREAM_ERROR) {
+ rc = false;
+ break;
+ }
+
+ const size_t len = BLOCK_SIZE - zs.avail_out;
+ ringbuffer_insert(rb_out, b_out, len);
+ } while (zs.avail_out == 0);
+ }
+
+ deflateEnd(&zs);
+ return rc;
+}
+
+void btif_debug_btsnoop_init() {
+ if (s_buffer == NULL)
+ s_buffer = ringbuffer_init(BTSNOOP_MEM_BUFFER_SIZE);
+ btsnoop_mem_set_callback(btsnoop_cb);
+}
+
+void btif_debug_btsnoop_dump(int fd) {
+ dprintf(fd, "\n--- BEGIN:BTSNOOP_LOG_SUMMARY (%zu bytes in) ---\n", ringbuffer_size(s_buffer));
+
+ ringbuffer_t *rb = ringbuffer_init(BTSNOOP_MEM_BUFFER_SIZE);
+ if (rb == NULL) {
+ dprintf(fd, "%s() - Unable to allocate memory for compression", __FUNCTION__);
+ return;
+ }
+
+ // Prepend preamble
+
+ btsnooz_preamble_t preamble;
+ preamble.version = BTSNOOZ_CURRENT_VERSION;
+ preamble.last_ts = s_last_ts;
+ ringbuffer_insert(rb, (uint8_t*)&preamble, sizeof(btsnooz_preamble_t));
+
+ // Compress data
+
+ bool rc = btsnoop_compress(s_buffer, rb);
+ if (rc == false) {
+ dprintf(fd, "%s() - Log compression failed", __FUNCTION__);
+ goto err;
+ }
+
+ // Base64 encode & output
+
+ uint8_t b64_in[3] = {0};
+ char b64_out[5] = {0};
+
+ size_t i = sizeof(btsnooz_preamble_t);
+ while (ringbuffer_size(rb) > 0) {
+ size_t read = ringbuffer_pop(rb, b64_in, 3);
+ if (i > 0 && i % MAX_LINE_LENGTH == 0)
+ dprintf(fd, "\n");
+ i += b64_ntop(b64_in, read, b64_out, 5);
+ dprintf(fd, b64_out);
+ }
+
+ dprintf(fd, "\n--- END:BTSNOOP_LOG_SUMMARY (%zu bytes out) ---\n", i);
+
+err:
+ ringbuffer_free(rb);
+}
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2015 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "btif_debug.h"
+#include "btif_debug_conn.h"
+
+#define NUM_CONNECTION_EVT 16
+
+typedef struct conn_event_t {
+ uint64_t ts;
+ btif_debug_conn_state_t state;
+ bt_bdaddr_t bda;
+ uint8_t status;
+} conn_event_t;
+
+static conn_event_t connection_events[NUM_CONNECTION_EVT];
+static uint8_t current_event = 0;
+
+static char* format_ts(const uint64_t ts, char* buffer, int len) {
+ const uint64_t ms = ts / 1000;
+ const time_t secs = ms / 1000;
+ struct tm *ptm = localtime(&secs);
+
+ strftime(buffer, len, "%m-%d %H:%M:%S.%%03u", ptm);
+ snprintf(buffer, len, buffer, (uint16_t)(ms % 1000));
+
+ return buffer;
+}
+
+static char* format_state(const btif_debug_conn_state_t state) {
+ switch (state) {
+ case BTIF_DEBUG_CONNECTED:
+ return "CONNECTED ";
+ case BTIF_DEBUG_DISCONNECTED:
+ return "DISCONNECTED";
+ }
+ return "UNKNOWN";
+}
+
+static void next_event() {
+ ++current_event;
+ if (current_event == NUM_CONNECTION_EVT)
+ current_event = 0;
+}
+
+
+void btif_debug_conn_state(const bt_bdaddr_t bda, const btif_debug_conn_state_t state,
+ const uint8_t status) {
+ next_event();
+
+ conn_event_t *evt = &connection_events[current_event];
+ evt->ts = btif_debug_ts();
+ evt->state = state;
+ evt->status = status;
+ memcpy(&evt->bda, &bda, sizeof(bt_bdaddr_t));
+}
+
+void btif_debug_conn_dump(int fd) {
+ const uint8_t current_event_local = current_event; // Cache to avoid threading issues
+ uint8_t dump_event = current_event_local;
+ char buffer[30] = {0};
+
+ dprintf(fd, "\nConnection Events:\n");
+ if (connection_events[dump_event].ts == 0)
+ dprintf(fd, " None\n");
+
+ while (connection_events[dump_event].ts) {
+ conn_event_t *evt = &connection_events[dump_event];
+ dprintf(fd, " %s %s %02x:%02x:%02x:%02x:%02x:%02x status=%d\n",
+ format_ts(evt->ts, buffer, 30),
+ format_state(evt->state),
+ evt->bda.address[0], evt->bda.address[1], evt->bda.address[2],
+ evt->bda.address[3], evt->bda.address[4], evt->bda.address[5],
+ evt->status
+ );
+
+ // Go to previous event; wrap if needed
+ if (dump_event > 0)
+ --dump_event;
+ else
+ dump_event = NUM_CONNECTION_EVT - 1;
+
+ // Check if we dumped all events
+ if (dump_event == current_event_local)
+ break;
+ }
+}
#include "btif_gatt.h"
#include "btif_gatt_util.h"
+#include "btif_debug_conn.h"
#include "btif_dm.h"
#include "btif_storage.h"
if (p_data->open.status == BTA_GATT_OK)
btif_gatt_check_encrypted_link(p_data->open.remote_bda);
+
+ btif_debug_conn_state(bda, BTIF_DEBUG_CONNECTED, p_data->open.status);
break;
}
bdcpy(bda.address, p_data->close.remote_bda);
HAL_CBACK(bt_gatt_callbacks, client->close_cb, p_data->close.conn_id
, p_data->status, p_data->close.client_if, &bda);
+
+ btif_debug_conn_state(bda, BTIF_DEBUG_DISCONNECTED, p_data->close.status);
break;
}
{
VERBOSE("%s() - frames=%d (queue=%d)", __FUNCTION__, nb_frame, btif_media_cb.TxAaQ.count);
- while (btif_media_cb.TxAaQ.count >= (MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ-nb_frame))
+ if (btif_media_cb.TxAaQ.count >= MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ)
{
- APPL_TRACE_WARNING("%s() - TX queue buffer count %d",
- __FUNCTION__, btif_media_cb.TxAaQ.count);
- GKI_freebuf(GKI_dequeue(&(btif_media_cb.TxAaQ)));
+ APPL_TRACE_WARNING("%s() - TX queue buffer count %d, truncating to %d",
+ __FUNCTION__, btif_media_cb.TxAaQ.count, nb_frame);
+ while (btif_media_cb.TxAaQ.count > nb_frame)
+ GKI_freebuf(GKI_dequeue(&(btif_media_cb.TxAaQ)));
}
- if (btif_media_cb.TxAaQ.count) --nb_frame;
-
switch (btif_media_cb.TxTranscoding)
{
case BTIF_MEDIA_TRSCD_PCM_2_SBC:
src/bt_hci_bdroid.c \
src/btsnoop.c \
src/btsnoop_net.c \
+ src/btsnoop_mem.c \
src/lpm.c \
src/utils.c \
src/vendor.c
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+#include "bt_hci_bdroid.h"
+
+// Callback for each HCI packet.
+// Highlander mode - there can be only one...
+typedef void (*btsnoop_data_cb) (const uint8_t type, const uint16_t len, const uint8_t *p_data);
+
+// --> Sets the HCI packet callback
+void btsnoop_mem_set_callback(btsnoop_data_cb cb);
+
+// <-- Internal callback used to capture packets
+void btsnoop_mem_capture(const HC_BT_HDR *p_buf);
+
#include "bt_hci_bdroid.h"
#include "bt_utils.h"
#include "utils.h"
+#include "btsnoop_mem.h"
typedef enum {
kCommandPacket = 1,
void btsnoop_capture(const HC_BT_HDR *p_buf, bool is_rcvd) {
const uint8_t *p = (const uint8_t *)(p_buf + 1) + p_buf->offset;
+ btsnoop_mem_capture(p_buf);
+
if (hci_btsnoop_fd == -1)
return;
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "btsnoop_mem.h"
+#include <time.h>
+
+static btsnoop_data_cb s_data_cb = NULL;
+
+void btsnoop_mem_set_callback(btsnoop_data_cb cb) {
+ s_data_cb = cb;
+}
+
+void btsnoop_mem_capture(const HC_BT_HDR *p_buf) {
+ if (s_data_cb == NULL)
+ return;
+
+ const uint8_t *p = (uint8_t *)(p_buf + 1) + p_buf->offset;
+ const uint16_t type = p_buf->event & MSG_EVT_MASK;
+ uint16_t len = 0;
+
+ switch (type) {
+ case MSG_STACK_TO_HC_HCI_CMD:
+ len = p[2] + 4;
+ break;
+
+ case MSG_HC_TO_STACK_HCI_EVT:
+ len = p[1] + 3;
+ break;
+
+ // Ignore data for privacy
+ case MSG_STACK_TO_HC_HCI_ACL:
+ case MSG_STACK_TO_HC_HCI_SCO:
+ case MSG_HC_TO_STACK_HCI_ACL:
+ case MSG_HC_TO_STACK_HCI_SCO:
+ break;
+ }
+
+ if (len)
+ (*s_data_cb)(type >> 8, len, p);
+}
**
******************************************************************************/
+/* Enable/disable BTSnoop memory logging */
+#ifndef BTSNOOP_MEM
+#define BTSNOOP_MEM TRUE
+#endif
+
#include "bt_trace.h"
#endif /* BT_TARGET_H */
../btif/src/btif_sock_thread.c \
../btif/src/btif_sock_util.c \
../btif/src/btif_storage.c \
+ ../btif/src/btif_debug.c \
+ ../btif/src/btif_debug_btsnoop.c \
+ ../btif/src/btif_debug_conn.c \
../btif/src/btif_util.c
# callouts
$(LOCAL_PATH)/../audio_a2dp_hw \
$(LOCAL_PATH)/../utils/include \
$(bdroid_C_INCLUDES) \
- external/tinyxml2
+ external/tinyxml2 \
+ external/zlib
LOCAL_CFLAGS += -DBUILDCFG $(bdroid_CFLAGS) -Wno-error=maybe-uninitialized -Wno-error=uninitialized -Wno-error=unused-parameter
LOCAL_CONLYFLAGS := -std=c99
libcutils \
libdl \
liblog \
+ libz \
libpower
LOCAL_STATIC_LIBRARIES := \
./src/list.c \
./src/reactor.c \
./src/semaphore.c \
- ./src/thread.c
+ ./src/thread.c \
+ ./src/ringbuffer.c
LOCAL_CFLAGS := -std=c99 -Wall -Werror
LOCAL_MODULE := libosi
./test/config_test.cpp \
./test/list_test.cpp \
./test/reactor_test.cpp \
- ./test/thread_test.cpp
+ ./test/thread_test.cpp \
+ ./test/ringbuffer_test.cpp
LOCAL_CFLAGS := -Wall -Werror
LOCAL_MODULE := ositests
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+
+typedef struct ringbuffer_t ringbuffer_t;
+
+// NOTE:
+// None of the functions below are thread safe when it comes to accessing the
+// *rb pointer. It is *NOT* possible to insert and pop/delete at the same time.
+// Callers must protect the *rb pointer separately.
+
+// Create a ringbuffer with the specified size
+// Returns NULL if memory allocation failed. Resulting pointer must be freed
+// using ringbuffer_free().
+ringbuffer_t* ringbuffer_init(const size_t size);
+
+// Frees the ringbuffer structure and buffer
+// Save to call with NULL.
+void ringbuffer_free(ringbuffer_t *rb);
+
+// Returns remaining buffer size
+size_t ringbuffer_available(const ringbuffer_t *rb);
+
+// Returns size of data in buffer
+size_t ringbuffer_size(const ringbuffer_t *rb);
+
+// Attempts to insert up to len bytes of data at *p into the buffer
+// Return actual number of bytes added. Can be less than len if buffer is full.
+size_t ringbuffer_insert(ringbuffer_t *rb, const uint8_t *p, size_t len);
+
+// Peek len number of bytes from the ringbuffer into the buffer *p
+// Return the actual number of bytes peeked. Can be less than len if there is
+// less than 'len' data available.
+size_t ringbuffer_peek(const ringbuffer_t *rb, uint8_t *p, size_t len);
+
+// Does the same as ringbuffer_peek(), but also advances the ring buffer head
+size_t ringbuffer_pop(ringbuffer_t *rb, uint8_t *p, size_t len);
+
+// Deltes len bytes from the ringbuffer starting from the head
+// Return actual number of bytes deleted.
+size_t ringbuffer_delete(ringbuffer_t *rb, size_t len);
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include "ringbuffer.h"
+
+struct ringbuffer_t {
+ size_t total;
+ size_t available;
+ uint8_t *base;
+ uint8_t *head;
+ uint8_t *tail;
+};
+
+ringbuffer_t* ringbuffer_init(const size_t size) {
+ ringbuffer_t* p = malloc(sizeof(ringbuffer_t));
+ if (p == 0) {
+ return NULL;
+ }
+
+ p->base = malloc(size);
+ if (p->base == 0) {
+ free(p);
+ return NULL;
+ }
+
+ p->head = p->tail = p->base;
+ p->total = p->available = size;
+
+ return p;
+}
+
+void ringbuffer_free(ringbuffer_t *rb) {
+ if (rb != NULL)
+ free(rb->base);
+ free(rb);
+}
+
+size_t ringbuffer_available(const ringbuffer_t *rb) {
+ if (!rb)
+ return 0;
+ return rb->available;
+}
+
+size_t ringbuffer_size(const ringbuffer_t *rb) {
+ if (!rb)
+ return 0;
+ return rb->total - rb->available;
+}
+
+size_t ringbuffer_insert(ringbuffer_t *rb, const uint8_t *p, size_t len) {
+ if (!rb || !p)
+ return 0;
+
+ if (len > ringbuffer_available(rb))
+ len = ringbuffer_available(rb);
+
+ for (size_t i = 0; i != len; ++i) {
+ *rb->tail++ = *p++;
+ if (rb->tail >= (rb->base + rb->total))
+ rb->tail = rb->base;
+ }
+
+ rb->available -= len;
+ return len;
+}
+
+size_t ringbuffer_delete(ringbuffer_t *rb, size_t len) {
+ if (!rb)
+ return 0;
+
+ if (len > ringbuffer_size(rb))
+ len = ringbuffer_size(rb);
+
+ rb->head += len;
+ if (rb->head >= (rb->base + rb->total))
+ rb->head -= rb->total;
+
+ rb->available += len;
+ return len;
+}
+
+size_t ringbuffer_peek(const ringbuffer_t *rb, uint8_t *p, size_t len) {
+ if (!rb || !p)
+ return 0;
+
+ uint8_t *b = rb->head;
+ size_t copied = 0;
+
+ while (copied < len && copied < ringbuffer_size(rb)) {
+ *p++ = *b++;
+ if (b >= (rb->base + rb->total))
+ b = rb->base;
+ ++copied;
+ }
+
+ return copied;
+}
+
+size_t ringbuffer_pop(ringbuffer_t *rb, uint8_t *p, size_t len) {
+ if (!rb || !p)
+ return 0;
+
+ const size_t copied = ringbuffer_peek(rb, p, len);
+ rb->head += copied;
+ if (rb->head >= (rb->base + rb->total))
+ rb->head -= rb->total;
+
+ rb->available += copied;
+ return copied;
+}
--- /dev/null
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "ringbuffer.h"
+#include "osi.h"
+}
+
+TEST(RingbufferTest, test_new_simple) {
+ ringbuffer_t *rb = ringbuffer_init(4096);
+ ASSERT_TRUE(rb != NULL);
+ EXPECT_EQ(4096, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+ ringbuffer_free(rb);
+}
+
+TEST(RingbufferTest, test_insert_basic) {
+ ringbuffer_t *rb = ringbuffer_init(16);
+
+ uint8_t buffer[10] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
+ ringbuffer_insert(rb, buffer, 10);
+ EXPECT_EQ(10, ringbuffer_size(rb));
+ EXPECT_EQ(6, ringbuffer_available(rb));
+
+ uint8_t peek[10] = {0};
+ size_t peeked = ringbuffer_peek(rb, peek, 10);
+ EXPECT_EQ(10, ringbuffer_size(rb)); // Ensure size doesn't change
+ EXPECT_EQ(6, ringbuffer_available(rb));
+ EXPECT_EQ(10, peeked);
+ ASSERT_TRUE(0 == memcmp(buffer, peek, peeked));
+
+ ringbuffer_free(rb);
+}
+
+TEST(RingbufferTest, test_insert_full) {
+ ringbuffer_t *rb = ringbuffer_init(5);
+
+ uint8_t aa[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ uint8_t bb[] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
+ uint8_t peek[5] = {0};
+
+ size_t added = ringbuffer_insert(rb, aa, 7);
+ EXPECT_EQ(5, added);
+ EXPECT_EQ(0, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ added = ringbuffer_insert(rb, bb, 5);
+ EXPECT_EQ(0, added);
+ EXPECT_EQ(0, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ size_t peeked = ringbuffer_peek(rb, peek, 5);
+ EXPECT_EQ(5, peeked);
+ EXPECT_EQ(0, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ ASSERT_TRUE(0 == memcmp(aa, peek, peeked));
+
+ ringbuffer_free(rb);
+}
+
+TEST(RingbufferTest, test_multi_insert_delete) {
+ ringbuffer_t *rb = ringbuffer_init(16);
+ EXPECT_EQ(16, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+
+ // Insert some bytes
+
+ uint8_t aa[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ size_t added = ringbuffer_insert(rb, aa, sizeof(aa));
+ EXPECT_EQ(8, added);
+ EXPECT_EQ(8, ringbuffer_available(rb));
+ EXPECT_EQ(8, ringbuffer_size(rb));
+
+ uint8_t bb[] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
+ ringbuffer_insert(rb, bb, sizeof(bb));
+ EXPECT_EQ(3, ringbuffer_available(rb));
+ EXPECT_EQ(13, ringbuffer_size(rb));
+
+ uint8_t content[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
+ uint8_t peek[16] = {0};
+ size_t peeked = ringbuffer_peek(rb, peek, 16);
+ EXPECT_EQ(13, peeked);
+ ASSERT_TRUE(0 == memcmp(content, peek, peeked));
+
+ // Delete some bytes
+
+ ringbuffer_delete(rb, sizeof(aa));
+ EXPECT_EQ(11, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ // Add some more to wrap buffer
+
+ uint8_t cc[] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
+ ringbuffer_insert(rb, cc, sizeof(cc));
+ EXPECT_EQ(2, ringbuffer_available(rb));
+ EXPECT_EQ(14, ringbuffer_size(rb));
+
+ uint8_t content2[] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xCC, 0xCC};
+ peeked = ringbuffer_peek(rb, peek, 7);
+ EXPECT_EQ(7, peeked);
+ ASSERT_TRUE(0 == memcmp(content2, peek, peeked));
+
+ // Pop buffer
+
+ memset(peek, 0, 16);
+ size_t popped = ringbuffer_pop(rb, peek, 7);
+ EXPECT_EQ(7, popped);
+ EXPECT_EQ(9, ringbuffer_available(rb));
+ ASSERT_TRUE(0 == memcmp(content2, peek, peeked));
+
+ // Add more again to check head motion
+
+ uint8_t dd[] = { 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD };
+ added = ringbuffer_insert(rb, dd, sizeof(dd));
+ EXPECT_EQ(8, added);
+ EXPECT_EQ(1, ringbuffer_available(rb));
+
+ // Delete everything
+
+ ringbuffer_delete(rb, 16);
+ EXPECT_EQ(16, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+
+ // Add small token
+
+ uint8_t ae[] = { 0xAE, 0xAE, 0xAE };
+ added = ringbuffer_insert(rb, ae, sizeof(ae));
+ EXPECT_EQ(13, ringbuffer_available(rb));
+
+ // Get everything
+
+ popped = ringbuffer_pop(rb, peek, 16);
+ EXPECT_EQ(added, popped);
+ EXPECT_EQ(16, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+ ASSERT_TRUE(0 == memcmp(ae, peek, popped));
+
+ ringbuffer_free(rb);
+}