diff --git a/arch/x86/bsp/driver_static_irq_stubs.s b/arch/x86/bsp/driver_static_irq_stubs.s index 9657888337e..4361d4803b4 100644 --- a/arch/x86/bsp/driver_static_irq_stubs.s +++ b/arch/x86/bsp/driver_static_irq_stubs.s @@ -64,6 +64,10 @@ by x86 BSPs. GTEXT(_i8253IntStub) #endif +#if defined(CONFIG_BLUETOOTH_UART) + GTEXT(_bluetooth_uart_stub) +#endif /* CONFIG_BLUETOOTH */ + #if defined(CONFIG_CONSOLE_HANDLER) GTEXT(_console_uart_stub) #endif /* CONFIG_CONSOLE_HANDLER */ @@ -146,6 +150,26 @@ SECTION_FUNC(TEXT, _i8253IntStub) jmp _IntExit /* Inform kernel interrupt is done */ #endif /* CONFIG_PIT */ +#if defined(CONFIG_BLUETOOTH_UART) +#if defined(CONFIG_PIC) +SECTION_FUNC(TEXT, _bluetooth_uart_stub) + call _IntEnt /* Inform kernel interrupt has begun */ + pushl $0 /* Push dummy parameter */ + call bt_uart_isr /* Call actual interrupt handler */ + call _i8259_eoi_master /* Inform the PIC interrupt is done */ + addl $4, %esp /* Clean-up stack from push above */ + jmp _IntExit /* Inform kernel interrupt is done */ +#elif defined(CONFIG_IOAPIC) +SECTION_FUNC(TEXT, _bluetooth_uart_stub) + call _IntEnt /* Inform kernel interrupt has begun */ + pushl $0 /* Push dummy parameter */ + call bt_uart_isr /* Call actual interrupt handler */ + call _ioapic_eoi /* Inform the PIC interrupt is done */ + addl $4, %esp /* Clean-up stack from push above */ + jmp _IntExit /* Inform kernel interrupt is done */ +#endif /* CONFIG_PIC */ +#endif /* CONFIG_BLUETOOTH_UART */ + #if defined(CONFIG_CONSOLE_HANDLER) #if defined(CONFIG_PIC) diff --git a/arch/x86/generic_pc/board.h b/arch/x86/generic_pc/board.h index b9f8a09063a..eafe2db0753 100644 --- a/arch/x86/generic_pc/board.h +++ b/arch/x86/generic_pc/board.h @@ -131,6 +131,14 @@ the 'generic_pc' BSP. #define CONFIG_UART_CONSOLE_IRQ COM1_INT_LVL #define CONFIG_UART_CONSOLE_INT_PRI COM1_INT_PRI +/* Bluetooth UART definitions */ +#define CONFIG_BLUETOOTH_UART_INDEX 1 +#define CONFIG_BLUETOOTH_UART_REGS COM2_BASE_ADRS +#define CONFIG_BLUETOOTH_UART_IRQ COM2_INT_LVL +#define CONFIG_BLUETOOTH_UART_INT_PRI COM2_INT_PRI +#define CONFIG_BLUETOOTH_UART_FREQ UART_XTAL_FREQ +#define CONFIG_BLUETOOTH_UART_BAUDRATE CONFIG_UART_BAUDRATE + /* * Programmable interval timer (PIT) device information (Intel i8253) * diff --git a/arch/x86/generic_pc/defs.objs b/arch/x86/generic_pc/defs.objs index 6f6e8a3b751..a3a9e25a549 100644 --- a/arch/x86/generic_pc/defs.objs +++ b/arch/x86/generic_pc/defs.objs @@ -68,6 +68,9 @@ bsp_drivers_SRC_PIT_y = drivers/timer/i8253.c bsp_drivers_SRC_LOAPIC_TIMER_y = arch/${vARCH}/timer/loApicTimer.c bsp_drivers_SRC_HPET_y = arch/${vARCH}/timer/hpet.c +# Bluetooth drivers +bsp_drivers_SRC_BLUETOOTH_UART_y = drivers/bluetooth/uart.c + # interrupt controllers # variable indicates PCI initialization bsp_PIC_INIT = ${CONFIG_PIC}${CONFIG_SHUTOFF_PIC} @@ -91,6 +94,7 @@ bsp_drivers_SRC += $(strip \ ${bsp_drivers_SRC_LOAPIC_${CONFIG_LOAPIC}} \ ${bsp_drivers_SRC_IOAPIC_${CONFIG_IOAPIC}} \ ${bsp_drivers_SRC_PIC_${bsp_PIC_INIT}} \ + ${bsp_drivers_SRC_BLUETOOTH_UART_${CONFIG_BLUETOOTH_UART}} \ ) # BSP specific section diff --git a/arch/x86/generic_pc/system.c b/arch/x86/generic_pc/system.c index 3bd09b46e61..7217f841b64 100644 --- a/arch/x86/generic_pc/system.c +++ b/arch/x86/generic_pc/system.c @@ -148,16 +148,25 @@ static void consoleInit(void) #endif /* DO_CONSOLE_INIT */ #if defined(CONFIG_BLUETOOTH) - +#if defined(CONFIG_BLUETOOTH_UART) +#include +/* Interrupt handling */ +extern void *_bluetooth_uart_stub; +SYS_INT_REGISTER(_bluetooth_uart_stub, + CONFIG_BLUETOOTH_UART_IRQ, + CONFIG_BLUETOOTH_UART_INT_PRI); +#endif /* CONFIG_BLUETOOTH_UART */ static void bluetooth_init(void) { +#if defined(CONFIG_BLUETOOTH_UART) + bt_uart_init(); +#endif } - #else #define bluetooth_init() \ do {/* nothing */ \ } while ((0)) -#endif /* BLUETOOTH */ +#endif /* CONFIG_BLUETOOTH */ /******************************************************************************* * diff --git a/drivers/bluetooth/uart.c b/drivers/bluetooth/uart.c new file mode 100644 index 00000000000..9afb320ece9 --- /dev/null +++ b/drivers/bluetooth/uart.c @@ -0,0 +1,279 @@ +/* uart.c - UART based Bluetooth driver */ + +/* + * 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 + +#define H4_HEADER_SIZE 1 + +#define H4_CMD 0x01 +#define H4_ACL 0x02 +#define H4_SCO 0x03 +#define H4_EVT 0x04 + +#define UART CONFIG_BLUETOOTH_UART_INDEX + +static int bt_uart_read(int uart, uint8_t *buf, size_t len) +{ + int total = 0; + + while (len) { + int rx; + + rx = uart_fifo_read(uart, buf, len); + if (rx == 0) { + BT_DBG("Got zero bytes from UART\n"); + continue; + } + + BT_DBG("read %d remaining %d\n", rx, len - rx); + len -= rx; + total += rx; + buf += rx; + } + + return total; +} + +static int bt_uart_evt_recv(struct bt_buf *buf) +{ + struct bt_hci_evt_hdr *hdr; + int read; + + hdr = (void *)bt_buf_add(buf, sizeof(*hdr)); + + read = bt_uart_read(UART, (void *)hdr, sizeof(*hdr)); + if (read != sizeof(*hdr)) { + BT_ERR("Cannot read event header\n"); + return -EIO; + } + + BT_DBG("len %u\n", hdr->len); + + return hdr->len; +} + +static int bt_uart_acl_recv(struct bt_buf *buf) +{ + struct bt_hci_acl_hdr *hdr; + uint16_t len; + int read; + + hdr = (void *)bt_buf_add(buf, sizeof(*hdr)); + + read = bt_uart_read(UART, (void *)hdr, sizeof(*hdr)); + if (read != sizeof(*hdr)) { + BT_ERR("Cannot read ACL header\n"); + return -EIO; + } + + len = sys_le16_to_cpu(hdr->len); + + BT_DBG("len %u\n", len); + + return len; +} + +void bt_uart_isr(void *unused) +{ + static struct bt_buf *buf; + static int remaining; + + ARG_UNUSED(unused); + + while (uart_irq_update(UART) && uart_irq_is_pending(UART)) { + int read; + + if (!uart_irq_rx_ready(UART)) { + if (uart_irq_tx_ready(UART)) + BT_DBG("transmit ready\n"); + else + BT_DBG("spurious interrupt\n"); + continue; + } + + /* Character(s) have been received */ + if (!buf) { + uint8_t type; + + /* Get packet type */ + read = bt_uart_read(UART, &type, sizeof(type)); + if (read != sizeof(type)) { + BT_ERR("Error reading UART\n"); + return; + } + + buf = bt_buf_get(); + if (!buf) { + BT_ERR("Cannot get free buffer\n"); + return; + } + + switch (type) { + case H4_EVT: + buf->type = BT_EVT; + remaining = bt_uart_evt_recv(buf); + break; + case H4_ACL: + buf->type = BT_ACL; + remaining = bt_uart_acl_recv(buf); + break; + default: + BT_ERR("Unknown H4 type %u\n", type); + goto failed; + } + + if (remaining < 0) { + BT_ERR("Corrupted data received\n"); + goto failed; + } + + if (remaining > bt_buf_tailroom(buf)) { + BT_ERR("Not enough space in buffer\n"); + goto failed; + } + + BT_DBG("need to get %u bytes\n", remaining); + } + + read = bt_uart_read(UART, bt_buf_tail(buf), remaining); + if (read < 0) { + BT_ERR("Error reading UART\n"); + goto failed; + } + + buf->len += read; + remaining -= read; + + BT_DBG("received %d bytes\n", read); + + if (!remaining) { + BT_DBG("full packet received\n"); + + /* Pass buffer to the stack */ + bt_recv(buf); + buf = NULL; + } + } + + return; + +failed: + bt_buf_put(buf); + remaining = 0; + buf = NULL; +} + +static int bt_uart_send(struct bt_buf *buf) +{ + uint8_t *type; + + if (bt_buf_headroom(buf) < H4_HEADER_SIZE) { + BT_ERR("Not enough headroom in buffer\n"); + return -EINVAL; + } + + type = bt_buf_push(buf, 1); + + switch (buf->type) { + case BT_CMD: + *type = H4_CMD; + break; + case BT_ACL: + *type = H4_ACL; + break; + case BT_EVT: + *type = H4_EVT; + break; + default: + BT_ERR("Unknown buf type %u\n", buf->type); + return -EINVAL; + } + + return uart_fifo_fill(UART, buf->data, buf->len); +} + +static void bt_uart_setup(int uart, struct uart_init_info *info) +{ + BT_DBG("\n"); + + uart_init(uart, info); + + uart_irq_rx_disable(uart); + uart_irq_tx_disable(uart); + uart_int_connect(uart, bt_uart_isr, NULL, NULL); + + /* Drain the fifo */ + while (uart_irq_rx_ready(uart)) { + unsigned char c; + uart_fifo_read(uart, &c, 1); + } + + uart_irq_rx_enable(uart); +} + +static int bt_uart_open() +{ + struct uart_init_info info = { + .options = 0, + .sys_clk_freq = CONFIG_BLUETOOTH_UART_FREQ, + .baud_rate = CONFIG_BLUETOOTH_UART_BAUDRATE, + .regs = CONFIG_BLUETOOTH_UART_REGS, + .irq = CONFIG_BLUETOOTH_UART_IRQ, + .int_pri = CONFIG_BLUETOOTH_UART_INT_PRI, + }; + + bt_uart_setup(CONFIG_BLUETOOTH_UART_INDEX, &info); + + return 0; +} + +static struct bt_driver drv = { + .head_reserve = H4_HEADER_SIZE, + .open = bt_uart_open, + .send = bt_uart_send, +}; + +void bt_uart_init(void) +{ + bt_driver_register(&drv); +} diff --git a/drivers/bluetooth/uart.h b/drivers/bluetooth/uart.h new file mode 100644 index 00000000000..59ffd5f64fb --- /dev/null +++ b/drivers/bluetooth/uart.h @@ -0,0 +1,34 @@ +/* uart.h - HCI UART Bluetooth driver header */ + +/* + * 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. + */ + +void bt_uart_init(void); +void bt_uart_isr(void *); diff --git a/make/target/kconfig/modules/bluetooth.kconf b/make/target/kconfig/modules/bluetooth.kconf index ae3cc5bf53b..8340f59dc99 100644 --- a/make/target/kconfig/modules/bluetooth.kconf +++ b/make/target/kconfig/modules/bluetooth.kconf @@ -40,3 +40,12 @@ config BLUETOOTH default n help This option enables Bluetooth Low Energy support. + +config BLUETOOTH_UART + bool + prompt "Bluetooth UART driver" + depends on BLUETOOTH + select UART_INTERRUPT_DRIVEN + default n + help + Enable Bluetooth UART driver.