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;
+}