/**************************************************************************** * drivers/wireless/bluetooth/bt_uart.c * UART based Bluetooth driver * * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package * where the code was released with a compatible 3-clause BSD license: * * Copyright (c) 2016, Intel Corporation * All rights reserved. * * 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 the copyright holder 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "bt_uart.h" /**************************************************************************** * Private Functions ****************************************************************************/ static ssize_t btuart_read(FAR struct btuart_upperhalf_s *upper, FAR uint8_t *buffer, size_t buflen, size_t minread) { FAR const struct btuart_lowerhalf_s *lower; ssize_t ntotal = 0; ssize_t nread; wlinfo("buflen %lu minread %lu\n", (unsigned long)buflen, (unsigned long)minread); DEBUGASSERT(upper != NULL && upper->lower != NULL); lower = upper->lower; DEBUGASSERT(lower->read != NULL); while (buflen > 0) { nread = lower->read(lower, buffer, buflen); if (nread == 0 || nread == -EINTR) { wlwarn("Got zero bytes from UART\n"); if (ntotal < minread) { continue; } break; } else if (nread < 0) { wlwarn("Returned error %d\n", (int)nread); return nread; } wlinfo("read %ld remaining %lu\n", (long)nread, (unsigned long)(buflen - nread)); buflen -= nread; ntotal += nread; buffer += nread; } return ntotal; } static FAR struct bt_buf_s * btuart_evt_recv(FAR struct btuart_upperhalf_s *upper, FAR unsigned int *hdrlen) { FAR struct bt_buf_s *buf; struct bt_hci_evt_hdr_s hdr; ssize_t nread; /* We can ignore the return value since we pass buflen == minread */ nread = btuart_read(upper, (FAR uint8_t *)&hdr, sizeof(struct bt_hci_evt_hdr_s), sizeof(struct bt_hci_evt_hdr_s)); if (nread != sizeof(struct bt_hci_evt_hdr_s)) { wlerr("ERROR: btuart_read returned %ld\n", (long)nread); return NULL; } buf = bt_buf_alloc(BT_EVT, NULL, 0); if (buf != NULL) { memcpy(bt_buf_extend(buf, sizeof(struct bt_hci_evt_hdr_s)), &hdr, sizeof(struct bt_hci_evt_hdr_s)); } else { wlerr("ERROR: No available event buffers!\n"); } *hdrlen = hdr.len; wlinfo("hdrlen %u\n", hdr.len); return buf; } static FAR struct bt_buf_s * btuart_acl_recv(FAR struct btuart_upperhalf_s *upper, FAR unsigned int *hdrlen) { FAR struct bt_buf_s *buf; struct bt_hci_acl_hdr_s hdr; ssize_t nread; /* We can ignore the return value since we pass buflen == minread */ nread = btuart_read(upper, (FAR uint8_t *)&hdr, sizeof(struct bt_hci_acl_hdr_s), sizeof(struct bt_hci_acl_hdr_s)); if (nread != sizeof(struct bt_hci_acl_hdr_s)) { wlerr("ERROR: btuart_read returned %ld\n", (long)nread); return NULL; } buf = bt_buf_alloc(BT_ACL_IN, NULL, 0); if (buf != NULL) { memcpy(bt_buf_extend(buf, sizeof(struct bt_hci_acl_hdr_s)), &hdr, sizeof(struct bt_hci_acl_hdr_s)); } else { wlerr("ERROR: No available ACL buffers!\n"); } *hdrlen = BT_LE162HOST(hdr.len); wlinfo("hdrlen %u\n", *hdrlen); return buf; } static void btuart_rxwork(FAR void *arg) { FAR struct btuart_upperhalf_s *upper; FAR const struct btuart_lowerhalf_s *lower; FAR struct bt_buf_s *buf; unsigned int hdrlen; int remaining; ssize_t nread; uint8_t type; upper = (FAR struct btuart_upperhalf_s *)arg; DEBUGASSERT(upper != NULL && upper->lower != NULL); lower = upper->lower; /* Beginning of a new packet. * Read the first byte to get the packet type. */ nread = btuart_read(upper, &type, 1, 0); if (nread != 1) { wlwarn("WARNING: Unable to read H4 packet type: %ld\n", (long)nread); goto errout_with_busy; } switch (type) { case H4_EVT: buf = btuart_evt_recv(upper, &hdrlen); break; case H4_ACL: buf = btuart_acl_recv(upper, &hdrlen); break; default: wlerr("ERROR: Unknown H4 type %u\n", type); goto errout_with_busy; } if (buf == NULL) { /* Failed to allocate a buffer. Drain the Rx data and fail the read. */ nread = lower->rxdrain(lower); wlwarn("WARNING: Discarded %ld bytes\n", (long)nread); goto errout_with_busy; } else if (hdrlen > bt_buf_tailroom(buf)) { wlerr("ERROR: Not enough space in buffer\n"); goto errout_with_buf; } remaining = hdrlen; wlinfo("Need to get %u bytes\n", remaining); while (remaining > 0) { nread = btuart_read(upper, bt_buf_tail(buf), remaining, 0); if (nread < 0) { wlerr("ERROR: Read returned error %d\n", (int)nread); goto errout_with_buf; } wlinfo("Received %ld bytes\n", (long)nread); buf->len += nread; remaining -= nread; } wlinfo("Full packet received\n"); /* Pass buffer to the stack */ BT_DUMP("Received", buf->data, buf->len); upper->busy = false; bt_hci_receive(buf); return; errout_with_buf: bt_buf_release(buf); errout_with_busy: upper->busy = false; } static void btuart_rxcallback(FAR const struct btuart_lowerhalf_s *lower, FAR void *arg) { FAR struct btuart_upperhalf_s *upper; DEBUGASSERT(lower != NULL && arg != NULL); upper = (FAR struct btuart_upperhalf_s *)arg; if (!upper->busy) { upper->busy = true; int ret = work_queue(HPWORK, &upper->work, btuart_rxwork, arg, 0); if (ret < 0) { upper->busy = false; wlerr("ERROR: work_queue failed: %d\n", ret); } } } /**************************************************************************** * Public Functions ****************************************************************************/ int btuart_send(FAR const struct bt_driver_s *dev, FAR struct bt_buf_s *buf) { FAR struct btuart_upperhalf_s *upper; FAR const struct btuart_lowerhalf_s *lower; FAR uint8_t *type; ssize_t ntotal = 0; upper = (FAR struct btuart_upperhalf_s *)dev; DEBUGASSERT(upper != NULL && upper->lower != NULL); lower = upper->lower; if (bt_buf_headroom(buf) < H4_HEADER_SIZE) { wlerr("Not enough headroom in buffer\n"); return -EINVAL; } type = bt_buf_provide(buf, H4_HEADER_SIZE); switch (buf->type) { case BT_CMD: *type = H4_CMD; break; case BT_ACL_OUT: *type = H4_ACL; break; case BT_EVT: *type = H4_EVT; break; default: wlerr("Unknown buf type %u\n", buf->type); return -EINVAL; } BT_DUMP("Sending", buf->data, buf->len); while (ntotal < buf->len) { ssize_t nwritten; nwritten = lower->write(lower, buf->data + ntotal, buf->len - ntotal); if (nwritten >= 0) { ntotal += nwritten; } else if (nwritten != -EINTR) { return nwritten; } } return OK; } int btuart_open(FAR const struct bt_driver_s *dev) { FAR struct btuart_upperhalf_s *upper; FAR const struct btuart_lowerhalf_s *lower; wlinfo("dev %p\n", dev); upper = (FAR struct btuart_upperhalf_s *)dev; DEBUGASSERT(upper != NULL && upper->lower != NULL); lower = upper->lower; /* Disable Rx callbacks */ lower->rxenable(lower, false); /* Drain any cached Rx data */ lower->rxdrain(lower); /* Attach the Rx event handler */ lower->rxattach(lower, btuart_rxcallback, upper); /* Re-enable Rx callbacks */ lower->rxenable(lower, true); return OK; }