Add support for the BCM43438A1 Bluetooth capability. It also adds a serial 'shim' to allow any regular serial port that can support a Bluetooth H4 interface (i.e. it has RTS/CTS) to be used to drive a Bluetooth device (Get a handle to it via hci_uart_getdevice("/dev/xxx") and then pass it to the btuart_register function.

Most of the bluetooth and wifi chips appear to need external firmware, and the 43438 is no exception. Fortunately, since Cypress got involved, these are much more straightforward to obtain and are shipped as part of their SDK, which is downloadable from their website.  Those firmwares are already provided as C arrays, so their names just need updating to;

const unsigned char bt_firmware_hcd -> The bt firmware array.

const int bt_firmware_len = sizeof(bt_firmware_hcd);
This commit is contained in:
Dave Marples
2019-09-21 07:16:37 -06:00
committed by Gregory Nutt
parent 218da47318
commit 05bbbec3e1
8 changed files with 748 additions and 43 deletions
+36 -24
View File
@@ -229,9 +229,13 @@ static void imxrt_lcd_clockconfig(void)
static void imxrt_pllsetup(void) static void imxrt_pllsetup(void)
{ {
#ifdef CONFIG_ARCH_FAMILY_IMXRT102x
uint32_t pll2reg;
uint32_t pll3reg;
#endif
uint32_t reg; 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 */ /* Init Arm PLL1 */
reg = CCM_ANALOG_PLL_ARM_DIV_SELECT(IMXRT_ARM_PLL_DIV_SELECT) | reg = CCM_ANALOG_PLL_ARM_DIV_SELECT(IMXRT_ARM_PLL_DIV_SELECT) |
@@ -258,8 +262,10 @@ static void imxrt_pllsetup(void)
/* Init ENET PLL6 */ /* Init ENET PLL6 */
reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | CCM_ANALOG_PLL_ENET_ENET1_125M_EN | reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ |
CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN | 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; CCM_ANALOG_PLL_ENET_ENET1_DIV_SELECT_50MHZ;
putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET);
@@ -270,59 +276,66 @@ static void imxrt_pllsetup(void)
#elif defined(CONFIG_ARCH_FAMILY_IMXRT102x) #elif defined(CONFIG_ARCH_FAMILY_IMXRT102x)
/* Init Sys PLL2 */ /* Init Sys PLL2 */
/* First reset its fractional dividers */ /* First reset its fractional dividers */
uint32_t pll2reg=getreg32(IMXRT_CCM_ANALOG_PFD_528); pll2reg = getreg32(IMXRT_CCM_ANALOG_PFD_528);
putreg32(pll2reg | putreg32(pll2reg |
CCM_ANALOG_PFD_528_PFD0_CLKGATE | CCM_ANALOG_PFD_528_PFD0_CLKGATE |
CCM_ANALOG_PFD_528_PFD1_CLKGATE | CCM_ANALOG_PFD_528_PFD1_CLKGATE |
CCM_ANALOG_PFD_528_PFD2_CLKGATE | CCM_ANALOG_PFD_528_PFD2_CLKGATE |
CCM_ANALOG_PFD_528_PFD3_CLKGATE, CCM_ANALOG_PFD_528_PFD3_CLKGATE,
IMXRT_CCM_ANALOG_PFD_528 ); IMXRT_CCM_ANALOG_PFD_528);
reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_DIV_SELECT) | reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_DIV_SELECT) |
CCM_ANALOG_PLL_SYS_ENABLE; CCM_ANALOG_PLL_SYS_ENABLE;
putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS);
while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & CCM_ANALOG_PLL_SYS_LOCK) == 0) 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 */ /* Init USB PLL3 */
/* capture it's original value */ /* capture it's original value */
uint32_t pll3reg=getreg32(IMXRT_CCM_ANALOG_PFD_480); pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480);
putreg32(pll3reg | putreg32(pll3reg |
CCM_ANALOG_PFD_480_PFD0_CLKGATE | CCM_ANALOG_PFD_480_PFD0_CLKGATE |
CCM_ANALOG_PFD_480_PFD1_CLKGATE | CCM_ANALOG_PFD_480_PFD1_CLKGATE |
CCM_ANALOG_PFD_480_PFD2_CLKGATE | CCM_ANALOG_PFD_480_PFD2_CLKGATE |
CCM_ANALOG_PFD_480_PFD3_CLKGATE, CCM_ANALOG_PFD_480_PFD3_CLKGATE,
IMXRT_CCM_ANALOG_PFD_480 ); IMXRT_CCM_ANALOG_PFD_480);
reg = CCM_ANALOG_PLL_USB1_DIV_SELECT(IMXRT_USB1_PLL_DIV_SELECT) | 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_ENABLE | CCM_ANALOG_PLL_USB1_EN_USB_CLKS |
CCM_ANALOG_PLL_USB1_POWER; CCM_ANALOG_PLL_USB1_POWER;
putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB1); putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB1);
while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & CCM_ANALOG_PLL_USB1_LOCK) == 0) 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 */ /* Init Audio PLL4 */
reg = CCM_ANALOG_PLL_AUDIO_DIV_SELECT(IMXRT_AUDIO_PLL_DIV_SELECT) | reg = CCM_ANALOG_PLL_AUDIO_DIV_SELECT(IMXRT_AUDIO_PLL_DIV_SELECT) |
CCM_ANALOG_PLL_AUDIO_ENABLE; CCM_ANALOG_PLL_AUDIO_ENABLE;
putreg32(reg, IMXRT_CCM_ANALOG_PLL_AUDIO); putreg32(reg, IMXRT_CCM_ANALOG_PLL_AUDIO);
while ((getreg32(IMXRT_CCM_ANALOG_PLL_AUDIO) & CCM_ANALOG_PLL_AUDIO_LOCK) == 0) while ((getreg32(IMXRT_CCM_ANALOG_PLL_AUDIO) & CCM_ANALOG_PLL_AUDIO_LOCK) == 0)
{ {
} }
/* Init ENET PLL6 */ /* Init ENET PLL6 */
reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | CCM_ANALOG_PLL_ENET_ENET1_125M_EN | reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ |
CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN; 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); putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET);
@@ -331,9 +344,8 @@ static void imxrt_pllsetup(void)
} }
#else #else
#error Unrecognised IMXRT family member for clock config # error Unrecognised IMXRT family member for clock config
#endif #endif
} }
/**************************************************************************** /****************************************************************************
@@ -482,7 +494,7 @@ void imxrt_clockconfig(void)
reg = getreg32(IMXRT_CCM_CSCDR2); reg = getreg32(IMXRT_CCM_CSCDR2);
reg &= ~CCM_CSCDR2_LPI2C_CLK_PODF_MASK; 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); putreg32(reg, IMXRT_CCM_CSCDR2);
while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0)
+3
View File
@@ -26,6 +26,9 @@ config BLUETOOTH_UART_BT860
bool "Laird BT860" bool "Laird BT860"
select BLUETOOTH_UART_GENERIC select BLUETOOTH_UART_GENERIC
config BLUETOOTH_UART_SHIM
bool "Generic shim to serial port"
config BLUETOOTH_UART_CC2564 config BLUETOOTH_UART_CC2564
bool "TI CC2564" bool "TI CC2564"
depends on EXPERIMENTAL depends on EXPERIMENTAL
+3
View File
@@ -44,6 +44,9 @@ CSRCS += bt_uart.c
ifeq ($(CONFIG_BLUETOOTH_UART_GENERIC),y) ifeq ($(CONFIG_BLUETOOTH_UART_GENERIC),y)
CSRCS += bt_uart_generic.c CSRCS += bt_uart_generic.c
endif endif
ifeq ($(CONFIG_BLUETOOTH_UART_SHIM),y)
CSRCS += bt_uart_shim.c bt_uart_generic.c
endif
ifeq ($(CONFIG_BLUETOOTH_UART_CC2564),y) ifeq ($(CONFIG_BLUETOOTH_UART_CC2564),y)
CSRCS += bt_uart_cc2564.c CSRCS += bt_uart_cc2564.c
endif endif
+11 -7
View File
@@ -81,7 +81,7 @@ static ssize_t btuart_read(FAR struct btuart_upperhalf_s *upper,
nread = lower->read(lower, buffer, buflen); nread = lower->read(lower, buffer, buflen);
if (nread == 0) if (nread == 0)
{ {
wlinfo("Got zero bytes from UART\n"); wlwarn("Got zero bytes from UART\n");
if (ntotal < minread) if (ntotal < minread)
{ {
continue; continue;
@@ -91,6 +91,7 @@ static ssize_t btuart_read(FAR struct btuart_upperhalf_s *upper,
} }
else if (nread < 0) else if (nread < 0)
{ {
wlwarn("Returned error %d\n", nread);
return nread; return nread;
} }
@@ -239,6 +240,12 @@ static void btuart_rxwork(FAR void *arg)
while (remaining > 0) while (remaining > 0)
{ {
nread = btuart_read(upper, bt_buf_tail(buf), 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); wlinfo("Received %ld bytes\n", (long)nread);
buf->len += nread; buf->len += nread;
@@ -257,9 +264,8 @@ static void btuart_rxwork(FAR void *arg)
/* Pass buffer to the stack */ /* Pass buffer to the stack */
upper->busy = false;
BT_DUMP("Received", buf->data, buf->len); BT_DUMP("Received", buf->data, buf->len);
upper->busy = false;
bt_hci_receive(buf); bt_hci_receive(buf);
return; return;
@@ -281,15 +287,13 @@ static void btuart_rxcallback(FAR const struct btuart_lowerhalf_s *lower,
if (!upper->busy) if (!upper->busy)
{ {
upper->busy = true;
int ret = work_queue(HPWORK, &upper->work, btuart_rxwork, arg, 0); int ret = work_queue(HPWORK, &upper->work, btuart_rxwork, arg, 0);
if (ret < 0) if (ret < 0)
{ {
upper->busy = false;
wlerr("ERROR: work_queue failed: %d\n", ret); wlerr("ERROR: work_queue failed: %d\n", ret);
} }
else
{
upper->busy = true;
}
} }
} }
+489
View File
@@ -0,0 +1,489 @@
/****************************************************************************
* drivers/wireless/bluetooth/bt_uart_shim.c
*
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
* Author: Dave Marples <dave@marples.net>
*
* 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 <nuttx/config.h>
#include <debug.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <semaphore.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <nuttx/arch.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include <nuttx/kthread.h>
#include <nuttx/semaphore.h>
#include <nuttx/serial/tioctl.h>
#include <nuttx/wireless/bluetooth/bt_uart.h>
#include <termios.h>
/****************************************************************************
* 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;
}
+5 -3
View File
@@ -211,7 +211,7 @@ static int bcmf_transmit(FAR struct bcmf_dev_s *priv,
return -EIO; return -EIO;
} }
NETDEV_TXPACKETS(priv->bc_dev); NETDEV_TXPACKETS(&priv->bc_dev);
return OK; return OK;
} }
@@ -351,7 +351,7 @@ static void bcmf_receive(FAR struct bcmf_dev_s *priv)
*/ */
if (priv->bc_dev.d_len > 0) if (priv->bc_dev.d_len > 0)
{ {
/* Update the Ethernet header with the correct MAC address */ /* Update the Ethernet header with the correct MAC address */
#ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv4
@@ -531,7 +531,9 @@ static void bcmf_rxpoll(FAR void *arg)
* transmissions. * transmissions.
*/ */
// bcmf_txdone(priv); #if 0
bcmf_txdone(priv);
#endif
net_unlock(); net_unlock();
} }
@@ -113,6 +113,9 @@
/* Construct OpCode from OGF and OCF */ /* 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_OP(ogf, ocf) ((ocf) | ((ogf) << 10))
#define BT_HCI_OP_DISCONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0006) #define BT_HCI_OP_DISCONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0006)
+198 -9
View File
@@ -83,6 +83,8 @@
#define TIMEOUT_SEC 2 #define TIMEOUT_SEC 2
#define TIMEOUT_NSEC 500 * 1024 * 1024 #define TIMEOUT_NSEC 500 * 1024 * 1024
#define BT_FIRMWARE_UPLOAD /* REVISIT: Should be a Kconfig option? */
/**************************************************************************** /****************************************************************************
* Public Data * Public Data
****************************************************************************/ ****************************************************************************/
@@ -95,6 +97,11 @@
struct bt_dev_s g_btdev; 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 * 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; for (prev = list->head;
prev && prev->flink != buf; prev && prev->flink != buf;
prev = prev->flink) prev = prev->flink)
{ {
} }
if (prev != NULL) 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) 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; 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); 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) 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; FAR struct bt_hci_cp_le_set_scan_enable_s *scan_enable;
int ret; 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->conn_interval_min = BT_HOST2LE16(0x0018);
cp->scan_interval = BT_HOST2LE16(0x0060); cp->scan_interval = BT_HOST2LE16(0x0060);
cp->scan_window = BT_HOST2LE16(0x0030); 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); 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; 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) static int hci_initialize(void)
{ {
FAR struct bt_hci_cp_host_buffer_size_s *hbs; FAR struct bt_hci_cp_host_buffer_size_s *hbs;
@@ -1195,14 +1366,30 @@ static int hci_initialize(void)
/* Send HCI_RESET */ /* 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 */ /* Read Local Supported Features */
ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp); ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp);
if (ret < 0) 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; 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); ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL, &rsp);
if (ret < 0) 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; 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); ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, NULL, &rsp);
if (ret < 0) 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; return ret;
} }
@@ -1274,6 +1461,7 @@ static int hci_initialize(void)
ev = bt_buf_extend(buf, sizeof(*ev)); ev = bt_buf_extend(buf, sizeof(*ev));
memset(ev, 0, sizeof(*ev)); memset(ev, 0, sizeof(*ev));
ev->events[0] |= 0x10; /* Disconnection Complete */ ev->events[0] |= 0x10; /* Disconnection Complete */
ev->events[1] |= 0x08; /* Read Remote Version Information Complete */ ev->events[1] |= 0x08; /* Read Remote Version Information Complete */
ev->events[1] |= 0x20; /* Command 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 */ /* All others use the low priority work queue */
/* Add the buffer to the low priority Rx buffer list */ /* Add the buffer to the low priority Rx buffer list */
bt_enqueue_bufwork(&g_lp_rxlist, buf); bt_enqueue_bufwork(&g_lp_rxlist, buf);