nimble/transport: Add common HCI H4 code

This can be shared by UART, CMAC and nRF5340.
This commit is contained in:
Andrzej Kaczmarek
2022-03-17 11:48:16 +01:00
parent d185f62755
commit a8e4aeb87e
3 changed files with 398 additions and 0 deletions
@@ -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_ */
+26
View File
@@ -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 <[email protected]>"
pkg.homepage: "https://mynewt.apache.org/"
pkg.deps:
- nimble
+304
View File
@@ -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;
}