blob: 613c9ce153305540a68516b7670c41cbc4b83d65 [file] [log] [blame]
/*
* 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/transport.h>
#include <nimble/transport/hci_ipc.h>
volatile struct hci_ipc_shm *g_ipc_shm;
static void
hci_ipc_alloc(struct hci_ipc_sm *sm)
{
assert(sm->hdr.type);
assert(sm->buf == NULL);
switch (sm->hdr.type) {
#if MYNEWT_VAL(BLE_CONTROLLER)
case HCI_IPC_TYPE_CMD:
sm->buf = ble_transport_alloc_cmd();
break;
#endif
case HCI_IPC_TYPE_ACL:
#if MYNEWT_VAL(BLE_CONTROLLER)
sm->om = ble_transport_alloc_acl_from_hs();
#else
sm->om = ble_transport_alloc_acl_from_ll();
#endif
break;
#if !MYNEWT_VAL(BLE_CONTROLLER)
case HCI_IPC_TYPE_EVT:
sm->buf = ble_transport_alloc_evt(0);
break;
case HCI_IPC_TYPE_EVT_DISCARDABLE:
sm->buf = ble_transport_alloc_evt(1);
break;
case HCI_IPC_TYPE_EVT_IN_CMD:
sm->buf = ble_transport_alloc_cmd();
break;
#endif
case HCI_IPC_TYPE_ISO:
#if MYNEWT_VAL(BLE_CONTROLLER)
sm->om = ble_transport_alloc_iso_from_hs();
#else
sm->om = ble_transport_alloc_iso_from_ll();
#endif
break;
default:
assert(0);
break;
}
assert(sm->buf);
sm->rem_len = sm->hdr.length;
sm->buf_len = 0;
}
static bool
hci_ipc_has_hdr(struct hci_ipc_sm *sm)
{
return sm->hdr_len == sizeof(sm->hdr);
}
static void
hci_ipc_frame(struct hci_ipc_sm *sm)
{
assert(sm->hdr.type);
assert(sm->buf);
assert(sm->rem_len == 0);
switch (sm->hdr.type) {
#if MYNEWT_VAL(BLE_CONTROLLER)
case HCI_IPC_TYPE_CMD:
ble_transport_to_ll_cmd(sm->buf);
break;
#endif
case HCI_IPC_TYPE_ACL:
#if MYNEWT_VAL(BLE_CONTROLLER)
ble_transport_to_ll_acl(sm->om);
#else
ble_transport_to_hs_acl(sm->om);
#endif
break;
#if !MYNEWT_VAL(BLE_CONTROLLER)
case HCI_IPC_TYPE_EVT:
case HCI_IPC_TYPE_EVT_DISCARDABLE:
case HCI_IPC_TYPE_EVT_IN_CMD:
ble_transport_to_hs_evt(sm->buf);
break;
#endif
case HCI_IPC_TYPE_ISO:
#if MYNEWT_VAL(BLE_CONTROLLER)
ble_transport_to_ll_iso(sm->om);
#else
ble_transport_to_hs_iso(sm->om);
#endif
break;
default:
assert(0);
break;
}
sm->hdr.type = 0;
sm->hdr.length = 0;
sm->hdr_len = 0;
sm->buf_len = 0;
sm->rem_len = 0;
sm->buf = NULL;
}
static uint16_t
hci_ipc_copy_to_hdr(struct hci_ipc_sm *sm, const uint8_t *buf, uint16_t len)
{
uint16_t rem_hdr_len;
uint8_t *p;
if (hci_ipc_has_hdr(sm)) {
return 0;
}
rem_hdr_len = sizeof(sm->hdr) - sm->hdr_len;
len = min(len, rem_hdr_len);
p = (void *)&sm->hdr;
memcpy(p + sm->hdr_len, buf, len);
sm->hdr_len += len;
if (hci_ipc_has_hdr(sm)) {
hci_ipc_alloc(sm);
}
return len;
}
static uint16_t
hci_ipc_copy_to_buf(struct hci_ipc_sm *sm, const uint8_t *buf, uint16_t len)
{
int rc;
assert(sm->hdr.type);
assert(sm->buf);
len = min(len, sm->rem_len);
switch (sm->hdr.type) {
#if MYNEWT_VAL(BLE_CONTROLLER)
case HCI_IPC_TYPE_CMD:
#else
case HCI_IPC_TYPE_EVT:
case HCI_IPC_TYPE_EVT_DISCARDABLE:
case HCI_IPC_TYPE_EVT_IN_CMD:
#endif
memcpy(sm->buf + sm->buf_len, buf, len);
break;
case HCI_IPC_TYPE_ACL:
case HCI_IPC_TYPE_ISO:
rc = os_mbuf_append(sm->om, buf, len);
assert(rc == 0);
break;
default:
assert(0);
break;
}
sm->rem_len -= len;
sm->buf_len += len;
if (sm->rem_len == 0) {
hci_ipc_frame(sm);
}
return len;
}
int
hci_ipc_rx(struct hci_ipc_sm *sm, const uint8_t *buf, uint16_t len)
{
uint16_t rem_len = len;
uint16_t copy_len;
while (rem_len) {
if (hci_ipc_has_hdr(sm)) {
copy_len = hci_ipc_copy_to_buf(sm, buf, rem_len);
} else {
copy_len = hci_ipc_copy_to_hdr(sm, buf, rem_len);
}
rem_len -= copy_len;
buf += copy_len;
}
return len;
}
void
hci_ipc_init(volatile struct hci_ipc_shm *shm, struct hci_ipc_sm *sm)
{
assert(g_ipc_shm == NULL);
g_ipc_shm = shm;
memset(sm, 0, sizeof(*sm));
#if MYNEWT_VAL(BLE_CONTROLLER)
while (shm->n2a_num_evt_disc == 0) {
/* Wait until app side initializes credits */
}
#else
shm->n2a_num_acl = MYNEWT_VAL(BLE_TRANSPORT_ACL_FROM_LL_COUNT);
shm->n2a_num_evt = MYNEWT_VAL(BLE_TRANSPORT_EVT_COUNT);
shm->n2a_num_evt_disc = MYNEWT_VAL(BLE_TRANSPORT_EVT_DISCARDABLE_COUNT);
#endif
}