diff --git a/arch/arm/src/imxrt/imxrt_clockconfig.c b/arch/arm/src/imxrt/imxrt_clockconfig.c index 0388961232d..0f96dbc2627 100644 --- a/arch/arm/src/imxrt/imxrt_clockconfig.c +++ b/arch/arm/src/imxrt/imxrt_clockconfig.c @@ -229,9 +229,13 @@ static void imxrt_lcd_clockconfig(void) static void imxrt_pllsetup(void) { +#ifdef CONFIG_ARCH_FAMILY_IMXRT102x + uint32_t pll2reg; + uint32_t pll3reg; +#endif uint32_t reg; -#if (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined (CONFIG_ARCH_FAMILY_IMXRT106x)) +#if (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined (CONFIG_ARCH_FAMILY_IMXRT106x)) /* Init Arm PLL1 */ reg = CCM_ANALOG_PLL_ARM_DIV_SELECT(IMXRT_ARM_PLL_DIV_SELECT) | @@ -258,8 +262,10 @@ static void imxrt_pllsetup(void) /* Init ENET PLL6 */ - reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | CCM_ANALOG_PLL_ENET_ENET1_125M_EN | - CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN | + reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | + CCM_ANALOG_PLL_ENET_ENET1_125M_EN | + CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN | CCM_ANALOG_PLL_ENET_ENET1_DIV_SELECT_50MHZ; putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); @@ -270,59 +276,66 @@ static void imxrt_pllsetup(void) #elif defined(CONFIG_ARCH_FAMILY_IMXRT102x) /* Init Sys PLL2 */ + /* First reset its fractional dividers */ - uint32_t pll2reg=getreg32(IMXRT_CCM_ANALOG_PFD_528); - putreg32(pll2reg | - CCM_ANALOG_PFD_528_PFD0_CLKGATE | - CCM_ANALOG_PFD_528_PFD1_CLKGATE | - CCM_ANALOG_PFD_528_PFD2_CLKGATE | - CCM_ANALOG_PFD_528_PFD3_CLKGATE, - IMXRT_CCM_ANALOG_PFD_528 ); + pll2reg = getreg32(IMXRT_CCM_ANALOG_PFD_528); + putreg32(pll2reg | + CCM_ANALOG_PFD_528_PFD0_CLKGATE | + CCM_ANALOG_PFD_528_PFD1_CLKGATE | + CCM_ANALOG_PFD_528_PFD2_CLKGATE | + CCM_ANALOG_PFD_528_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_528); reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_DIV_SELECT) | CCM_ANALOG_PLL_SYS_ENABLE; putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); + while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & CCM_ANALOG_PLL_SYS_LOCK) == 0) { } - putreg32(pll2reg,IMXRT_CCM_ANALOG_PFD_528); + putreg32(pll2reg, IMXRT_CCM_ANALOG_PFD_528); /* Init USB PLL3 */ + /* capture it's original value */ - uint32_t pll3reg=getreg32(IMXRT_CCM_ANALOG_PFD_480); - putreg32(pll3reg | - CCM_ANALOG_PFD_480_PFD0_CLKGATE | - CCM_ANALOG_PFD_480_PFD1_CLKGATE | - CCM_ANALOG_PFD_480_PFD2_CLKGATE | - CCM_ANALOG_PFD_480_PFD3_CLKGATE, - IMXRT_CCM_ANALOG_PFD_480 ); + pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480); + putreg32(pll3reg | + CCM_ANALOG_PFD_480_PFD0_CLKGATE | + CCM_ANALOG_PFD_480_PFD1_CLKGATE | + CCM_ANALOG_PFD_480_PFD2_CLKGATE | + CCM_ANALOG_PFD_480_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_480); reg = CCM_ANALOG_PLL_USB1_DIV_SELECT(IMXRT_USB1_PLL_DIV_SELECT) | CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_EN_USB_CLKS | CCM_ANALOG_PLL_USB1_POWER; putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB1); + while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & CCM_ANALOG_PLL_USB1_LOCK) == 0) { } - putreg32(pll3reg,IMXRT_CCM_ANALOG_PFD_480); + putreg32(pll3reg, IMXRT_CCM_ANALOG_PFD_480); /* Init Audio PLL4 */ reg = CCM_ANALOG_PLL_AUDIO_DIV_SELECT(IMXRT_AUDIO_PLL_DIV_SELECT) | CCM_ANALOG_PLL_AUDIO_ENABLE; putreg32(reg, IMXRT_CCM_ANALOG_PLL_AUDIO); + while ((getreg32(IMXRT_CCM_ANALOG_PLL_AUDIO) & CCM_ANALOG_PLL_AUDIO_LOCK) == 0) { } /* Init ENET PLL6 */ - reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | CCM_ANALOG_PLL_ENET_ENET1_125M_EN | - CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN; + reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | + CCM_ANALOG_PLL_ENET_ENET1_125M_EN | + CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN; putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); @@ -331,9 +344,8 @@ static void imxrt_pllsetup(void) } #else -#error Unrecognised IMXRT family member for clock config +# error Unrecognised IMXRT family member for clock config #endif - } /**************************************************************************** @@ -482,7 +494,7 @@ void imxrt_clockconfig(void) reg = getreg32(IMXRT_CCM_CSCDR2); reg &= ~CCM_CSCDR2_LPI2C_CLK_PODF_MASK; - reg |= CCM_CSCDR2_LPI2C_CLK_PODF(5-1); + reg |= CCM_CSCDR2_LPI2C_CLK_PODF(5 - 1); putreg32(reg, IMXRT_CCM_CSCDR2); while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) diff --git a/drivers/wireless/bluetooth/Kconfig b/drivers/wireless/bluetooth/Kconfig index b633080a4fb..bfc977d1221 100644 --- a/drivers/wireless/bluetooth/Kconfig +++ b/drivers/wireless/bluetooth/Kconfig @@ -26,6 +26,9 @@ config BLUETOOTH_UART_BT860 bool "Laird BT860" select BLUETOOTH_UART_GENERIC +config BLUETOOTH_UART_SHIM + bool "Generic shim to serial port" + config BLUETOOTH_UART_CC2564 bool "TI CC2564" depends on EXPERIMENTAL diff --git a/drivers/wireless/bluetooth/Make.defs b/drivers/wireless/bluetooth/Make.defs index 367d301ff8d..6c629de27de 100644 --- a/drivers/wireless/bluetooth/Make.defs +++ b/drivers/wireless/bluetooth/Make.defs @@ -44,6 +44,9 @@ CSRCS += bt_uart.c ifeq ($(CONFIG_BLUETOOTH_UART_GENERIC),y) CSRCS += bt_uart_generic.c endif +ifeq ($(CONFIG_BLUETOOTH_UART_SHIM),y) +CSRCS += bt_uart_shim.c bt_uart_generic.c +endif ifeq ($(CONFIG_BLUETOOTH_UART_CC2564),y) CSRCS += bt_uart_cc2564.c endif diff --git a/drivers/wireless/bluetooth/bt_uart.c b/drivers/wireless/bluetooth/bt_uart.c index e6267ed9894..96f994278f7 100644 --- a/drivers/wireless/bluetooth/bt_uart.c +++ b/drivers/wireless/bluetooth/bt_uart.c @@ -81,7 +81,7 @@ static ssize_t btuart_read(FAR struct btuart_upperhalf_s *upper, nread = lower->read(lower, buffer, buflen); if (nread == 0) { - wlinfo("Got zero bytes from UART\n"); + wlwarn("Got zero bytes from UART\n"); if (ntotal < minread) { continue; @@ -91,6 +91,7 @@ static ssize_t btuart_read(FAR struct btuart_upperhalf_s *upper, } else if (nread < 0) { + wlwarn("Returned error %d\n", nread); return nread; } @@ -239,6 +240,12 @@ static void btuart_rxwork(FAR void *arg) while (remaining > 0) { nread = btuart_read(upper, bt_buf_tail(buf), remaining, 0); + if (nread < 0) + { + wlerr("ERROR: Read returned error %d\n", nread); + goto errout_with_buf; + } + wlinfo("Received %ld bytes\n", (long)nread); buf->len += nread; @@ -257,9 +264,8 @@ static void btuart_rxwork(FAR void *arg) /* Pass buffer to the stack */ - upper->busy = false; - BT_DUMP("Received", buf->data, buf->len); + upper->busy = false; bt_hci_receive(buf); return; @@ -281,15 +287,13 @@ static void btuart_rxcallback(FAR const struct btuart_lowerhalf_s *lower, 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); } - else - { - upper->busy = true; - } } } diff --git a/drivers/wireless/bluetooth/bt_uart_shim.c b/drivers/wireless/bluetooth/bt_uart_shim.c new file mode 100644 index 00000000000..331d2606f72 --- /dev/null +++ b/drivers/wireless/bluetooth/bt_uart_shim.c @@ -0,0 +1,489 @@ +/**************************************************************************** + * drivers/wireless/bluetooth/bt_uart_shim.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Dave Marples + * + * 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 NuttX 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 OWNER 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HCIUART_DEFAULT_SPEED 115200 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure is the variable state of the binding to the UART */ + +struct hciuart_state_s + { + /* Registered Rx callback */ + + btuart_rxcallback_t callback; /* Rx callback function */ + void *arg; /* Rx callback argument */ + + int h; /* File handle to serial device */ + struct file f; /* File structure, detached */ + + sem_t dready; /* Semaphore used by the poll operation */ + bool enabled; /* Flag indicating that reception is enabled */ + + int serialmontask; /* The receive serial octets task handle */ + volatile struct pollfd p; /* Polling structure for serial monitor task */ + }; + +struct hciuart_config_s + { + /* Setup the interface from the upper to the lower */ + + struct btuart_lowerhalf_s lower; /* Generic UART lower half */ + struct hciuart_state_s state; /* Variable state */ + }; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* UART Lower-Half Methods */ + +static void hciuart_rxattach(const struct btuart_lowerhalf_s *lower, + btuart_rxcallback_t callback, void *arg); +static void hciuart_rxenable(const struct btuart_lowerhalf_s *lower, + bool enable); +static int hciuart_setbaud(const struct btuart_lowerhalf_s *lower, + uint32_t baud); +static ssize_t hciuart_read(const struct btuart_lowerhalf_s *lower, + void *buffer, size_t buflen); +static ssize_t hciuart_write(const struct btuart_lowerhalf_s *lower, + const void *buffer, size_t buflen); +static ssize_t hciuart_rxdrain(const struct btuart_lowerhalf_s *lower); + +/* This structure is the configuration of the HCI UART shim */ + +static struct btuart_lowerhalf_s lowerstatic = +{ + .rxattach = hciuart_rxattach, + .rxenable = hciuart_rxenable, + .setbaud = hciuart_setbaud, + .read = hciuart_read, + .write = hciuart_write, + .rxdrain = hciuart_rxdrain +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct hciuart_config_s *g_n; /* This is held global because its + * inconvenient to pass to the task */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hciuart_rxattach + * + * Description: + * Attach/detach the upper half Rx callback. + * + * rxattach() allows the upper half logic to attach a callback function + * that will be used to inform the upper half that an Rx frame is + * available. This callback will, most likely, be invoked in the + * context of an interrupt callback. The receive() method should then + * be invoked in order to receive the obtain the Rx frame data. + * + ****************************************************************************/ + +static void +hciuart_rxattach(const struct btuart_lowerhalf_s *lower, + btuart_rxcallback_t callback, void *arg) +{ + struct hciuart_config_s *config = (struct hciuart_config_s *)lower; + struct hciuart_state_s *state; + irqstate_t flags; + + state = &config->state; + + /* If the callback is NULL, then we are detaching */ + + flags = spin_lock_irqsave(); + if (callback == NULL) + { + /* Disable Rx callbacks and detach the Rx callback */ + + state->callback = NULL; + state->arg = NULL; + } + + /* Otherwise, we are attaching */ + + else + { + state->callback = NULL; + state->arg = arg; + state->callback = callback; + } + + hciuart_setbaud(lower, HCIUART_DEFAULT_SPEED); + spin_unlock_irqrestore(flags); +} + +/**************************************************************************** + * Name: hciuart_rxenable + * + * Description: + * Enable/disable RX callbacks from the HCI UART. + * + * hciuart_rxenable() may be used to enable or disable callback events. + * This probably translates to enabling and disabled Rx interrupts at + * the UART. NOTE: Rx event notification should be done sparingly: + * Rx data overrun may occur when Rx events are disabled! + * + ****************************************************************************/ + +static void hciuart_rxenable(const struct btuart_lowerhalf_s *lower, + bool enable) +{ + struct hciuart_config_s *config = (struct hciuart_config_s *)lower; + struct hciuart_state_s *s = &config->state; + + irqstate_t flags = spin_lock_irqsave(); + if ((enable) && (!s->enabled)) + { + wlinfo("Enable\n"); + s->enabled = true; + } + else + { + s->enabled = false; + wlinfo("Disable\n"); + } + + spin_unlock_irqrestore(flags); +} + +/**************************************************************************** + * Name: hciuart_setbaud + * + * Description: + * The HCI UART comes up with some initial BAUD rate. Some support + * auto-BAUD detection, some support writing a configuration file to + * select the initial BAUD. The simplest strategy, however, is simply + * to use the HCI UART's default initial BAUD to perform the basic + * bring up, then send a vendor-specific command to increase the HCI + * UARTs BAUD. This method then may be used to adjust the lower half + * driver to the new HCI UART BAUD. + * + ****************************************************************************/ + +static int +hciuart_setbaud(const struct btuart_lowerhalf_s *lower, uint32_t baud) +{ + struct hciuart_config_s *config = (struct hciuart_config_s *)lower; + struct hciuart_state_s *state = &config->state; + int ret; + + struct termios tio; + +#ifndef CONFIG_SERIAL_TERMIOS +# error TERMIOS Support needed for hciuart_setbaud +#endif + + ret = file_ioctl(&state->f, TCGETS, (long unsigned int)&tio); + if (ret) + { + wlerr("hciuart_setbaud: ERROR during TCGETS\n"); + return ret; + } + + if (baud != 0) + { + cfsetspeed(&tio, baud); + } + + /* To be a H4 interface, CTS/RTS are needed */ + + tio.c_cflag |= CRTS_IFLOW | CCTS_OFLOW; + + ret = file_ioctl(&state->f, TCSETS, (long unsigned int)&tio); + + if (ret) + { + wlerr("hciuart_setbaud: ERROR during TCSETS, does UART support CTS/RTS?\n"); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: hciuart_read + * + * Description: + * Read UART data. + * + * hciuart_read() after receipt of a callback notifying the upper half of + * the availability of Rx frame, the upper half may call the receive() + * method in order to obtain the buffered Rx frame data. + * + ****************************************************************************/ + +static ssize_t +hciuart_read(const struct btuart_lowerhalf_s *lower, + void *buffer, size_t buflen) +{ + struct hciuart_config_s *config = (struct hciuart_config_s *)lower; + struct hciuart_state_s *state = &config->state; + size_t ntotal; + + wlinfo("config %p buffer %p buflen %lu\n", config, buffer, (size_t) buflen); + + /* NOTE: This assumes that the caller has exclusive access to the Rx buffer, + * i.e., one lower half instance can server only one upper half! + */ + + ntotal = file_read(&state->f, buffer, buflen); + return ntotal; +} + +/**************************************************************************** + * Name: hciuart_write + * + * Description: + * Write UART data. + * + * hciuart_write() will add the outgoing frame to the Tx buffer and will + * return immediately. This function may block only in the event that + * there is insufficient buffer space to hold the Tx frame data. In that + * case the lower half will block until there is sufficient to buffer + * the entire outgoing packet. + * + ****************************************************************************/ + +static ssize_t +hciuart_write(const struct btuart_lowerhalf_s *lower, + const void *buffer, size_t buflen) +{ + const struct hciuart_config_s *config + = (const struct hciuart_config_s *)lower; + struct hciuart_state_s *state = &config->state; + + wlinfo("config %p buffer %p buflen %lu\n", config, buffer, (size_t) buflen); + + buflen = file_write(&state->f, buffer, buflen); + + return buflen; +} + +/**************************************************************************** + * Name: hciuart_rxdrain + * + * Description: + * Flush/drain all buffered RX data + * + ****************************************************************************/ + +static ssize_t hciuart_rxdrain(const struct btuart_lowerhalf_s *lower) +{ + struct hciuart_config_s *config = (struct hciuart_config_s *)lower; + struct hciuart_state_s *s = &config->state; + + file_ioctl(&s->f, TCDRN, 0); + return 0; +} + +/**************************************************************************** + * Name: hcicollecttask + * + * Description: + * Loop and alert when serial data arrive + * + ****************************************************************************/ + +static int hcicollecttask(int argc, FAR char **argv) +{ + struct hciuart_state_s *s = &g_n->state; + + file_poll(&s->f, &s->p, true); + + for (; ; ) + { + /* Wait for data to arrive */ + + int ret = nxsem_wait(s->p.sem); + if (ret < 0) + { + wlwarn("Poll interrupted %d\n", ret); + continue; + } + + /* These flags can change dynamically as new events occur, so snapshot */ + + irqstate_t flags = enter_critical_section(); + uint32_t tevents = s->p.revents; + s->p.revents = 0; + leave_critical_section(flags); + + wlinfo("Poll completed %d\n", tevents); + + /* Given the nature of file_poll, there are multiple reasons why + * we might be here, so make sure we only consider the read. + */ + + if (tevents & POLLIN) + { + if (!s->enabled) + { + /* We aren't expected to be listening, so drop these data */ + + wlwarn("Dropping data\n"); + hciuart_rxdrain(&g_n->lower); + } + else + { + if (s->callback != NULL) + { + wlinfo("Activating callback\n"); + s->callback(&g_n->lower, s->arg); + } + else + { + wlwarn("Dropping data (no CB)\n"); + hciuart_rxdrain(&g_n->lower); + } + } + } + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hci_uart_getdevice + * + * Description: + * Get a pointer to the device that will be used to communicate with the + * regular serial port on the HCI. + * + * Input Paramters: + * Entry in filesystem hirearchy for device + * + * Returned Value: + * Pointer to device interface + * + ****************************************************************************/ + +void *hci_uart_getdevice(char *path) +{ + struct hciuart_state_s *s; + int f2; + + /* Get the memory for this shim instance */ + + g_n = (struct hciuart_config_s *)kmm_zalloc(sizeof(struct hciuart_config_s)); + + if (!g_n) + { + return 0; + } + + s = &g_n->state; + + f2 = open(path, O_RDWR | O_BINARY); + + if (f2 < 0) + { + kmm_free(g_n); + g_n = 0; + return 0; + } + + /* Detach the file and give it somewhere to be kept */ + + s->h = file_detach(f2, &s->f); + + /* Hook the routines in */ + + memcpy(&g_n->lower, &lowerstatic, sizeof(struct btuart_lowerhalf_s)); + + /* Put materials into poll structure */ + + nxsem_init(&s->dready, 0, 0); + nxsem_setprotocol(&s->dready, SEM_PRIO_NONE); + + s->p.fd = s->h; + s->p.events = POLLIN; + s->p.sem = &s->dready; + + s->enabled = true; + + s->serialmontask = kthread_create("HCICollect", + CONFIG_BLUETOOTH_TXCONN_PRIORITY, + 1024, hcicollecttask, NULL); + + return g_n; +} diff --git a/drivers/wireless/ieee80211/bcmf_netdev.c b/drivers/wireless/ieee80211/bcmf_netdev.c index a0f6ef55c0c..dcd90b8f6b2 100644 --- a/drivers/wireless/ieee80211/bcmf_netdev.c +++ b/drivers/wireless/ieee80211/bcmf_netdev.c @@ -211,7 +211,7 @@ static int bcmf_transmit(FAR struct bcmf_dev_s *priv, return -EIO; } - NETDEV_TXPACKETS(priv->bc_dev); + NETDEV_TXPACKETS(&priv->bc_dev); return OK; } @@ -351,7 +351,7 @@ static void bcmf_receive(FAR struct bcmf_dev_s *priv) */ if (priv->bc_dev.d_len > 0) - { + { /* Update the Ethernet header with the correct MAC address */ #ifdef CONFIG_NET_IPv4 @@ -531,7 +531,9 @@ static void bcmf_rxpoll(FAR void *arg) * transmissions. */ - // bcmf_txdone(priv); +#if 0 + bcmf_txdone(priv); +#endif net_unlock(); } diff --git a/include/nuttx/wireless/bluetooth/bt_hci.h b/include/nuttx/wireless/bluetooth/bt_hci.h index 482681970c7..efd6b4eeeae 100644 --- a/include/nuttx/wireless/bluetooth/bt_hci.h +++ b/include/nuttx/wireless/bluetooth/bt_hci.h @@ -113,6 +113,9 @@ /* Construct OpCode from OGF and OCF */ +#define BT_OGF_VENDOR 0x18 +#define BT_HC_VN_READ_CONT_FEATURES BT_OP(BT_OGF_VENDOR, 0x06E) + #define BT_OP(ogf, ocf) ((ocf) | ((ogf) << 10)) #define BT_HCI_OP_DISCONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0006) diff --git a/wireless/bluetooth/bt_hcicore.c b/wireless/bluetooth/bt_hcicore.c index 34c61756549..698fd8e5df6 100644 --- a/wireless/bluetooth/bt_hcicore.c +++ b/wireless/bluetooth/bt_hcicore.c @@ -83,6 +83,8 @@ #define TIMEOUT_SEC 2 #define TIMEOUT_NSEC 500 * 1024 * 1024 +#define BT_FIRMWARE_UPLOAD /* REVISIT: Should be a Kconfig option? */ + /**************************************************************************** * Public Data ****************************************************************************/ @@ -95,6 +97,11 @@ struct bt_dev_s g_btdev; +#ifdef BT_FIRMWARE_UPLOAD +extern const uint8_t bt_firmware_hcd[]; +const long int bt_firmware_len; +#endiuf + /**************************************************************************** * Private Data ****************************************************************************/ @@ -186,8 +193,8 @@ static FAR struct bt_buf_s *bt_dequeue_bufwork(FAR struct bt_bufferlist_s *list) for (prev = list->head; prev && prev->flink != buf; prev = prev->flink) - { - } + { + } if (prev != NULL) { @@ -421,7 +428,8 @@ static void hci_cmd_status(FAR struct bt_buf_s *buf) static void hci_num_completed_packets(FAR struct bt_buf_s *buf) { FAR struct bt_hci_evt_num_completed_packets_s *evt = (FAR void *)buf->data; - uint16_t i, num_handles = BT_LE162HOST(evt->num_handles); + uint16_t num_handles = BT_LE162HOST(evt->num_handles); + uint16_t i; wlinfo("num_handles %u\n", num_handles); @@ -555,7 +563,8 @@ static int bt_hci_start_scanning(uint8_t scan_type, uint8_t scan_filter) static int bt_hci_stop_scanning(void) { - FAR struct bt_buf_s *buf, *rsp; + FAR struct bt_buf_s *buf; + FAR struct bt_buf_s *rsp; FAR struct bt_hci_cp_le_set_scan_enable_s *scan_enable; int ret; @@ -616,7 +625,7 @@ static int hci_le_create_conn(FAR const bt_addr_le_t *addr) cp->conn_interval_min = BT_HOST2LE16(0x0018); cp->scan_interval = BT_HOST2LE16(0x0060); cp->scan_window = BT_HOST2LE16(0x0030); - cp->supervision_timeout = BT_HOST2LE16(0x07D0); + cp->supervision_timeout = BT_HOST2LE16(0x07d0); return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL); } @@ -1184,6 +1193,168 @@ static void le_read_buffer_size_complete(FAR struct bt_buf_s *buf) g_btdev.le_pkts = rp->le_max_num; } +/**************************************************************************** + * Name: bt_check_fw_upload() + * + * Description: + * This function uploads firmware to the bt device if it is needed. + * + * Input Parameters: + * none + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef BT_FIRMWARE_UPLOAD +static int bt_check_fw_upload(void) +{ + uint8_t *rp = (uint8_t *)bt_firmware_hcd; + FAR uint8_t *bm; + + FAR struct bt_buf_s *buf; + FAR struct bt_buf_s *rsp; + uint8_t command; + uint8_t command2; + uint8_t txlen; + uint32_t addr; + int ret = OK; + + const uint8_t HCD_PATCHRAM_COMMAND = 0x2e; + const uint8_t HCD_LAUNCH_COMMAND = 0x4e; + const uint8_t HCD_WRITE_COMMAND = 0x4c; + const uint8_t HCD_COMMAND_BYTE2 = 0xfc; + const uint8_t HCD_LAUNCH_LEN = 4; + const uint8_t HCD_LAUNCH_PAYLOAD = 0xff; + + ret = bt_hci_cmd_send_sync(HCD_PATCHRAM_COMMAND | (HCD_COMMAND_BYTE2 << 8), + NULL, &rsp); + bt_buf_release(rsp); + + if (ret < 0) + { + wlerr("ERROR: HCD_LAUNCH_COMMAND failed: %d\n", ret); + return ret; + } + + while (rp < (bt_firmware_hcd + bt_firmware_len)) + { + command = rp[0]; + command2 = rp[1]; + txlen = rp[2]; + addr = rp[3] | (rp[4] << 8) | (rp[5] << 16) | (rp[6] << 24); + + /* Sanity check on the data as they are read */ + + if (command == HCD_LAUNCH_COMMAND) + { + break; + } + + if ((txlen < 4) || (command2 != HCD_COMMAND_BYTE2) || + (command != HCD_WRITE_COMMAND)) + { + wlerr("ERROR: Firmware file format\n"); + return -ENODEV; + } + + /* Jump past the header and remove the length from the send amount */ + + rp += 7; + txlen -= 4; + + while (txlen > 0) + { + uint8_t txelement = txlen > + (BLUETOOTH_MAX_MTU - 4) ? (BLUETOOTH_MAX_MTU - + 4) : txlen; + buf = bt_hci_cmd_create(command | (command2 << 8), txelement + 4); + if (buf == NULL) + { + wlerr("ERROR: Failed to create buffer\n"); + return -ENOBUFS; + } + + bm = (uint8_t *)bt_buf_extend(buf, txelement + 4); + bm[0] = addr & 0xff; + bm[1] = (addr >> 8) & 0xff; + bm[2] = (addr >> 16) & 0xff; + bm[3] = (addr >> 24) & 0xff; + memcpy(&bm[4], rp, txelement); + + ret = bt_hci_cmd_send_sync(command | (command2 << 8), buf, &rsp); + if (ret < 0) + { + wlerr("ERROR: Upload failed: %d\n", ret); + return ret; + } + + bt_buf_release(rsp); + rp += txelement; + txlen -= txelement; + addr += txelement; + } + } + + /* To have gotten here we must've uploaded correctly */ + + buf = bt_hci_cmd_create(HCD_LAUNCH_COMMAND | (HCD_COMMAND_BYTE2 << 8), + HCD_LAUNCH_LEN); + if (buf == NULL) + { + wlerr("ERROR: Failed to create buffer\n"); + return -ENOBUFS; + } + + bm = (uint8_t *)bt_buf_extend(buf, HCD_LAUNCH_LEN); + memset(bm, HCD_LAUNCH_PAYLOAD, HCD_LAUNCH_LEN); + + ret = bt_hci_cmd_send_sync(HCD_LAUNCH_COMMAND | (HCD_COMMAND_BYTE2 << 8), + buf, &rsp); + bt_buf_release(rsp); + + if (ret < 0) + { + wlerr("ERROR: HCD_LAUNCH_COMMAND failed: %d\n", ret); + return ret; + } + + /* Give everything time to start up */ + + nxsig_usleep(1000000); + + /* Everything happened and the new firmware is launched */ + + ret = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp); + if (ret < 0) + { + wlerr("ERROR: BT_HCI_OP_RESET failed: %d\n", ret); + return ret; + } + + bt_buf_release(rsp); + + return OK; +} + +#else +# define bt_check_fw_upload() OK +#endif + +/**************************************************************************** + * Name: hci_initialize() + * + * Description: + * + * Input Parameters: + * none + * + * Returned Value: + * None + * + ****************************************************************************/ + static int hci_initialize(void) { FAR struct bt_hci_cp_host_buffer_size_s *hbs; @@ -1195,14 +1366,30 @@ static int hci_initialize(void) /* Send HCI_RESET */ - bt_hci_cmd_send(BT_HCI_OP_RESET, NULL); + ret = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp); + if (ret < 0) + { + wlerr("ERROR: BT_HCI_OP_RESET failed: %d\n", ret); + return ret; + } + + bt_buf_release(rsp); + + /* Upload new firmware when/if needed */ + + ret = bt_check_fw_upload(); + if (ret < 0) + { + wlerr("ERROR: Firmware upload failed: %d\n", ret); + return ret; + } /* Read Local Supported Features */ ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp); if (ret < 0) { - wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret); + wlerr("ERROR: BT_HCI_OP_READ_LOCAL_FEATURES failed: %d\n", ret); return ret; } @@ -1246,7 +1433,7 @@ static int hci_initialize(void) ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL, &rsp); if (ret < 0) { - wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret); + wlerr("ERROR: BT_HCI_OP_LE_READ_LOCAL_FEATURES failed: %d\n", ret); return ret; } @@ -1258,7 +1445,7 @@ static int hci_initialize(void) ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, NULL, &rsp); if (ret < 0) { - wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret); + wlerr("ERROR: BT_HCI_OP_LE_READ_BUFFER_SIZE failed: %d\n", ret); return ret; } @@ -1274,6 +1461,7 @@ static int hci_initialize(void) ev = bt_buf_extend(buf, sizeof(*ev)); memset(ev, 0, sizeof(*ev)); + ev->events[0] |= 0x10; /* Disconnection Complete */ ev->events[1] |= 0x08; /* Read Remote Version Information Complete */ ev->events[1] |= 0x20; /* Command Complete */ @@ -1574,6 +1762,7 @@ void bt_hci_receive(FAR struct bt_buf_s *buf) } /* All others use the low priority work queue */ + /* Add the buffer to the low priority Rx buffer list */ bt_enqueue_bufwork(&g_lp_rxlist, buf);