diff --git a/drivers/bluetooth/hci/Kconfig b/drivers/bluetooth/hci/Kconfig index 0a42284c4c8..7f346813a97 100644 --- a/drivers/bluetooth/hci/Kconfig +++ b/drivers/bluetooth/hci/Kconfig @@ -27,6 +27,8 @@ config BT_H5 bool "H:5 UART [EXPERIMENTAL]" select BT_UART select EXPERIMENTAL + default y + depends on DT_HAS_ZEPHYR_BT_HCI_3WIRE_UART_ENABLED help Bluetooth three-wire (H:5) UART driver. Implementation of HCI Three-Wire UART Transport Layer. diff --git a/drivers/bluetooth/hci/h5.c b/drivers/bluetooth/hci/h5.c index 25a3b3e28f6..0be22f61f88 100644 --- a/drivers/bluetooth/hci/h5.c +++ b/drivers/bluetooth/hci/h5.c @@ -17,11 +17,13 @@ #include #include #include +#include +#include #include #include #include -#include +#include #include "../util.h" @@ -29,14 +31,7 @@ #include LOG_MODULE_REGISTER(bt_driver); -static K_KERNEL_STACK_DEFINE(tx_stack, CONFIG_BT_DRV_TX_STACK_SIZE); -static K_KERNEL_STACK_DEFINE(rx_stack, CONFIG_BT_DRV_RX_STACK_SIZE); - -static struct k_thread tx_thread_data; -static struct k_thread rx_thread_data; - -static struct k_work_delayable ack_work; -static struct k_work_delayable retx_work; +#define DT_DRV_COMPAT zephyr_bt_hci_3wire_uart #define HCI_3WIRE_ACK_PKT 0x00 #define HCI_COMMAND_PKT 0x01 @@ -86,13 +81,21 @@ static bool reliable_packet(uint8_t type) #define H5_SET_LEN(hdr, len) (((hdr)[1] |= ((len) & 0x0f) << 4), \ ((hdr)[2] |= (len) >> 4)) -static struct h5 { +struct h5_data { + /* Needed for delayed work callbacks */ + const struct device *dev; + + bt_hci_recv_t recv; + struct net_buf *rx_buf; struct k_fifo tx_queue; struct k_fifo rx_queue; struct k_fifo unack_queue; + struct k_work_delayable ack_work; + struct k_work_delayable retx_work; + uint8_t tx_win; uint8_t tx_ack; uint8_t tx_seq; @@ -111,9 +114,21 @@ static struct h5 { PAYLOAD, END, } rx_state; -} h5; -static uint8_t unack_queue_len; + uint8_t unack_queue_len; +}; + +struct h5_config { + const struct device *uart; + + k_thread_stack_t *rx_stack; + size_t rx_stack_size; + struct k_thread *rx_thread; + + k_thread_stack_t *tx_stack; + size_t tx_stack_size; + struct k_thread *tx_thread; +}; static const uint8_t sync_req[] = { 0x01, 0x7e }; static const uint8_t sync_rsp[] = { 0x02, 0x7d }; @@ -123,23 +138,21 @@ static const uint8_t conf_rsp[] = { 0x04, 0x7b }; /* H5 signal buffers pool */ #define MAX_SIG_LEN 3 -#define SIGNAL_COUNT 2 +#define SIGNAL_COUNT (2 * DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)) #define SIG_BUF_SIZE (BT_BUF_RESERVE + MAX_SIG_LEN) NET_BUF_POOL_DEFINE(h5_pool, SIGNAL_COUNT, SIG_BUF_SIZE, 0, NULL); -static const struct device *const h5_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_uart)); - -static void h5_reset_rx(void) +static void h5_reset_rx(struct h5_data *h5) { - if (h5.rx_buf) { - net_buf_unref(h5.rx_buf); - h5.rx_buf = NULL; + if (h5->rx_buf) { + net_buf_unref(h5->rx_buf); + h5->rx_buf = NULL; } - h5.rx_state = START; + h5->rx_state = START; } -static int h5_unslip_byte(uint8_t *byte) +static int h5_unslip_byte(const struct device *uart, uint8_t *byte) { int count; @@ -148,7 +161,7 @@ static int h5_unslip_byte(uint8_t *byte) } do { - count = uart_fifo_read(h5_dev, byte, sizeof(*byte)); + count = uart_fifo_read(uart, byte, sizeof(*byte)); } while (!count); switch (*byte) { @@ -166,20 +179,20 @@ static int h5_unslip_byte(uint8_t *byte) return 0; } -static void process_unack(void) +static void process_unack(struct h5_data *h5) { - uint8_t next_seq = h5.tx_seq; - uint8_t number_removed = unack_queue_len; + uint8_t next_seq = h5->tx_seq; + uint8_t number_removed = h5->unack_queue_len; - if (!unack_queue_len) { + if (!h5->unack_queue_len) { return; } - LOG_DBG("rx_ack %u tx_ack %u tx_seq %u unack_queue_len %u", h5.rx_ack, h5.tx_ack, h5.tx_seq, - unack_queue_len); + LOG_DBG("rx_ack %u tx_ack %u tx_seq %u unack_queue_len %u", h5->rx_ack, h5->tx_ack, + h5->tx_seq, h5->unack_queue_len); - while (unack_queue_len > 0) { - if (next_seq == h5.rx_ack) { + while (h5->unack_queue_len > 0) { + if (next_seq == h5->rx_ack) { /* Next sequence number is the same as last received * ack number */ @@ -191,15 +204,15 @@ static void process_unack(void) next_seq = (next_seq - 1) & 0x07; } - if (next_seq != h5.rx_ack) { - LOG_ERR("Wrong sequence: rx_ack %u tx_seq %u next_seq %u", h5.rx_ack, h5.tx_seq, - next_seq); + if (next_seq != h5->rx_ack) { + LOG_ERR("Wrong sequence: rx_ack %u tx_seq %u next_seq %u", h5->rx_ack, + h5->tx_seq, next_seq); } LOG_DBG("Need to remove %u packet from the queue", number_removed); while (number_removed) { - struct net_buf *buf = net_buf_get(&h5.unack_queue, K_NO_WAIT); + struct net_buf *buf = net_buf_get(&h5->unack_queue, K_NO_WAIT); if (!buf) { LOG_ERR("Unack queue is empty"); @@ -210,7 +223,7 @@ static void process_unack(void) LOG_DBG("Remove buf from the unack_queue"); net_buf_unref(buf); - unack_queue_len--; + h5->unack_queue_len--; number_removed--; } } @@ -261,25 +274,27 @@ static void hexdump(const char *str, const uint8_t *packet, size_t length) #define hexdump(str, packet, length) #endif -static uint8_t h5_slip_byte(uint8_t byte) +static uint8_t h5_slip_byte(const struct device *uart, uint8_t byte) { switch (byte) { case SLIP_DELIMITER: - uart_poll_out(h5_dev, SLIP_ESC); - uart_poll_out(h5_dev, SLIP_ESC_DELIM); + uart_poll_out(uart, SLIP_ESC); + uart_poll_out(uart, SLIP_ESC_DELIM); return 2; case SLIP_ESC: - uart_poll_out(h5_dev, SLIP_ESC); - uart_poll_out(h5_dev, SLIP_ESC_ESC); + uart_poll_out(uart, SLIP_ESC); + uart_poll_out(uart, SLIP_ESC_ESC); return 2; default: - uart_poll_out(h5_dev, byte); + uart_poll_out(uart, byte); return 1; } } -static void h5_send(const uint8_t *payload, uint8_t type, int len) +static void h5_send(const struct device *dev, const uint8_t *payload, uint8_t type, int len) { + const struct h5_config *cfg = dev->config; + struct h5_data *h5 = dev->data; uint8_t hdr[4]; int i; @@ -288,14 +303,14 @@ static void h5_send(const uint8_t *payload, uint8_t type, int len) (void)memset(hdr, 0, sizeof(hdr)); /* Set ACK for outgoing packet and stop delayed work */ - H5_SET_ACK(hdr, h5.tx_ack); + H5_SET_ACK(hdr, h5->tx_ack); /* If cancel fails we may ack the same seq number twice, this is OK. */ - (void)k_work_cancel_delayable(&ack_work); + (void)k_work_cancel_delayable(&h5->ack_work); if (reliable_packet(type)) { H5_SET_RELIABLE(hdr); - H5_SET_SEQ(hdr, h5.tx_seq); - h5.tx_seq = (h5.tx_seq + 1) % 8; + H5_SET_SEQ(hdr, h5->tx_seq); + h5->tx_seq = (h5->tx_seq + 1) % 8; } H5_SET_TYPE(hdr, type); @@ -306,97 +321,100 @@ static void h5_send(const uint8_t *payload, uint8_t type, int len) h5_print_header(hdr, "TX: <"); - uart_poll_out(h5_dev, SLIP_DELIMITER); + uart_poll_out(cfg->uart, SLIP_DELIMITER); for (i = 0; i < 4; i++) { - h5_slip_byte(hdr[i]); + h5_slip_byte(cfg->uart, hdr[i]); } for (i = 0; i < len; i++) { - h5_slip_byte(payload[i]); + h5_slip_byte(cfg->uart, payload[i]); } - uart_poll_out(h5_dev, SLIP_DELIMITER); + uart_poll_out(cfg->uart, SLIP_DELIMITER); } /* Delayed work taking care about retransmitting packets */ static void retx_timeout(struct k_work *work) { - ARG_UNUSED(work); + struct k_work_delayable *delayable = k_work_delayable_from_work(work); + struct h5_data *h5 = CONTAINER_OF(delayable, struct h5_data, retx_work); - LOG_DBG("unack_queue_len %u", unack_queue_len); + LOG_DBG("unack_queue_len %u", h5->unack_queue_len); - if (unack_queue_len) { + if (h5->unack_queue_len) { struct k_fifo tmp_queue; struct net_buf *buf; k_fifo_init(&tmp_queue); /* Queue to temporary queue */ - while ((buf = net_buf_get(&h5.tx_queue, K_NO_WAIT))) { + while ((buf = net_buf_get(&h5->tx_queue, K_NO_WAIT))) { net_buf_put(&tmp_queue, buf); } /* Queue unack packets to the beginning of the queue */ - while ((buf = net_buf_get(&h5.unack_queue, K_NO_WAIT))) { + while ((buf = net_buf_get(&h5->unack_queue, K_NO_WAIT))) { /* include also packet type */ net_buf_push(buf, sizeof(uint8_t)); - net_buf_put(&h5.tx_queue, buf); - h5.tx_seq = (h5.tx_seq - 1) & 0x07; - unack_queue_len--; + net_buf_put(&h5->tx_queue, buf); + h5->tx_seq = (h5->tx_seq - 1) & 0x07; + h5->unack_queue_len--; } /* Queue saved packets from temp queue */ while ((buf = net_buf_get(&tmp_queue, K_NO_WAIT))) { - net_buf_put(&h5.tx_queue, buf); + net_buf_put(&h5->tx_queue, buf); } } } static void ack_timeout(struct k_work *work) { - ARG_UNUSED(work); + struct k_work_delayable *delayable = k_work_delayable_from_work(work); + struct h5_data *h5 = CONTAINER_OF(delayable, struct h5_data, ack_work); LOG_DBG(""); - h5_send(NULL, HCI_3WIRE_ACK_PKT, 0); + h5_send(h5->dev, NULL, HCI_3WIRE_ACK_PKT, 0); } -static void h5_process_complete_packet(uint8_t *hdr) +static void h5_process_complete_packet(const struct device *dev, uint8_t *hdr) { + struct h5_data *h5 = dev->data; struct net_buf *buf; LOG_DBG(""); /* rx_ack should be in every packet */ - h5.rx_ack = H5_HDR_ACK(hdr); + h5->rx_ack = H5_HDR_ACK(hdr); if (reliable_packet(H5_HDR_PKT_TYPE(hdr))) { /* For reliable packet increment next transmit ack number */ - h5.tx_ack = (h5.tx_ack + 1) % 8; + h5->tx_ack = (h5->tx_ack + 1) % 8; /* Submit delayed work to ack the packet */ - k_work_reschedule(&ack_work, H5_RX_ACK_TIMEOUT); + k_work_reschedule(&h5->ack_work, H5_RX_ACK_TIMEOUT); } h5_print_header(hdr, "RX: >"); - process_unack(); + process_unack(h5); - buf = h5.rx_buf; - h5.rx_buf = NULL; + buf = h5->rx_buf; + h5->rx_buf = NULL; switch (H5_HDR_PKT_TYPE(hdr)) { case HCI_3WIRE_ACK_PKT: net_buf_unref(buf); break; case HCI_3WIRE_LINK_PKT: - net_buf_put(&h5.rx_queue, buf); + net_buf_put(&h5->rx_queue, buf); break; case HCI_EVENT_PKT: case HCI_ACLDATA_PKT: case HCI_ISODATA_PKT: hexdump("=> ", buf->data, buf->len); - bt_recv(buf); + h5->recv(dev, buf); break; } } @@ -406,22 +424,21 @@ static inline struct net_buf *get_evt_buf(uint8_t evt) return bt_buf_get_evt(evt, false, K_NO_WAIT); } -static void bt_uart_isr(const struct device *unused, void *user_data) +static void bt_uart_isr(const struct device *uart, void *user_data) { + const struct device *dev = user_data; + struct h5_data *h5 = dev->data; static int remaining; uint8_t byte; int ret; static uint8_t hdr[4]; size_t buf_tailroom; - ARG_UNUSED(unused); - ARG_UNUSED(user_data); + while (uart_irq_update(uart) && + uart_irq_is_pending(uart)) { - while (uart_irq_update(h5_dev) && - uart_irq_is_pending(h5_dev)) { - - if (!uart_irq_rx_ready(h5_dev)) { - if (uart_irq_tx_ready(h5_dev)) { + if (!uart_irq_rx_ready(uart)) { + if (uart_irq_tx_ready(uart)) { LOG_DBG("transmit ready"); } else { LOG_DBG("spurious interrupt"); @@ -430,15 +447,15 @@ static void bt_uart_isr(const struct device *unused, void *user_data) break; } - ret = uart_fifo_read(h5_dev, &byte, sizeof(byte)); + ret = uart_fifo_read(uart, &byte, sizeof(byte)); if (!ret) { continue; } - switch (h5.rx_state) { + switch (h5->rx_state) { case START: if (byte == SLIP_DELIMITER) { - h5.rx_state = HEADER; + h5->rx_state = HEADER; remaining = sizeof(hdr); } break; @@ -451,8 +468,8 @@ static void bt_uart_isr(const struct device *unused, void *user_data) continue; } - if (h5_unslip_byte(&byte) < 0) { - h5_reset_rx(); + if (h5_unslip_byte(uart, &byte) < 0) { + h5_reset_rx(h5); continue; } @@ -470,86 +487,85 @@ static void bt_uart_isr(const struct device *unused, void *user_data) /* The buffer is allocated only once we know * the exact event type. */ - h5.rx_state = PAYLOAD; + h5->rx_state = PAYLOAD; break; case HCI_ACLDATA_PKT: - h5.rx_buf = bt_buf_get_rx(BT_BUF_ACL_IN, - K_NO_WAIT); - if (!h5.rx_buf) { + h5->rx_buf = bt_buf_get_rx(BT_BUF_ACL_IN, + K_NO_WAIT); + if (!h5->rx_buf) { LOG_WRN("No available data buffers"); - h5_reset_rx(); + h5_reset_rx(h5); continue; } - h5.rx_state = PAYLOAD; + h5->rx_state = PAYLOAD; break; case HCI_ISODATA_PKT: - h5.rx_buf = bt_buf_get_rx(BT_BUF_ISO_IN, - K_NO_WAIT); - if (!h5.rx_buf) { + h5->rx_buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_NO_WAIT); + if (!h5->rx_buf) { LOG_WRN("No available data buffers"); - h5_reset_rx(); + h5_reset_rx(h5); continue; } - h5.rx_state = PAYLOAD; + h5->rx_state = PAYLOAD; break; case HCI_3WIRE_LINK_PKT: case HCI_3WIRE_ACK_PKT: - h5.rx_buf = net_buf_alloc(&h5_pool, K_NO_WAIT); - if (!h5.rx_buf) { + h5->rx_buf = net_buf_alloc(&h5_pool, K_NO_WAIT); + if (!h5->rx_buf) { LOG_WRN("No available signal buffers"); - h5_reset_rx(); + h5_reset_rx(h5); continue; } - h5.rx_state = PAYLOAD; + h5->rx_state = PAYLOAD; break; default: LOG_ERR("Wrong packet type %u", H5_HDR_PKT_TYPE(hdr)); - h5.rx_state = END; + h5->rx_state = END; break; } if (!remaining) { - h5.rx_state = END; + h5->rx_state = END; } break; case PAYLOAD: - if (h5_unslip_byte(&byte) < 0) { - h5_reset_rx(); + if (h5_unslip_byte(uart, &byte) < 0) { + h5_reset_rx(h5); continue; } /* Allocate HCI event buffer now that we know the * exact event type. */ - if (!h5.rx_buf) { - h5.rx_buf = get_evt_buf(byte); - if (!h5.rx_buf) { + if (!h5->rx_buf) { + h5->rx_buf = get_evt_buf(byte); + if (!h5->rx_buf) { LOG_WRN("No available event buffers"); - h5_reset_rx(); + h5_reset_rx(h5); continue; } } - buf_tailroom = net_buf_tailroom(h5.rx_buf); + buf_tailroom = net_buf_tailroom(h5->rx_buf); if (buf_tailroom < sizeof(byte)) { LOG_ERR("Not enough space in buffer %zu/%zu", sizeof(byte), buf_tailroom); - h5_reset_rx(); + h5_reset_rx(h5); break; } - net_buf_add_mem(h5.rx_buf, &byte, sizeof(byte)); + net_buf_add_mem(h5->rx_buf, &byte, sizeof(byte)); remaining--; if (!remaining) { - h5.rx_state = END; + h5->rx_state = END; } break; case END: if (byte != SLIP_DELIMITER) { LOG_ERR("Missing ending SLIP_DELIMITER"); - h5_reset_rx(); + h5_reset_rx(h5); break; } @@ -560,15 +576,15 @@ static void bt_uart_isr(const struct device *unused, void *user_data) * full packet anyway to clear UART. */ if (H5_HDR_RELIABLE(hdr) && - H5_HDR_SEQ(hdr) != h5.tx_ack) { - LOG_ERR("Seq expected %u got %u. Drop packet", h5.tx_ack, + H5_HDR_SEQ(hdr) != h5->tx_ack) { + LOG_ERR("Seq expected %u got %u. Drop packet", h5->tx_ack, H5_HDR_SEQ(hdr)); - h5_reset_rx(); + h5_reset_rx(h5); break; } - h5_process_complete_packet(hdr); - h5.rx_state = START; + h5_process_complete_packet(dev, hdr); + h5->rx_state = START; break; } } @@ -579,8 +595,9 @@ static uint8_t h5_get_type(struct net_buf *buf) return net_buf_pull_u8(buf); } -static int h5_queue(struct net_buf *buf) +static int h5_queue(const struct device *dev, struct net_buf *buf) { + struct h5_data *h5 = dev->data; uint8_t type; LOG_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len); @@ -602,29 +619,31 @@ static int h5_queue(struct net_buf *buf) memcpy(net_buf_push(buf, sizeof(type)), &type, sizeof(type)); - net_buf_put(&h5.tx_queue, buf); + net_buf_put(&h5->tx_queue, buf); return 0; } static void tx_thread(void *p1, void *p2, void *p3) { - ARG_UNUSED(p1); + const struct device *dev = p1; + struct h5_data *h5 = dev->data; + ARG_UNUSED(p2); ARG_UNUSED(p3); LOG_DBG(""); /* FIXME: make periodic sending */ - h5_send(sync_req, HCI_3WIRE_LINK_PKT, sizeof(sync_req)); + h5_send(dev, sync_req, HCI_3WIRE_LINK_PKT, sizeof(sync_req)); while (true) { struct net_buf *buf; uint8_t type; - LOG_DBG("link_state %u", h5.link_state); + LOG_DBG("link_state %u", h5->link_state); - switch (h5.link_state) { + switch (h5->link_state) { case UNINIT: /* FIXME: send sync */ k_sleep(K_MSEC(100)); @@ -634,32 +653,34 @@ static void tx_thread(void *p1, void *p2, void *p3) k_sleep(K_MSEC(100)); break; case ACTIVE: - buf = net_buf_get(&h5.tx_queue, K_FOREVER); + buf = net_buf_get(&h5->tx_queue, K_FOREVER); type = h5_get_type(buf); - h5_send(buf->data, type, buf->len); + h5_send(dev, buf->data, type, buf->len); /* buf is dequeued from tx_queue and queued to unack * queue. */ - net_buf_put(&h5.unack_queue, buf); - unack_queue_len++; + net_buf_put(&h5->unack_queue, buf); + h5->unack_queue_len++; - k_work_reschedule(&retx_work, H5_TX_ACK_TIMEOUT); + k_work_reschedule(&h5->retx_work, H5_TX_ACK_TIMEOUT); break; } } } -static void h5_set_txwin(uint8_t *conf) +static void h5_set_txwin(struct h5_data *h5, uint8_t *conf) { - conf[2] = h5.tx_win & 0x07; + conf[2] = h5->tx_win & 0x07; } static void rx_thread(void *p1, void *p2, void *p3) { - ARG_UNUSED(p1); + const struct device *dev = p1; + struct h5_data *h5 = dev->data; + ARG_UNUSED(p2); ARG_UNUSED(p3); @@ -668,42 +689,42 @@ static void rx_thread(void *p1, void *p2, void *p3) while (true) { struct net_buf *buf; - buf = net_buf_get(&h5.rx_queue, K_FOREVER); + buf = net_buf_get(&h5->rx_queue, K_FOREVER); hexdump("=> ", buf->data, buf->len); if (!memcmp(buf->data, sync_req, sizeof(sync_req))) { - if (h5.link_state == ACTIVE) { + if (h5->link_state == ACTIVE) { /* TODO Reset H5 */ } - h5_send(sync_rsp, HCI_3WIRE_LINK_PKT, sizeof(sync_rsp)); + h5_send(dev, sync_rsp, HCI_3WIRE_LINK_PKT, sizeof(sync_rsp)); } else if (!memcmp(buf->data, sync_rsp, sizeof(sync_rsp))) { - if (h5.link_state == ACTIVE) { + if (h5->link_state == ACTIVE) { /* TODO Reset H5 */ } - h5.link_state = INIT; - h5_set_txwin(conf_req); - h5_send(conf_req, HCI_3WIRE_LINK_PKT, sizeof(conf_req)); + h5->link_state = INIT; + h5_set_txwin(h5, conf_req); + h5_send(dev, conf_req, HCI_3WIRE_LINK_PKT, sizeof(conf_req)); } else if (!memcmp(buf->data, conf_req, 2)) { /* * The Host sends Config Response messages without a * Configuration Field. */ - h5_send(conf_rsp, HCI_3WIRE_LINK_PKT, sizeof(conf_rsp)); + h5_send(dev, conf_rsp, HCI_3WIRE_LINK_PKT, sizeof(conf_rsp)); /* Then send Config Request with Configuration Field */ - h5_set_txwin(conf_req); - h5_send(conf_req, HCI_3WIRE_LINK_PKT, sizeof(conf_req)); + h5_set_txwin(h5, conf_req); + h5_send(dev, conf_req, HCI_3WIRE_LINK_PKT, sizeof(conf_req)); } else if (!memcmp(buf->data, conf_rsp, 2)) { - h5.link_state = ACTIVE; + h5->link_state = ACTIVE; if (buf->len > 2) { /* Configuration field present */ - h5.tx_win = (buf->data[2] & 0x07); + h5->tx_win = (buf->data[2] & 0x07); } - LOG_DBG("Finished H5 configuration, tx_win %u", h5.tx_win); + LOG_DBG("Finished H5 configuration, tx_win %u", h5->tx_win); } else { LOG_ERR("Not handled yet %x %x", buf->data[0], buf->data[1]); } @@ -717,74 +738,91 @@ static void rx_thread(void *p1, void *p2, void *p3) } } -static void h5_init(void) +static void h5_init(const struct device *dev) { + const struct h5_config *cfg = dev->config; + struct h5_data *h5 = dev->data; + k_tid_t tid; + LOG_DBG(""); - h5.link_state = UNINIT; - h5.rx_state = START; - h5.tx_win = 4U; + h5->link_state = UNINIT; + h5->rx_state = START; + h5->tx_win = 4U; /* TX thread */ - k_fifo_init(&h5.tx_queue); - k_thread_create(&tx_thread_data, tx_stack, - K_KERNEL_STACK_SIZEOF(tx_stack), - tx_thread, NULL, NULL, NULL, - K_PRIO_COOP(CONFIG_BT_HCI_TX_PRIO), - 0, K_NO_WAIT); - k_thread_name_set(&tx_thread_data, "tx_thread"); + k_fifo_init(&h5->tx_queue); + tid = k_thread_create(cfg->tx_thread, cfg->tx_stack, cfg->tx_stack_size, + tx_thread, (void *)dev, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_HCI_TX_PRIO), + 0, K_NO_WAIT); + k_thread_name_set(tid, "tx_thread"); - k_fifo_init(&h5.rx_queue); - k_thread_create(&rx_thread_data, rx_stack, - K_KERNEL_STACK_SIZEOF(rx_stack), - rx_thread, NULL, NULL, NULL, - K_PRIO_COOP(CONFIG_BT_RX_PRIO), - 0, K_NO_WAIT); - k_thread_name_set(&rx_thread_data, "rx_thread"); + k_fifo_init(&h5->rx_queue); + tid = k_thread_create(cfg->rx_thread, cfg->rx_stack, cfg->rx_stack_size, + rx_thread, (void *)dev, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_RX_PRIO), + 0, K_NO_WAIT); + k_thread_name_set(tid, "rx_thread"); /* Unack queue */ - k_fifo_init(&h5.unack_queue); + k_fifo_init(&h5->unack_queue); /* Init delayed work */ - k_work_init_delayable(&ack_work, ack_timeout); - k_work_init_delayable(&retx_work, retx_timeout); + k_work_init_delayable(&h5->ack_work, ack_timeout); + k_work_init_delayable(&h5->retx_work, retx_timeout); } -static int h5_open(void) +static int h5_open(const struct device *dev, bt_hci_recv_t recv) { + const struct h5_config *cfg = dev->config; + struct h5_data *h5 = dev->data; + LOG_DBG(""); - uart_irq_rx_disable(h5_dev); - uart_irq_tx_disable(h5_dev); + /* This is needed so that we can access the device struct from within the + * delayed work callbacks. + */ + h5->dev = dev; - bt_uart_drain(h5_dev); + h5->recv = recv; - uart_irq_callback_set(h5_dev, bt_uart_isr); + uart_irq_rx_disable(cfg->uart); + uart_irq_tx_disable(cfg->uart); - h5_init(); + bt_uart_drain(cfg->uart); - uart_irq_rx_enable(h5_dev); + uart_irq_callback_user_data_set(cfg->uart, bt_uart_isr, (void *)dev); + + h5_init(dev); + + uart_irq_rx_enable(cfg->uart); return 0; } -static const struct bt_hci_driver drv = { - .name = "H:5", - .bus = BT_HCI_DRIVER_BUS_UART, - .open = h5_open, - .send = h5_queue, +static const struct bt_hci_driver_api h5_driver_api = { + .open = h5_open, + .send = h5_queue, }; -static int bt_uart_init(void) -{ +#define BT_UART_DEVICE_INIT(inst) \ + static K_KERNEL_STACK_DEFINE(rx_thread_stack_##inst, CONFIG_BT_DRV_RX_STACK_SIZE); \ + static struct k_thread rx_thread_##inst; \ + static K_KERNEL_STACK_DEFINE(tx_thread_stack_##inst, CONFIG_BT_DRV_TX_STACK_SIZE); \ + static struct k_thread tx_thread_##inst; \ + static const struct h5_config h5_config_##inst = { \ + .uart = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + .rx_stack = rx_thread_stack_##inst, \ + .rx_stack_size = K_KERNEL_STACK_SIZEOF(rx_thread_stack_##inst), \ + .rx_thread = &rx_thread_##inst, \ + .tx_stack = tx_thread_stack_##inst, \ + .tx_stack_size = K_KERNEL_STACK_SIZEOF(tx_thread_stack_##inst), \ + .tx_thread = &tx_thread_##inst, \ + }; \ + static struct h5_data h5_##inst; \ + DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &h5_##inst, &h5_config_##inst, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &h5_driver_api) - if (!device_is_ready(h5_dev)) { - return -ENODEV; - } - bt_hci_driver_register(&drv); - - return 0; -} - -SYS_INIT(bt_uart_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); +DT_INST_FOREACH_STATUS_OKAY(BT_UART_DEVICE_INIT) diff --git a/dts/bindings/bluetooth/zephyr,bt-hci-3wire-uart.yaml b/dts/bindings/bluetooth/zephyr,bt-hci-3wire-uart.yaml new file mode 100644 index 00000000000..d2a5f62c213 --- /dev/null +++ b/dts/bindings/bluetooth/zephyr,bt-hci-3wire-uart.yaml @@ -0,0 +1,13 @@ +# UART Bluetooth HCI device + +description: Bluetooth HCI behind a 3-Wire UART device (H:5) + +compatible: "zephyr,bt-hci-3wire-uart" + +include: bt-hci.yaml + +properties: + bt-hci-name: + default: "H:5" + bt-hci-bus: + default: "BT_HCI_BUS_UART" diff --git a/tests/bluetooth/init/h5.overlay b/tests/bluetooth/init/h5.overlay new file mode 100644 index 00000000000..8c49815cb09 --- /dev/null +++ b/tests/bluetooth/init/h5.overlay @@ -0,0 +1,16 @@ +/ { + chosen { + zephyr,bt-hci = &bt_hci_3wire_uart; + }; +}; + +&bt_hci_uart { + status = "disabled"; +}; + +&uart2 { + bt_hci_3wire_uart: bt_hci_3wire_uart { + compatible = "zephyr,bt-hci-3wire-uart"; + status = "okay"; + }; +}; diff --git a/tests/bluetooth/init/testcase.yaml b/tests/bluetooth/init/testcase.yaml index 63267a96c8e..69afaaf8b08 100644 --- a/tests/bluetooth/init/testcase.yaml +++ b/tests/bluetooth/init/testcase.yaml @@ -326,10 +326,14 @@ tests: - nrf52840dk/nrf52840 - nrf51dk/nrf51822 bluetooth.init.test_h5: - extra_args: CONF_FILE=prj_h5.conf + extra_args: + - CONF_FILE=prj_h5.conf + - DTC_OVERLAY_FILE=h5.overlay platform_allow: qemu_cortex_m3 bluetooth.init.test_h5_dbg: - extra_args: CONF_FILE=prj_h5_dbg.conf + extra_args: + - CONF_FILE=prj_h5_dbg.conf + - DTC_OVERLAY_FILE=h5.overlay platform_allow: qemu_cortex_m3 bluetooth.init.test_llcp: extra_args: CONF_FILE=prj_llcp.conf