| /* |
| * 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 |
| } |