You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2022/03/17 10:50:11 UTC

[mynewt-nimble] 01/22: nimble/transport: Add common HCI H4 code

This is an automated email from the ASF dual-hosted git repository.

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git

commit a8e4aeb87e79641a5ea046ba7cf900d1729dae26
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Thu Mar 3 16:10:31 2022 +0100

    nimble/transport: Add common HCI H4 code
    
    This can be shared by UART, CMAC and nRF5340.
---
 .../hci_h4/include/nimble/transport/hci_h4.h       |  68 +++++
 nimble/transport/common/hci_h4/pkg.yml             |  26 ++
 nimble/transport/common/hci_h4/src/hci_h4.c        | 304 +++++++++++++++++++++
 3 files changed, 398 insertions(+)

diff --git a/nimble/transport/common/hci_h4/include/nimble/transport/hci_h4.h b/nimble/transport/common/hci_h4/include/nimble/transport/hci_h4.h
new file mode 100644
index 0000000..139160a
--- /dev/null
+++ b/nimble/transport/common/hci_h4/include/nimble/transport/hci_h4.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef _HCI_H4_H_
+#define _HCI_H4_H_
+
+#include <stdint.h>
+
+#define HCI_H4_NONE      0x00
+#define HCI_H4_CMD       0x01
+#define HCI_H4_ACL       0x02
+#define HCI_H4_EVT       0x04
+#define HCI_H4_ISO       0x05
+
+typedef void *(hci_h4_alloc_cmd)(void);
+typedef void *(hci_h4_alloc_evt)(int);
+typedef struct os_mbuf *(hci_h4_alloc_acl)(void);
+
+struct hci_h4_allocators {
+    hci_h4_alloc_cmd *cmd;
+    hci_h4_alloc_acl *acl;
+    hci_h4_alloc_evt *evt;
+};
+
+extern const struct hci_h4_allocators hci_h4_allocs_from_ll;
+extern const struct hci_h4_allocators hci_h4_allocs_from_hs;
+
+typedef int (hci_h4_frame_cb)(uint8_t pkt_type, void *data);
+
+struct hci_h4_sm {
+    uint8_t state;
+    uint8_t pkt_type;
+    uint8_t min_len;
+    uint16_t len;
+    uint16_t exp_len;
+    uint8_t hdr[4];
+    union {
+        uint8_t *buf;
+        struct os_mbuf *om;
+    };
+
+    const struct hci_h4_allocators *allocs;
+    hci_h4_frame_cb *frame_cb;
+};
+
+void hci_h4_sm_init(struct hci_h4_sm *h4sm,
+                    const struct hci_h4_allocators *allocs,
+                    hci_h4_frame_cb *frame_cb);
+
+int hci_h4_sm_rx(struct hci_h4_sm *h4sm, const uint8_t *buf, uint16_t len);
+
+#endif /* _HCI_H4_H_ */
diff --git a/nimble/transport/common/hci_h4/pkg.yml b/nimble/transport/common/hci_h4/pkg.yml
new file mode 100644
index 0000000..24c4b58
--- /dev/null
+++ b/nimble/transport/common/hci_h4/pkg.yml
@@ -0,0 +1,26 @@
+#
+# 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/common/hci_h4
+pkg.description: HCI H4 protocol
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "https://mynewt.apache.org/"
+
+pkg.deps:
+    - nimble
diff --git a/nimble/transport/common/hci_h4/src/hci_h4.c b/nimble/transport/common/hci_h4/src/hci_h4.c
new file mode 100644
index 0000000..ac76ab0
--- /dev/null
+++ b/nimble/transport/common/hci_h4/src/hci_h4.c
@@ -0,0 +1,304 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <syscfg/syscfg.h>
+#include <os/os.h>
+#include <os/os_mbuf.h>
+#include <nimble/hci_common.h>
+#include <nimble/transport.h>
+#include <nimble/transport/hci_h4.h>
+
+#define HCI_H4_SM_W4_PKT_TYPE   0
+#define HCI_H4_SM_W4_HEADER     1
+#define HCI_H4_SM_W4_PAYLOAD    2
+#define HCI_H4_SM_COMPLETED     3
+
+const struct hci_h4_allocators hci_h4_allocs_from_ll = {
+    .acl = ble_transport_alloc_acl_from_ll,
+    .evt = ble_transport_alloc_evt,
+};
+const struct hci_h4_allocators hci_h4_allocs_from_hs = {
+    .cmd = ble_transport_alloc_cmd,
+    .acl = ble_transport_alloc_acl_from_hs,
+};
+
+struct hci_h4_input_buffer {
+    const uint8_t *buf;
+    uint16_t len;
+};
+
+static void
+hci_h4_frame_start(struct hci_h4_sm *rxs, uint8_t pkt_type)
+{
+    rxs->pkt_type = pkt_type;
+    rxs->len = 0;
+    rxs->exp_len = 0;
+
+    switch (rxs->pkt_type) {
+    case HCI_H4_CMD:
+        rxs->min_len = 3;
+        break;
+    case HCI_H4_ACL:
+        rxs->min_len = 4;
+        break;
+    case HCI_H4_EVT:
+        rxs->min_len = 2;
+        break;
+    default:
+        /* XXX sync loss */
+        assert(0);
+        break;
+    }
+}
+
+static int
+hci_h4_ib_consume(struct hci_h4_input_buffer *ib, uint16_t len)
+{
+    assert(ib->len >= len);
+
+    ib->buf += len;
+    ib->len -= len;
+
+    return len;
+}
+
+static int
+hci_h4_ib_pull_min_len(struct hci_h4_sm *rxs,
+                       struct hci_h4_input_buffer *ib)
+{
+    uint16_t len;
+
+    len = min(ib->len, rxs->min_len - rxs->len);
+    memcpy(&rxs->hdr[rxs->len], ib->buf, len);
+
+    rxs->len += len;
+    hci_h4_ib_consume(ib, len);
+
+    return rxs->len != rxs->min_len;
+}
+
+static int
+hci_h4_sm_w4_header(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib)
+{
+    int rc;
+
+    rc = hci_h4_ib_pull_min_len(h4sm, ib);
+    if (rc) {
+        /* need more data */
+        return 1;
+    }
+
+    switch (h4sm->pkt_type) {
+    case HCI_H4_CMD:
+        assert(h4sm->allocs && h4sm->allocs->cmd);
+        h4sm->buf = h4sm->allocs->cmd();
+        if (!h4sm->buf) {
+            return -1;
+        }
+
+        memcpy(h4sm->buf, h4sm->hdr, h4sm->len);
+        h4sm->exp_len = h4sm->hdr[2] + 3;
+        break;
+    case HCI_H4_ACL:
+        assert(h4sm->allocs && h4sm->allocs->acl);
+        h4sm->om = h4sm->allocs->acl();
+        if (!h4sm->om) {
+            return -1;
+        }
+
+        os_mbuf_append(h4sm->om, h4sm->hdr, h4sm->len);
+        h4sm->exp_len = get_le16(&h4sm->hdr[2]) + 4;
+        break;
+    case HCI_H4_EVT:
+        if (h4sm->hdr[0] == BLE_HCI_EVCODE_LE_META) {
+            /* For LE Meta event we need 3 bytes to parse header */
+            h4sm->min_len = 3;
+            rc = hci_h4_ib_pull_min_len(h4sm, ib);
+            if (rc) {
+                /* need more data */
+                return 1;
+            }
+        }
+
+        /* TODO lo/hi pool? */
+
+        assert(h4sm->allocs && h4sm->allocs->evt);
+        h4sm->buf = h4sm->allocs->evt(0);
+        if (!h4sm->buf) {
+            return -1;
+        }
+
+        memcpy(h4sm->buf, h4sm->hdr, h4sm->len);
+
+        h4sm->exp_len = h4sm->hdr[1] + 2;
+        break;
+    default:
+        assert(0);
+        break;
+    }
+
+    return 0;
+}
+
+static int
+hci_h4_sm_w4_payload(struct hci_h4_sm *h4sm,
+                     struct hci_h4_input_buffer *ib)
+{
+    uint16_t mbuf_len;
+    uint16_t len;
+    int rc;
+
+    len = min(ib->len, h4sm->exp_len - h4sm->len);
+
+    switch (h4sm->pkt_type) {
+    case HCI_H4_CMD:
+    case HCI_H4_EVT:
+        if (h4sm->buf) {
+            memcpy(&h4sm->buf[h4sm->len], ib->buf, len);
+        }
+        break;
+    case HCI_H4_ACL:
+        assert(h4sm->om);
+
+        mbuf_len = OS_MBUF_PKTLEN(h4sm->om);
+        rc = os_mbuf_append(h4sm->om, ib->buf, len);
+        if (rc) {
+            /* Some data may already be appended so need to adjust h4sm only by
+             * the size of appended data.
+             */
+            len = OS_MBUF_PKTLEN(h4sm->om) - mbuf_len;
+            h4sm->len += len;
+            hci_h4_ib_consume(ib, len);
+
+            return -1;
+        }
+        break;
+    default:
+        assert(0);
+        break;
+    }
+
+    h4sm->len += len;
+    hci_h4_ib_consume(ib, len);
+
+    /* return 1 if need more data */
+    return h4sm->len != h4sm->exp_len;
+}
+
+static void
+hci_h4_sm_completed(struct hci_h4_sm *h4sm)
+{
+    int rc;
+
+    switch (h4sm->pkt_type) {
+    case HCI_H4_CMD:
+    case HCI_H4_EVT:
+        if (h4sm->buf) {
+            assert(h4sm->frame_cb);
+            rc = h4sm->frame_cb(h4sm->pkt_type, h4sm->buf);
+            if (rc != 0) {
+                ble_transport_free(h4sm->buf);
+            }
+            h4sm->buf = NULL;
+        }
+        break;
+    case HCI_H4_ACL:
+        if (h4sm->om) {
+            assert(h4sm->frame_cb);
+            rc = h4sm->frame_cb(h4sm->pkt_type, h4sm->om);
+            if (rc != 0) {
+                os_mbuf_free_chain(h4sm->om);
+            }
+            h4sm->om = NULL;
+        }
+        break;
+    default:
+        assert(0);
+        break;
+    }
+}
+
+int
+hci_h4_sm_rx(struct hci_h4_sm *h4sm, const uint8_t *buf, uint16_t len)
+{
+    struct hci_h4_input_buffer ib = {
+        .buf = buf,
+        .len = len,
+    };
+
+    int rc = 0;
+    while (ib.len && (rc >= 0)) {
+        rc = 0;
+        switch (h4sm->state) {
+        case HCI_H4_SM_W4_PKT_TYPE:
+            hci_h4_frame_start(h4sm, ib.buf[0]);
+            hci_h4_ib_consume(&ib, 1);
+            h4sm->state = HCI_H4_SM_W4_HEADER;
+        /* no break */
+        case HCI_H4_SM_W4_HEADER:
+            rc = hci_h4_sm_w4_header(h4sm, &ib);
+            if (rc) {
+                break;
+            }
+            h4sm->state = HCI_H4_SM_W4_PAYLOAD;
+        /* no break */
+        case HCI_H4_SM_W4_PAYLOAD:
+            rc = hci_h4_sm_w4_payload(h4sm, &ib);
+            if (rc) {
+                break;
+            }
+            h4sm->state = HCI_H4_SM_COMPLETED;
+        /* no break */
+        case HCI_H4_SM_COMPLETED:
+            hci_h4_sm_completed(h4sm);
+            h4sm->state = HCI_H4_SM_W4_PKT_TYPE;
+            break;
+        default:
+            assert(0);
+            break;
+        }
+    }
+
+    /* Calculate consumed bytes
+     *
+     * Note: we should always consume some bytes unless there is an oom error.
+     * It's also possible that we have an oom error but already consumed some
+     * data, in such case just return success and error will be returned on next
+     * pass.
+     */
+    len = len - ib.len;
+    if (len == 0) {
+        assert(rc < 0);
+        return -1;
+    }
+
+    return len;
+}
+
+void
+hci_h4_sm_init(struct hci_h4_sm *h4sm, const struct hci_h4_allocators *allocs,
+               hci_h4_frame_cb *frame_cb)
+{
+    memset(h4sm, 0, sizeof(*h4sm));
+    h4sm->allocs = allocs;
+    h4sm->frame_cb = frame_cb;
+}