You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by je...@apache.org on 2022/08/24 09:33:42 UTC
[mynewt-nimble] branch master updated: nimble/transport: Add CDC transport
This is an automated email from the ASF dual-hosted git repository.
jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
The following commit(s) were added to refs/heads/master by this push:
new df2ed42d nimble/transport: Add CDC transport
df2ed42d is described below
commit df2ed42d23b5a18cf46f8a8054a330332dca9fdf
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Thu Aug 18 15:31:38 2022 +0200
nimble/transport: Add CDC transport
This adds implementation of TinyUSB CDC class that
that will transmit HCI protocol over USB/CDC class
---
nimble/transport/cdc/pkg.yml | 34 +++
nimble/transport/cdc/src/cdc_hci.c | 432 +++++++++++++++++++++++++++++++++++++
nimble/transport/cdc/syscfg.yml | 27 +++
nimble/transport/pkg.yml | 2 +
nimble/transport/syscfg.yml | 1 +
5 files changed, 496 insertions(+)
diff --git a/nimble/transport/cdc/pkg.yml b/nimble/transport/cdc/pkg.yml
new file mode 100644
index 00000000..3620ee14
--- /dev/null
+++ b/nimble/transport/cdc/pkg.yml
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+pkg.name: nimble/transport/cdc
+pkg.description: HCI H4 transport over CDC
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "https://mynewt.apache.org/"
+
+pkg.deps:
+ - "@apache-mynewt-core/hw/hal"
+ - "@apache-mynewt-core/kernel/os"
+ - nimble
+ - nimble/transport/common/hci_h4
+ - "@apache-mynewt-core/hw/usb/tinyusb"
+ - "@apache-mynewt-core/hw/usb/tinyusb/cdc"
+
+pkg.apis:
+ - ble_transport
diff --git a/nimble/transport/cdc/src/cdc_hci.c b/nimble/transport/cdc/src/cdc_hci.c
new file mode 100644
index 00000000..08c8f984
--- /dev/null
+++ b/nimble/transport/cdc/src/cdc_hci.c
@@ -0,0 +1,432 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sysinit/sysinit.h>
+#include <syscfg/syscfg.h>
+#include <os/os_mbuf.h>
+#include <os/os_mempool.h>
+#include <nimble/transport.h>
+#include <nimble/transport/hci_h4.h>
+#include <class/cdc/cdc_device.h>
+#include <device/usbd_pvt.h>
+#include <cdc/cdc.h>
+#include <nimble/hci_common.h>
+
+#define CDC_HCI_LOG_LEVEL 2
+
+/* State machine for assembling packets from HOST (commands and ALCs) */
+static struct hci_h4_sm cdc_hci_h4sm;
+
+static const struct cdc_callbacks cdc_hci_callback;
+
+struct usb_in_packet;
+struct usb_in_queue {
+ STAILQ_HEAD(, usb_in_packet) queue;
+};
+
+static struct os_mempool usb_in_packet_pool;
+
+#define USB_IN_PACKET_COUNT (MYNEWT_VAL(BLE_TRANSPORT_ACL_FROM_LL_COUNT) + \
+ MYNEWT_VAL(BLE_TRANSPORT_EVT_COUNT) + \
+ MYNEWT_VAL(BLE_TRANSPORT_EVT_DISCARDABLE_COUNT) + 1)
+
+typedef struct cdc_hci_itf {
+ /* CDC Interface */
+ cdc_itf_t cdc_itf;
+ /* ACL or Evnet packet that is currently transferred over USB */
+ struct usb_in_packet *current_in_packet;
+ int current_in_packet_offset;
+ /* ACL and Event packets that are waiting to be transmitted */
+ struct usb_in_queue usb_in_queue;
+ uint8_t rx_buffer[USBD_CDC_DATA_EP_SIZE];
+} cdc_hci_itf_t;
+
+static cdc_hci_itf_t cdc_hci_itf = {
+ .cdc_itf.callbacks = &cdc_hci_callback,
+ .usb_in_queue = {STAILQ_HEAD_INITIALIZER(cdc_hci_itf.usb_in_queue.queue)},
+};
+
+struct usb_packet_ops {
+ void (*packet_free)(struct usb_in_packet *packet);
+ int (*packet_write)(struct usb_in_packet *packet, size_t offset);
+ int (*packet_size)(struct usb_in_packet *packet);
+};
+
+struct usb_in_packet {
+ STAILQ_ENTRY(usb_in_packet) next;
+ const struct usb_packet_ops *ops;
+ void *data;
+};
+
+static uint8_t usb_in_packet_pool_mem[OS_MEMPOOL_BYTES(USB_IN_PACKET_COUNT, sizeof(struct usb_in_packet))];
+
+static void
+cdc_hci_rx_cb(cdc_itf_t *itf)
+{
+ int ret;
+ uint8_t cdc_num = itf->cdc_num;
+ uint32_t available = tud_cdc_n_available(cdc_num);
+ uint32_t received = 0;
+ int consumed = 0;
+
+ while ((available > 0) || (received > consumed)) {
+ if (consumed == received) {
+ consumed = 0;
+ received = tud_cdc_n_read(cdc_num, cdc_hci_itf.rx_buffer, min(available, sizeof(cdc_hci_itf.rx_buffer)));
+ available = tud_cdc_n_available(cdc_num);
+ }
+ ret = hci_h4_sm_rx(&cdc_hci_h4sm, cdc_hci_itf.rx_buffer + consumed, received - consumed);
+ if (ret < 0) {
+ tud_cdc_n_read_flush(cdc_num);
+ break;
+ }
+ consumed += ret;
+ }
+}
+
+static void
+usb_in_packet_free(struct usb_in_packet *pkt)
+{
+ if (pkt) {
+ pkt->ops->packet_free(pkt);
+ os_memblock_put(&usb_in_packet_pool, pkt);
+ }
+}
+
+static int
+usb_in_packet_write(struct usb_in_packet *pkt, int offset)
+{
+ return pkt->ops->packet_write(pkt, offset);
+}
+
+static int
+usb_in_packet_size(struct usb_in_packet *pkt)
+{
+ if (pkt) {
+ return pkt->ops->packet_size(pkt);
+ } else {
+ return 0;
+ }
+}
+
+static void
+cdc_hci_send_next_in_packet(void)
+{
+ int sr;
+ struct usb_in_packet *last_packet;
+
+ if (cdc_hci_itf.current_in_packet == NULL ||
+ cdc_hci_itf.current_in_packet_offset == usb_in_packet_size(cdc_hci_itf.current_in_packet)) {
+ OS_ENTER_CRITICAL(sr);
+ last_packet = cdc_hci_itf.current_in_packet;
+ cdc_hci_itf.current_in_packet_offset = 0;
+ cdc_hci_itf.current_in_packet = STAILQ_FIRST(&cdc_hci_itf.usb_in_queue.queue);
+ if (cdc_hci_itf.current_in_packet) {
+ STAILQ_REMOVE_HEAD(&cdc_hci_itf.usb_in_queue.queue, next);
+ }
+ OS_EXIT_CRITICAL(sr);
+ usb_in_packet_free(last_packet);
+ }
+ if (cdc_hci_itf.current_in_packet != NULL &&
+ cdc_hci_itf.current_in_packet_offset < usb_in_packet_size(cdc_hci_itf.current_in_packet)) {
+ cdc_hci_itf.current_in_packet_offset += usb_in_packet_write(cdc_hci_itf.current_in_packet,
+ cdc_hci_itf.current_in_packet_offset);
+ }
+}
+
+static void
+cdc_hci_tx_complete_cb(cdc_itf_t *itf)
+{
+ (void)itf;
+
+ cdc_hci_send_next_in_packet();
+}
+
+static void
+cdc_hci_line_state_cb(cdc_itf_t *itf, bool dtr, bool rts)
+{
+ (void)itf;
+ (void)rts;
+
+ if (dtr) {
+ cdc_hci_send_next_in_packet();
+ }
+}
+
+static struct usb_in_packet *
+cdc_hci_get_usb_in_packet(void)
+{
+ struct usb_in_packet *packet = (struct usb_in_packet *)os_memblock_get(&usb_in_packet_pool);
+ if (packet) {
+ packet->data = NULL;
+ }
+ return packet;
+}
+
+static void
+cdc_hci_send_next_in_packet_from_usbd_task(void *dummy)
+{
+ (void)dummy;
+ cdc_hci_send_next_in_packet();
+}
+
+static void
+cdc_hci_usb_in_enqueue_packet(struct usb_in_packet *pkt)
+{
+ int sr;
+
+ /* Add to IN queue */
+ OS_ENTER_CRITICAL(sr);
+ STAILQ_INSERT_TAIL(&cdc_hci_itf.usb_in_queue.queue, pkt, next);
+ OS_EXIT_CRITICAL(sr);
+
+ usbd_defer_func(cdc_hci_send_next_in_packet_from_usbd_task, NULL, true);
+}
+
+
+/*
+ * Returns packet size as seen over USB
+ */
+static int
+cdc_hci_event_packet_size(struct usb_in_packet *packet)
+{
+ struct ble_hci_ev *event = packet->data;
+ return event->length + 2 /* opcode + length */ + 1 /* H4 header */;
+}
+
+/*
+ * Free Event packet that BLE stack provided to USB stack.
+ * Function is called once all data was sent over USB IN endpoint.
+ */
+static void
+cdc_hci_event_packet_free(struct usb_in_packet *packet)
+{
+ ble_transport_free(packet->data);
+}
+
+/*
+ * Write Event packed from BLE stack to USB IN endpoint.
+ * Flush will be called separately.
+ */
+static int
+cdc_hci_event_packet_write(struct usb_in_packet *packet, size_t offset)
+{
+ uint8_t cdc_num = cdc_hci_itf.cdc_itf.cdc_num;
+ const uint8_t *buf = ((const uint8_t *)packet->data) - 1;
+ const struct ble_hci_ev *hci_ev = packet->data;
+ size_t h4_event_size = hci_ev->length + sizeof(struct ble_hci_ev) + 1;
+ size_t new_offset = offset;
+
+ /* Write H4 Event type */
+ if (new_offset == 0) {
+ new_offset = tud_cdc_n_write_char(cdc_num, HCI_H4_EVT);
+ }
+ /* If first byte was not written rest of the event has to wait as well */
+ if (new_offset > 0) {
+ new_offset += tud_cdc_n_write(cdc_num, buf + new_offset, h4_event_size - new_offset);
+ tud_cdc_n_write_flush(cdc_num);
+ }
+
+ return (int)(new_offset - offset);
+}
+
+/*
+ * USB IN endpoint packet handling functions for Events
+ */
+static const struct usb_packet_ops event_packet_ops = {
+ .packet_free = cdc_hci_event_packet_free,
+ .packet_write = cdc_hci_event_packet_write,
+ .packet_size = cdc_hci_event_packet_size,
+};
+
+/*
+ * Returns packet size as seen over USB
+ */
+static int
+cdc_hci_acl_packet_size(struct usb_in_packet *packet)
+{
+ struct os_mbuf *om = (struct os_mbuf *)packet->data;
+ /* Data size of mbuf plus one for H4 header (2) */
+ return os_mbuf_len(om) + 1;
+}
+
+/*
+ * Free ACL data packet that BLE stack provided to USB stack.
+ * Function is called once all data was sent over USB IN endpoint.
+ */
+static void
+cdc_hci_acl_packet_free(struct usb_in_packet *packet)
+{
+ struct os_mbuf *om = (struct os_mbuf *)packet->data;
+
+ os_mbuf_free_chain(om);
+}
+
+/*
+ * Write ACL packed from BLE stack to USB IN endpoint.
+ * Code traverses mbuf to send all data.
+ * Flush will be called separately.
+ */
+static int
+cdc_hci_acl_packet_write(struct usb_in_packet *packet, size_t offset)
+{
+ uint8_t cdc_num = cdc_hci_itf.cdc_itf.cdc_num;
+ struct os_mbuf *om = (struct os_mbuf *)packet->data;
+ struct os_mbuf *mb;
+ size_t new_offset = offset;
+ uint16_t mbuf_offset;
+ size_t write_size;
+ size_t written;
+
+ if (om) {
+ if (new_offset == 0) {
+ /* Write H4 ACL type */
+ new_offset += tud_cdc_n_write_char(cdc_num, HCI_H4_ACL);
+ }
+
+ for (;;) {
+ mb = os_mbuf_off(om, (int)new_offset - 1, &mbuf_offset);
+ assert(mb);
+ /* mbuf_offset is == om_len when new_offset reached end of mbuf data */
+ if (mb->om_len == mbuf_offset) {
+ break;
+ }
+ /* Chunk in current mbuf */
+ write_size = mb->om_len - mbuf_offset;
+ written = tud_cdc_n_write(cdc_num, mb->om_data + mbuf_offset, write_size);
+ new_offset += written;
+ /* USB FIFO did not have enough space for whole mbuf, stop write for now */
+ if (written < write_size) {
+ break;
+ }
+ }
+ tud_cdc_n_write_flush(cdc_num);
+ }
+
+ return (int)(new_offset - offset);
+}
+
+/*
+ * USB IN endpoint packet handling functions for ACL data
+ */
+static const struct usb_packet_ops cdc_hci_acl_packet_ops = {
+ .packet_free = cdc_hci_acl_packet_free,
+ .packet_write = cdc_hci_acl_packet_write,
+ .packet_size = cdc_hci_acl_packet_size,
+};
+
+static int
+cdc_hci_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;
+}
+
+/*
+ * BLE stack callback with Event to be dispatched to USB IN endpoint.
+ */
+int
+ble_transport_to_hs_evt_impl(void *buf)
+{
+ struct usb_in_packet *pkt;
+
+ assert(buf != NULL);
+
+ pkt = cdc_hci_get_usb_in_packet();
+ if (pkt == NULL) {
+ ble_transport_free(buf);
+ return BLE_ERR_MEM_CAPACITY;
+ }
+
+ pkt->data = buf;
+ pkt->ops = &event_packet_ops;
+ cdc_hci_usb_in_enqueue_packet(pkt);
+
+ return 0;
+}
+
+/*
+ * BLE stack callback with ACL data to be dispatched to USB IN endpoint.
+ */
+int
+ble_transport_to_hs_acl_impl(struct os_mbuf *om)
+{
+ struct usb_in_packet *pkt;
+
+ /* If this packet is zero length, just free it */
+ if (OS_MBUF_PKTLEN(om) == 0) {
+ os_mbuf_free_chain(om);
+ return 0;
+ }
+
+ pkt = cdc_hci_get_usb_in_packet();
+ if (pkt == NULL) {
+ assert(0);
+ return -ENOMEM;
+ }
+
+ pkt->data = om;
+ pkt->ops = &cdc_hci_acl_packet_ops;
+ cdc_hci_usb_in_enqueue_packet(pkt);
+
+ return 0;
+}
+
+void
+ble_transport_hs_init(void)
+{
+ int rc;
+
+ SYSINIT_ASSERT_ACTIVE();
+
+ rc = os_mempool_init(&usb_in_packet_pool,
+ USB_IN_PACKET_COUNT,
+ sizeof(struct usb_in_packet),
+ usb_in_packet_pool_mem,
+ "usb_in_packet_pool");
+
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ cdc_itf_add(&cdc_hci_itf.cdc_itf);
+
+ hci_h4_sm_init(&cdc_hci_h4sm, &hci_h4_allocs_from_hs, cdc_hci_frame_cb);
+}
+
+static const struct cdc_callbacks cdc_hci_callback = {
+ .cdc_rx_cb = cdc_hci_rx_cb,
+ .cdc_line_coding_cb = NULL,
+ .cdc_line_state_cb = cdc_hci_line_state_cb,
+ .cdc_rx_wanted_cb = NULL,
+ .cdc_send_break_cb = NULL,
+ .cdc_tx_complete_cb = cdc_hci_tx_complete_cb,
+};
+
diff --git a/nimble/transport/cdc/syscfg.yml b/nimble/transport/cdc/syscfg.yml
new file mode 100644
index 00000000..03366250
--- /dev/null
+++ b/nimble/transport/cdc/syscfg.yml
@@ -0,0 +1,27 @@
+# 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.
+#
+
+syscfg.defs:
+ USBD_CDC_HCI_DESCRIPTOR_STRING:
+ description: String for CDC/HCI interface
+ value:
+
+ USBD_CDC_HCI:
+ description: Constant value indicating package usage
+ value: 1
+
diff --git a/nimble/transport/pkg.yml b/nimble/transport/pkg.yml
index 03924ced..5070c728 100644
--- a/nimble/transport/pkg.yml
+++ b/nimble/transport/pkg.yml
@@ -44,6 +44,8 @@ pkg.deps.'BLE_TRANSPORT_HS == "uart"':
- nimble/transport/uart
pkg.deps.'BLE_TRANSPORT_HS == "usb"':
- nimble/transport/usb
+pkg.deps.'BLE_TRANSPORT_HS == "cdc"':
+ - nimble/transport/cdc
pkg.deps.'BLE_TRANSPORT_LL == "apollo3"':
- nimble/transport/apollo3
diff --git a/nimble/transport/syscfg.yml b/nimble/transport/syscfg.yml
index e69b0ef6..a4ae6d56 100644
--- a/nimble/transport/syscfg.yml
+++ b/nimble/transport/syscfg.yml
@@ -31,6 +31,7 @@ syscfg.defs:
- nrf5340
- uart
- usb
+ - cdc
- custom
BLE_TRANSPORT_LL:
description: >