/* 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 #include #include #include #include #include #include #include #include #include #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 */ #define NUM_BUFS 22 static struct bt_buf buffers[NUM_BUFS]; /* Available (free) buffers queues */ static struct nano_fifo avail_hci; static struct nano_fifo avail_acl_in; static struct nano_fifo avail_acl_out; static struct nano_fifo *get_avail(enum bt_buf_type type) { switch (type) { case BT_CMD: case BT_EVT: return &avail_hci; case BT_ACL_IN: return &avail_acl_in; case BT_ACL_OUT: return &avail_acl_out; default: return NULL; } } struct bt_buf *bt_buf_get(enum bt_buf_type type, size_t reserve_head) { struct nano_fifo *avail = get_avail(type); struct bt_buf *buf; BT_DBG("type %d reserve %u\n", type, reserve_head); buf = nano_fifo_get(avail); if (!buf) { if (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; } void bt_buf_put(struct bt_buf *buf) { struct bt_hci_cp_host_num_completed_packets *cp; struct bt_hci_handle_count *hc; struct nano_fifo *avail = get_avail(buf->type); uint16_t handle; BT_DBG("buf %p ref %u type %d\n", buf, buf->ref, buf->type); if (--buf->ref) { return; } handle = buf->acl.handle; nano_fifo_put(avail, buf); if (avail != &avail_acl_in) { return; } 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); } 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; } void *bt_buf_add(struct bt_buf *buf, size_t len) { uint8_t *tail = buf->data + buf->len; BT_DBG("buf %p len %u\n", buf, len); #if defined(CONFIG_BLUETOOTH_DEBUG) if (bt_buf_tailroom(buf) < len) { BT_ERR("buf %p overflow! len %u tailroom %u\n", buf, len, bt_buf_tailroom(buf)); } #endif buf->len += len; return tail; } void *bt_buf_push(struct bt_buf *buf, size_t len) { BT_DBG("buf %p len %u\n", buf, len); #if defined(CONFIG_BLUETOOTH_DEBUG) if (bt_buf_headroom(buf) < len) { BT_ERR("buf %p underflow! len %u headroom %u\n", buf, len, bt_buf_headroom(buf)); } #endif 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); #if defined(CONFIG_BLUETOOTH_DEBUG) if (buf->len < len) { BT_ERR("buf %p overflow! len %u buf->len %u\n", buf, len, buf->len); } #endif 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_cpu_to_le16(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; /* 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)); nano_fifo_init(&avail_acl_in); for (i = 0; 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]); } 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; }