/* uart.c - Nordic BLE UART based Bluetooth driver */ /* * Copyright (c) 2016 Intel Corporation * * 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 #include #include #include #include #include #include #include #include "uart.h" #include "rpc.h" /* TODO: check size */ #define NBLE_IPC_COUNT 2 #define NBLE_BUF_SIZE 384 static struct nano_fifo rx; static NET_BUF_POOL(rx_pool, NBLE_IPC_COUNT, NBLE_BUF_SIZE, &rx, NULL, 0); static struct nano_fifo tx; static NET_BUF_POOL(tx_pool, NBLE_IPC_COUNT, NBLE_BUF_SIZE, &tx, NULL, 0); static BT_STACK_NOINIT(rx_fiber_stack, 256); static struct device *nble_dev; static struct nano_fifo rx_queue; static void rx_fiber(void) { BT_DBG("Started"); while (true) { struct net_buf *buf; buf = nano_fifo_get(&rx_queue, TICKS_UNLIMITED); BT_DBG("Got buf %p", buf); rpc_deserialize(buf->data, buf->len); net_buf_unref(buf); } } uint8_t *rpc_alloc_cb(uint16_t length) { struct net_buf *buf; BT_DBG("length %u", length); buf = net_buf_get(&tx, 0); if (!buf) { BT_ERR("Unable to get tx buffer"); return NULL; } if (length > net_buf_tailroom(buf)) { BT_ERR("Too big tx buffer requested"); net_buf_unref(buf); return NULL; } return buf->__buf; } static void poll_out(const void *buf, size_t length) { const uint8_t *ptr = buf; while (length--) { uart_poll_out(nble_dev, *ptr++); } } void rpc_transmit_cb(uint8_t *data, uint16_t length) { struct net_buf *buf = CONTAINER_OF(data, struct net_buf, __buf); struct ipc_uart_header hdr; BT_DBG("buf %p length %u", data, length); hdr.len = length; hdr.channel = 0; hdr.src_cpu_id = 0; /* Send header */ poll_out(&hdr, sizeof(hdr)); /* Send data */ poll_out(buf->data, length); net_buf_unref(buf); } static int nble_read(struct device *uart, uint8_t *buf, size_t len, size_t min) { int total = 0; int tries = 10; while (len) { int rx; rx = uart_fifo_read(uart, buf, len); if (rx == 0) { BT_DBG("Got zero bytes from UART"); if (total < min && tries--) { continue; } break; } BT_DBG("read %d remaining %d", rx, len - rx); len -= rx; total += rx; buf += rx; } return total; } static size_t nble_discard(struct device *uart, size_t len) { /* FIXME: correct size for nble */ uint8_t buf[33]; return uart_fifo_read(uart, buf, min(len, sizeof(buf))); } void bt_uart_isr(void *unused) { static struct net_buf *buf; static int remaining; ARG_UNUSED(unused); while (uart_irq_update(nble_dev) && uart_irq_is_pending(nble_dev)) { int read; if (!uart_irq_rx_ready(nble_dev)) { if (uart_irq_tx_ready(nble_dev)) { BT_DBG("transmit ready"); /* * Implementing ISR based transmit requires * extra API for uart such as * uart_line_status(), etc. The support was * removed from the recent code, using polling * for transmit for now. */ } else { BT_DBG("spurious interrupt"); } continue; } /* Beginning of a new packet */ if (!remaining) { struct ipc_uart_header hdr; /* Get packet type */ read = nble_read(nble_dev, (uint8_t *)&hdr, sizeof(hdr), sizeof(hdr)); if (read != sizeof(hdr)) { BT_WARN("Unable to read NBLE header"); continue; } remaining = hdr.len; buf = net_buf_get(&rx, 0); if (!buf) { BT_ERR("No available IPC buffers"); } #if 0 } else { memcpy(net_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr)); } #endif BT_DBG("need to get %u bytes", remaining); if (buf && remaining > net_buf_tailroom(buf)) { BT_ERR("Not enough space in buffer"); net_buf_unref(buf); buf = NULL; } } if (!buf) { read = nble_discard(nble_dev, remaining); BT_WARN("Discarded %d bytes", read); remaining -= read; continue; } read = nble_read(nble_dev, net_buf_tail(buf), remaining, 0); buf->len += read; remaining -= read; BT_DBG("received %d bytes", read); if (!remaining) { BT_DBG("full packet received"); /* Pass buffer to the stack */ nano_fifo_put(&rx_queue, buf); } } } int nble_open(void) { BT_DBG(""); /* Initialize receive queue and start rx_fiber */ nano_fifo_init(&rx_queue); fiber_start(rx_fiber_stack, sizeof(rx_fiber_stack), (nano_fiber_entry_t)rx_fiber, 0, 0, 7, 0); uart_irq_rx_disable(nble_dev); uart_irq_tx_disable(nble_dev); IRQ_CONNECT(CONFIG_NBLE_UART_IRQ, CONFIG_NBLE_UART_IRQ_PRI, bt_uart_isr, 0, UART_IRQ_FLAGS); irq_enable(CONFIG_NBLE_UART_IRQ); /* Drain the fifo */ while (uart_irq_rx_ready(nble_dev)) { unsigned char c; uart_fifo_read(nble_dev, &c, 1); } uart_irq_rx_enable(nble_dev); return 0; } static int _bt_nble_init(struct device *unused) { ARG_UNUSED(unused); nble_dev = device_get_binding(CONFIG_NBLE_UART_ON_DEV_NAME); if (!nble_dev) { return DEV_INVALID_CONF; } net_buf_pool_init(rx_pool); net_buf_pool_init(tx_pool); return DEV_OK; } DEVICE_INIT(bt_nble, "", _bt_nble_init, NULL, NULL, NANOKERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);