/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include #include #include #include #include "os/os_mbuf.h" #include "os/os_mempool.h" #include "hal/hal_uart.h" #include "nimble/transport.h" #include "nimble/transport/hci_h4.h" #define TX_Q_SIZE (MYNEWT_VAL(BLE_TRANSPORT_ACL_FROM_LL_COUNT) + \ MYNEWT_VAL(BLE_TRANSPORT_EVT_COUNT) + \ MYNEWT_VAL(BLE_TRANSPORT_EVT_DISCARDABLE_COUNT)) struct hci_uart_tx { uint8_t type; uint8_t sent_type; uint16_t len; uint16_t idx; struct os_mbuf *om; uint8_t *buf; STAILQ_ENTRY(hci_uart_tx) tx_q_next; }; static STAILQ_HEAD(, hci_uart_tx) tx_q; static struct os_mempool pool_tx_q; static uint8_t pool_tx_q_buf[ OS_MEMPOOL_BYTES(TX_Q_SIZE, sizeof(struct hci_uart_tx)) ]; struct hci_h4_sm hci_uart_h4sm; static int hci_uart_frame_cb(uint8_t pkt_type, void *data) { switch (pkt_type) { case HCI_H4_CMD: return ble_transport_to_ll_cmd(data); case HCI_H4_ACL: return ble_transport_to_ll_acl(data); default: assert(0); break; } return -1; } static int hci_uart_rx_char(void *arg, uint8_t data) { hci_h4_sm_rx(&hci_uart_h4sm, &data, 1); return 0; } static int hci_uart_tx_char(void *arg) { struct hci_uart_tx *tx; uint8_t ch; os_sr_t sr; OS_ENTER_CRITICAL(sr); tx = STAILQ_FIRST(&tx_q); OS_EXIT_CRITICAL(sr); if (!tx) { return -1; } if (!tx->sent_type) { tx->sent_type = 1; return tx->type; } switch (tx->type) { case HCI_H4_EVT: ch = tx->buf[tx->idx]; tx->idx++; if (tx->idx == tx->len) { ble_transport_free(tx->buf); OS_ENTER_CRITICAL(sr); STAILQ_REMOVE_HEAD(&tx_q, tx_q_next); OS_EXIT_CRITICAL(sr); os_memblock_put(&pool_tx_q, tx); } break; case HCI_H4_ACL: os_mbuf_copydata(tx->om, 0, 1, &ch); os_mbuf_adj(tx->om, 1); tx->len--; if (tx->len == 0) { os_mbuf_free_chain(tx->om); OS_ENTER_CRITICAL(sr); STAILQ_REMOVE_HEAD(&tx_q, tx_q_next); OS_EXIT_CRITICAL(sr); os_memblock_put(&pool_tx_q, tx); } break; default: assert(0); OS_ENTER_CRITICAL(sr); STAILQ_REMOVE_HEAD(&tx_q, tx_q_next); OS_EXIT_CRITICAL(sr); os_memblock_put(&pool_tx_q, tx); } return ch; } static int hci_uart_configure(void) { enum hal_uart_parity parity; enum hal_uart_flow_ctl flowctl; int rc; rc = hal_uart_init_cbs(MYNEWT_VAL(BLE_TRANSPORT_UART_PORT), hci_uart_tx_char, NULL, hci_uart_rx_char, NULL); if (rc != 0) { return -1; } if (MYNEWT_VAL_CHOICE(BLE_TRANSPORT_UART_PARITY, odd)) { parity = HAL_UART_PARITY_ODD; } else if (MYNEWT_VAL_CHOICE(BLE_TRANSPORT_UART_PARITY, even)) { parity = HAL_UART_PARITY_EVEN; } else { parity = HAL_UART_PARITY_NONE; } if (MYNEWT_VAL_CHOICE(BLE_TRANSPORT_UART_FLOW_CONTROL, rtscts)) { flowctl = HAL_UART_FLOW_CTL_RTS_CTS; } else { flowctl = HAL_UART_FLOW_CTL_NONE; } rc = hal_uart_config(MYNEWT_VAL(BLE_TRANSPORT_UART_PORT), MYNEWT_VAL(BLE_TRANSPORT_UART_BAUDRATE), MYNEWT_VAL(BLE_TRANSPORT_UART_DATA_BITS), MYNEWT_VAL(BLE_TRANSPORT_UART_STOP_BITS), parity, flowctl); if (rc != 0) { return -1; } return 0; } int ble_transport_to_hs_evt_impl(void *buf) { struct hci_uart_tx *txe; os_sr_t sr; txe = os_memblock_get(&pool_tx_q); if (!txe) { assert(0); return -ENOMEM; } txe->type = HCI_H4_EVT; txe->sent_type = 0; txe->len = 2 + ((uint8_t *)buf)[1]; txe->buf = buf; txe->idx = 0; txe->om = NULL; OS_ENTER_CRITICAL(sr); STAILQ_INSERT_TAIL(&tx_q, txe, tx_q_next); OS_EXIT_CRITICAL(sr); hal_uart_start_tx(MYNEWT_VAL(BLE_TRANSPORT_UART_PORT)); return 0; } int ble_transport_to_hs_acl_impl(struct os_mbuf *om) { struct hci_uart_tx *txe; os_sr_t sr; txe = os_memblock_get(&pool_tx_q); if (!txe) { assert(0); return -ENOMEM; } txe->type = HCI_H4_ACL; txe->sent_type = 0; txe->len = OS_MBUF_PKTLEN(om); txe->idx = 0; txe->buf = NULL; txe->om = om; OS_ENTER_CRITICAL(sr); STAILQ_INSERT_TAIL(&tx_q, txe, tx_q_next); OS_EXIT_CRITICAL(sr); hal_uart_start_tx(MYNEWT_VAL(BLE_TRANSPORT_UART_PORT)); return 0; } void ble_transport_hs_init(void) { int rc; SYSINIT_ASSERT_ACTIVE(); rc = hci_uart_configure(); SYSINIT_PANIC_ASSERT(rc == 0); rc = os_mempool_init(&pool_tx_q, TX_Q_SIZE, sizeof(struct hci_uart_tx), pool_tx_q_buf, "hci_uart_tx_q"); SYSINIT_PANIC_ASSERT(rc == 0); hci_h4_sm_init(&hci_uart_h4sm, &hci_h4_allocs_from_hs, hci_uart_frame_cb); STAILQ_INIT(&tx_q); }