| /* buf.c - Bluetooth buffer management */ |
| |
| /* |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1) Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2) 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. |
| * |
| * 3) Neither the name of Intel Corporation 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 <nanokernel.h> |
| #include <toolchain.h> |
| #include <errno.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <atomic.h> |
| #include <misc/byteorder.h> |
| |
| #include <bluetooth/log.h> |
| #include <bluetooth/hci.h> |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/buf.h> |
| |
| #include "hci_core.h" |
| |
| #if !defined(CONFIG_BLUETOOTH_DEBUG_BUF) |
| #undef BT_DBG |
| #define BT_DBG(fmt, ...) |
| #endif |
| |
| /* Total number of all types of buffers */ |
| #if defined(CONFIG_BLUETOOTH_CONN) |
| #define NUM_BUFS 22 |
| #else |
| #define NUM_BUFS 8 |
| #endif /* CONFIG_BLUETOOTH_CONN */ |
| |
| static struct bt_buf buffers[NUM_BUFS]; |
| |
| /* Available (free) buffers queues */ |
| static struct nano_fifo avail_hci; |
| |
| #if defined(CONFIG_BLUETOOTH_CONN) |
| static struct nano_fifo avail_acl_in; |
| static struct nano_fifo avail_acl_out; |
| #endif /* CONFIG_BLUETOOTH_CONN */ |
| |
| static struct nano_fifo *get_avail(enum bt_buf_type type) |
| { |
| switch (type) { |
| case BT_CMD: |
| case BT_EVT: |
| return &avail_hci; |
| #if defined(CONFIG_BLUETOOTH_CONN) |
| case BT_ACL_IN: |
| return &avail_acl_in; |
| case BT_ACL_OUT: |
| return &avail_acl_out; |
| #endif /* CONFIG_BLUETOOTH_CONN */ |
| default: |
| return NULL; |
| } |
| } |
| |
| struct bt_buf *bt_buf_get(enum bt_buf_type type, size_t reserve_head) |
| { |
| struct nano_fifo *avail; |
| struct bt_buf *buf; |
| |
| BT_DBG("type %d reserve %u\n", type, reserve_head); |
| |
| avail = get_avail(type); |
| if (!avail) { |
| return NULL; |
| } |
| |
| buf = nano_fifo_get(avail); |
| if (!buf) { |
| if (sys_execution_context_type_get() == NANO_CTX_ISR) { |
| BT_ERR("Failed to get free buffer\n"); |
| return NULL; |
| } |
| |
| BT_WARN("Low on buffers. Waiting (type %d)\n", type); |
| buf = nano_fifo_get_wait(avail); |
| } |
| |
| memset(buf, 0, sizeof(*buf)); |
| |
| buf->ref = 1; |
| buf->type = type; |
| buf->data = buf->buf + reserve_head; |
| |
| BT_DBG("buf %p type %d reserve %u\n", buf, buf->type, reserve_head); |
| |
| return buf; |
| } |
| |
| #if defined(CONFIG_BLUETOOTH_CONN) |
| static void report_completed_packet(struct bt_buf *buf) |
| { |
| |
| struct bt_hci_cp_host_num_completed_packets *cp; |
| struct bt_hci_handle_count *hc; |
| uint16_t handle; |
| |
| handle = buf->acl.handle; |
| nano_fifo_put(&avail_acl_in, buf); |
| |
| BT_DBG("Reporting completed packet for handle %u\n", handle); |
| |
| buf = bt_hci_cmd_create(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, |
| sizeof(*cp) + sizeof(*hc)); |
| if (!buf) { |
| BT_ERR("Unable to allocate new HCI command\n"); |
| return; |
| } |
| |
| cp = bt_buf_add(buf, sizeof(*cp)); |
| cp->num_handles = sys_cpu_to_le16(1); |
| |
| hc = bt_buf_add(buf, sizeof(*hc)); |
| hc->handle = sys_cpu_to_le16(handle); |
| hc->count = sys_cpu_to_le16(1); |
| |
| bt_hci_cmd_send(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, buf); |
| } |
| #endif /* CONFIG_BLUETOOTH_CONN */ |
| |
| void bt_buf_put(struct bt_buf *buf) |
| { |
| struct nano_fifo *avail = get_avail(buf->type); |
| |
| BT_DBG("buf %p ref %u type %d\n", buf, buf->ref, buf->type); |
| |
| if (--buf->ref) { |
| return; |
| } |
| |
| #if defined(CONFIG_BLUETOOTH_CONN) |
| if (avail == &avail_acl_in) { |
| report_completed_packet(buf); |
| return; |
| } |
| #endif /* CONFIG_BLUETOOTH_CONN */ |
| |
| /* Even if connection support is disabled avail shall always be not |
| * null. It is required to first get bt_buf with specific type to be |
| * able to put it. If connection support is disabled get returns NULL. |
| */ |
| BT_ASSERT(avail); |
| |
| nano_fifo_put(avail, buf); |
| } |
| |
| struct bt_buf *bt_buf_hold(struct bt_buf *buf) |
| { |
| BT_DBG("buf %p (old) ref %u type %d\n", buf, buf->ref, buf->type); |
| buf->ref++; |
| return buf; |
| } |
| |
| struct bt_buf *bt_buf_clone(struct bt_buf *buf) |
| { |
| struct bt_buf *clone; |
| |
| clone = bt_buf_get(buf->type, bt_buf_headroom(buf)); |
| if (!clone) { |
| return NULL; |
| } |
| |
| /* TODO: Add reference to the original buffer instead of copying it. */ |
| memcpy(bt_buf_add(clone, buf->len), buf->data, buf->len); |
| |
| return clone; |
| } |
| |
| void *bt_buf_add(struct bt_buf *buf, size_t len) |
| { |
| uint8_t *tail = bt_buf_tail(buf); |
| |
| BT_DBG("buf %p len %u\n", buf, len); |
| |
| BT_ASSERT(bt_buf_tailroom(buf) >= len); |
| |
| buf->len += len; |
| return tail; |
| } |
| |
| void bt_buf_add_le16(struct bt_buf *buf, uint16_t value) |
| { |
| BT_DBG("buf %p value %u\n", buf, value); |
| |
| value = sys_cpu_to_le16(value); |
| memcpy(bt_buf_add(buf, sizeof(value)), &value, sizeof(value)); |
| } |
| |
| void *bt_buf_push(struct bt_buf *buf, size_t len) |
| { |
| BT_DBG("buf %p len %u\n", buf, len); |
| |
| BT_ASSERT(bt_buf_headroom(buf) >= len); |
| |
| buf->data -= len; |
| buf->len += len; |
| return buf->data; |
| } |
| |
| void *bt_buf_pull(struct bt_buf *buf, size_t len) |
| { |
| BT_DBG("buf %p len %u\n", buf, len); |
| |
| BT_ASSERT(buf->len >= len); |
| |
| buf->len -= len; |
| return buf->data += len; |
| } |
| |
| uint16_t bt_buf_pull_le16(struct bt_buf *buf) |
| { |
| uint16_t value; |
| |
| value = UNALIGNED_GET((uint16_t *)buf->data); |
| bt_buf_pull(buf, sizeof(value)); |
| |
| return sys_le16_to_cpu(value); |
| } |
| |
| size_t bt_buf_headroom(struct bt_buf *buf) |
| { |
| return buf->data - buf->buf; |
| } |
| |
| size_t bt_buf_tailroom(struct bt_buf *buf) |
| { |
| return BT_BUF_MAX_DATA - bt_buf_headroom(buf) - buf->len; |
| } |
| |
| int bt_buf_init(int acl_in, int acl_out) |
| { |
| int i = 0; |
| |
| /* Check that we have enough buffers configured */ |
| if (acl_out + acl_in >= NUM_BUFS - 2) { |
| BT_ERR("Too many ACL buffers requested\n"); |
| return -EINVAL; |
| } |
| |
| BT_DBG("Available bufs: ACL in: %d, ACL out: %d, cmds/evts: %d\n", |
| acl_in, acl_out, NUM_BUFS - (acl_in + acl_out)); |
| |
| #if defined(CONFIG_BLUETOOTH_CONN) |
| nano_fifo_init(&avail_acl_in); |
| for (; acl_in > 0; i++, acl_in--) { |
| nano_fifo_put(&avail_acl_in, &buffers[i]); |
| } |
| |
| nano_fifo_init(&avail_acl_out); |
| for (; acl_out > 0; i++, acl_out--) { |
| nano_fifo_put(&avail_acl_out, &buffers[i]); |
| } |
| #endif /* CONFIG_BLUETOOTH_CONN */ |
| |
| nano_fifo_init(&avail_hci); |
| for (; i < NUM_BUFS; i++) { |
| nano_fifo_put(&avail_hci, &buffers[i]); |
| } |
| |
| BT_DBG("%u buffers * %u bytes = %u bytes\n", NUM_BUFS, |
| sizeof(buffers[0]), sizeof(buffers)); |
| |
| return 0; |
| } |