mirror of
https://github.com/google/pebble.git
synced 2025-04-18 18:54:44 -04:00
fw: drivers: add da1468x bluetooth code
This commit is contained in:
parent
5b5d49cb49
commit
4051c5bb97
203 changed files with 19237 additions and 2 deletions
1
src/bluetooth-fw/da1468x/.gitignore
vendored
Normal file
1
src/bluetooth-fw/da1468x/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
!openocd.cfg
|
52
src/bluetooth-fw/da1468x/common/dialog_utils.c
Normal file
52
src/bluetooth-fw/da1468x/common/dialog_utils.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "dialog_utils.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ble_common.h"
|
||||
|
||||
#include <bluetooth/gap_le_connect.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool dialog_utils_dialog_is_addr_type_random(addr_type_t addr_type) {
|
||||
return (addr_type == PRIVATE_ADDRESS);
|
||||
}
|
||||
|
||||
addr_type_t dialog_utils_local_addr_type_to_dialog(const BTDeviceInternal *address) {
|
||||
return (address->is_random_address) ? PRIVATE_ADDRESS : PUBLIC_ADDRESS;
|
||||
}
|
||||
|
||||
void dialog_utils_bd_address_to_bt_device(const bd_address_t *addr, BTDeviceInternal *device_out) {
|
||||
*device_out = (BTDeviceInternal) {};
|
||||
device_out->is_random_address = dialog_utils_dialog_is_addr_type_random(addr->addr_type);
|
||||
memcpy(&device_out->address, &addr->addr, sizeof(device_out->address));
|
||||
}
|
||||
|
||||
void dialog_utils_bt_device_to_bd_address(const BTDeviceInternal *device, bd_address_t *addr_out) {
|
||||
addr_out->addr_type = dialog_utils_local_addr_type_to_dialog(device);
|
||||
memcpy(addr_out->addr, &device->address, sizeof(addr_out->addr));
|
||||
}
|
||||
|
||||
HciStatusCode ble_error_to_hci_status_error(ble_error_t e) {
|
||||
switch (e) {
|
||||
case BLE_STATUS_OK:
|
||||
return HciStatusCode_Success;
|
||||
default:
|
||||
return (e + HciStatusCode_VS_Base);
|
||||
}
|
||||
}
|
54
src/bluetooth-fw/da1468x/common/hc_endpoint_bonding_sync.c
Normal file
54
src/bluetooth-fw/da1468x/common/hc_endpoint_bonding_sync.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "bonding_sync_impl.h"
|
||||
#include "hc_protocol/hc_endpoint_bonding_sync.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <bluetooth/bonding_sync.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void hc_endpoint_bonding_sync_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_BondingSync_AddBonding:
|
||||
bonding_sync_handle_hc_add((const BleBonding *)msg->payload);
|
||||
break;
|
||||
case HcMessageID_BondingSync_RemoveBonding:
|
||||
bonding_sync_handle_hc_remove((const BleBonding *)msg->payload);
|
||||
break;
|
||||
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Unknown command ID: %"PRIu8, msg->command_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_send_cmd(HcMessageID_BondingSync cmd_id, const BleBonding *bonding) {
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_BondingSync, cmd_id,
|
||||
(const uint8_t *)bonding, sizeof(*bonding));
|
||||
}
|
||||
|
||||
void hc_endpoint_bonding_sync_add(const BleBonding *bonding) {
|
||||
prv_send_cmd(HcMessageID_BondingSync_AddBonding, bonding);
|
||||
}
|
||||
|
||||
void hc_endpoint_bonding_sync_remove(const BleBonding *bonding) {
|
||||
prv_send_cmd(HcMessageID_BondingSync_RemoveBonding, bonding);
|
||||
}
|
506
src/bluetooth-fw/da1468x/common/hc_protocol.c
Normal file
506
src/bluetooth-fw/da1468x/common/hc_protocol.c
Normal file
|
@ -0,0 +1,506 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "core_dump.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
#include "host_transport.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "os/mutex.h"
|
||||
#include "os/tick.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum HcProtocolState {
|
||||
HcProtoclStateNotInitialized = 0,
|
||||
HcProtoclStateIsInitialized,
|
||||
HcProtoclStateDeinitializing,
|
||||
} HcProtocolState;
|
||||
|
||||
#define TIMEOUT_TICKS (configTICK_RATE_HZ / 2)
|
||||
#define NUM_CONSEC_FAILURES 5
|
||||
|
||||
extern const HcProtocolMessageHandler g_hc_protocol_endpoints_table[HcEndpointIDCount];
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
SemaphoreHandle_t semaphore;
|
||||
const HcProtocolMessage *request;
|
||||
HcProtocolMessage *response;
|
||||
} HcExpectation;
|
||||
|
||||
static PebbleRecursiveMutex *s_hc_lock;
|
||||
static HcProtocolState s_hc_state;
|
||||
static HcExpectation *s_expectations_head;
|
||||
static uint8_t s_hc_next_transaction_id;
|
||||
static SemaphoreHandle_t s_retry_semph;
|
||||
|
||||
static volatile uint8_t s_outstanding_enqueues;
|
||||
|
||||
static uint8_t s_num_consec_enqueue_failures;
|
||||
#if !BT_CONTROLLER_BUILD
|
||||
static uint8_t s_num_consec_request_failures;
|
||||
#endif
|
||||
|
||||
static void prv_lock(void) {
|
||||
mutex_lock_recursive(s_hc_lock);
|
||||
}
|
||||
|
||||
static void prv_unlock(void) {
|
||||
mutex_unlock_recursive(s_hc_lock);
|
||||
}
|
||||
|
||||
static void prv_update_hc_state(HcProtocolState state) {
|
||||
prv_lock();
|
||||
s_hc_state = state;
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
//! WARNING: Will log if function is called and state is not initialized.
|
||||
static bool prv_check_initialized(void) {
|
||||
bool is_initialized;
|
||||
HcProtocolState state;
|
||||
prv_lock();
|
||||
{
|
||||
state = s_hc_state;
|
||||
is_initialized = (state == HcProtoclStateIsInitialized);
|
||||
}
|
||||
prv_unlock();
|
||||
if (!is_initialized) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "prv_check_initialized called when hc_protocol is not initialized, "
|
||||
"state: %d", state);
|
||||
}
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
static TickType_t prv_remaining_ticks(TickType_t timeout_ticks, TickType_t start_ticks) {
|
||||
const TickType_t now = xTaskGetTickCount();
|
||||
const TickType_t elapsed = (now - start_ticks);
|
||||
if (timeout_ticks > elapsed) {
|
||||
return timeout_ticks - elapsed;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool prv_still_processing_enqueues(void) {
|
||||
bool done;
|
||||
prv_lock();
|
||||
{
|
||||
done = (s_outstanding_enqueues != 0);
|
||||
}
|
||||
prv_unlock();
|
||||
return done;
|
||||
}
|
||||
|
||||
// Only to be called by prv_enqueue(). This function keeps track of how many
|
||||
// enqueues are outstanding. If an enqueue is about to start but the stack is
|
||||
// no longer up, it does NOT bump the job count as we expect prv_enqueue() to
|
||||
// bail immediately
|
||||
static bool prv_update_enqueue_count(bool start) {
|
||||
bool initialized;
|
||||
prv_lock();
|
||||
{
|
||||
initialized = prv_check_initialized();
|
||||
if (initialized || !start) {
|
||||
s_outstanding_enqueues += (start) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
return initialized;
|
||||
}
|
||||
|
||||
static bool prv_enqueue(HcProtocolMessage *message) {
|
||||
PBL_ASSERTN(message->message_length >= sizeof(HcProtocolMessage));
|
||||
|
||||
if (!prv_update_enqueue_count(true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
// Retry for 500ms
|
||||
TickType_t start_ticks = xTaskGetTickCount();
|
||||
|
||||
while (1) {
|
||||
HostTransportEnqueueStatus status =
|
||||
host_transport_tx_enqueue((const uint8_t *)message, message->message_length);
|
||||
|
||||
if (status != HostTransportEnqueueStatus_RetryLater) {
|
||||
success = (status == HostTransportEnqueueStatus_Success);
|
||||
goto done;
|
||||
}
|
||||
|
||||
TickType_t remaining_ticks = prv_remaining_ticks(TIMEOUT_TICKS, start_ticks);
|
||||
|
||||
bool is_timeout;
|
||||
if (remaining_ticks == 0) {
|
||||
is_timeout = true;
|
||||
} else {
|
||||
is_timeout = (xSemaphoreTake(s_retry_semph, remaining_ticks) == pdFALSE);
|
||||
}
|
||||
|
||||
bool initialized = prv_check_initialized();
|
||||
if (is_timeout || !initialized) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Failed to enqueue HC request (endpoint:%d, command:%"PRIu8")",
|
||||
message->endpoint_id, message->command_id);
|
||||
s_num_consec_enqueue_failures++;
|
||||
if (initialized && (s_num_consec_enqueue_failures >= NUM_CONSEC_FAILURES)) {
|
||||
core_dump_and_reset_or_reboot();
|
||||
}
|
||||
success = false;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
s_num_consec_enqueue_failures = 0;
|
||||
|
||||
done:
|
||||
prv_update_enqueue_count(false /* stopping */);
|
||||
return success;
|
||||
}
|
||||
|
||||
static void prv_assign_next_transaction_id(HcProtocolMessage *message) {
|
||||
prv_lock();
|
||||
message->transaction_id.sn = s_hc_next_transaction_id;
|
||||
const uint8_t max_sn = ((1 << HC_PROTOCOL_SN_BIT_WIDTH) - 1);
|
||||
++s_hc_next_transaction_id;
|
||||
s_hc_next_transaction_id %= max_sn;
|
||||
prv_unlock();
|
||||
|
||||
message->transaction_id.is_response = false;
|
||||
}
|
||||
|
||||
bool hc_protocol_enqueue(HcProtocolMessage *message) {
|
||||
if (!prv_check_initialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prv_assign_next_transaction_id(message);
|
||||
return prv_enqueue(message);
|
||||
}
|
||||
|
||||
static HcProtocolMessage *prv_create_message(HcEndpointID endpoint_id, HcCommandID command_id,
|
||||
const uint8_t *request_payload,
|
||||
size_t request_payload_length) {
|
||||
size_t message_length = sizeof(HcProtocolMessage) + request_payload_length;
|
||||
HcProtocolMessage *message = (HcProtocolMessage *)kernel_zalloc_check(message_length);
|
||||
message->message_length = message_length;
|
||||
message->endpoint_id = endpoint_id;
|
||||
message->command_id = command_id;
|
||||
if (request_payload_length) {
|
||||
memcpy(message->payload, request_payload, request_payload_length);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
bool hc_protocol_enqueue_with_payload(HcEndpointID endpoint_id, HcCommandID command_id,
|
||||
const uint8_t *request_payload,
|
||||
size_t request_payload_length) {
|
||||
if (!prv_check_initialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HcProtocolMessage *message = prv_create_message(endpoint_id, command_id, request_payload,
|
||||
request_payload_length);
|
||||
const bool rv = hc_protocol_enqueue(message);
|
||||
kernel_free(message);
|
||||
return rv;
|
||||
}
|
||||
|
||||
#if !BT_CONTROLLER_BUILD
|
||||
static HcExpectation *prv_set_expectation_for_request(const HcProtocolMessage *request_message) {
|
||||
SemaphoreHandle_t semaphore = xSemaphoreCreateBinary();
|
||||
PBL_ASSERTN(semaphore);
|
||||
|
||||
HcExpectation *expectation = kernel_malloc_check(sizeof(HcExpectation));
|
||||
*expectation = (HcExpectation) {
|
||||
.semaphore = semaphore,
|
||||
.request = request_message,
|
||||
.response = NULL,
|
||||
};
|
||||
prv_lock();
|
||||
s_expectations_head = (HcExpectation *) list_prepend((ListNode *)s_expectations_head,
|
||||
&expectation->node);
|
||||
prv_unlock();
|
||||
return expectation;
|
||||
}
|
||||
|
||||
static void prv_cleanup_expectation(HcExpectation *expectation) {
|
||||
prv_lock();
|
||||
list_remove((ListNode *)expectation, (ListNode **)&s_expectations_head, NULL);
|
||||
prv_unlock();
|
||||
vSemaphoreDelete(expectation->semaphore);
|
||||
kernel_free(expectation);
|
||||
}
|
||||
|
||||
static HcProtocolMessage *prv_expect(HcExpectation *expectation) {
|
||||
HcProtocolMessage *response = NULL;
|
||||
// Save for debugging
|
||||
const HcCommandID cmd_id = expectation->request->command_id;
|
||||
const HcEndpointID end_id = expectation->request->endpoint_id;
|
||||
|
||||
TickType_t time_ticks = milliseconds_to_ticks(HC_PROTOCOL_DEFAULT_RESPONSE_TIMEOUT_MS);
|
||||
if (end_id == HcEndpointID_Ctl) {
|
||||
time_ticks = milliseconds_to_ticks(HC_PROTOCOL_DEFAULT_CTL_ENDPOINT_RESPONSE_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
xSemaphoreTake(expectation->semaphore, time_ticks);
|
||||
prv_lock();
|
||||
{
|
||||
// If we've timed out, expectation->response is NULL:
|
||||
response = expectation->response;
|
||||
// Set request to NULL, so it can't be matched any longer (important in timeout scenario)
|
||||
expectation->request = NULL;
|
||||
}
|
||||
prv_unlock();
|
||||
|
||||
if (response) {
|
||||
s_num_consec_request_failures = 0;
|
||||
} else {
|
||||
s_num_consec_request_failures++;
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HC request timed out (endpoint:%d, command:%"PRIu8")",
|
||||
end_id, cmd_id);
|
||||
if (prv_check_initialized() && (s_num_consec_request_failures >= NUM_CONSEC_FAILURES)) {
|
||||
core_dump_and_reset_or_reboot();
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
HcProtocolMessage *hc_protocol_enqueue_with_payload_and_expect(HcEndpointID endpoint_id,
|
||||
HcCommandID command_id,
|
||||
const uint8_t *request_payload,
|
||||
size_t request_payload_length) {
|
||||
if (!prv_check_initialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HcProtocolMessage *request = prv_create_message(endpoint_id, command_id, request_payload,
|
||||
request_payload_length);
|
||||
// `response` will be NULL if it failed.
|
||||
HcProtocolMessage *response = hc_protocol_enqueue_and_expect(request);
|
||||
kernel_free(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
HcProtocolMessage *hc_protocol_enqueue_and_expect(HcProtocolMessage *request_message) {
|
||||
if (!prv_check_initialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow because we'd deadlock otherwise:
|
||||
PBL_ASSERTN(!host_transport_is_current_task_host_transport_task());
|
||||
|
||||
prv_assign_next_transaction_id(request_message);
|
||||
HcExpectation *expectation = prv_set_expectation_for_request(request_message);
|
||||
|
||||
if (!prv_enqueue(request_message)) {
|
||||
prv_cleanup_expectation(expectation);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HcProtocolMessage *response_message = prv_expect(expectation);
|
||||
prv_cleanup_expectation(expectation);
|
||||
|
||||
return response_message;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool hc_protocol_enqueue_response(const HcProtocolMessage *to_request,
|
||||
const uint8_t *response_payload, size_t response_payload_length) {
|
||||
if (!prv_check_initialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PBL_ASSERTN(!to_request->transaction_id.is_response);
|
||||
size_t message_length = sizeof(HcProtocolMessage) + response_payload_length;
|
||||
HcProtocolMessage *response = kernel_malloc_check(message_length);
|
||||
response->message_length = message_length;
|
||||
response->endpoint_id = to_request->endpoint_id;
|
||||
response->command_id = to_request->command_id;
|
||||
response->transaction_id.is_response = true;
|
||||
response->transaction_id.sn = to_request->transaction_id.sn;
|
||||
if (response_payload_length) {
|
||||
memcpy(response->payload, response_payload, response_payload_length);
|
||||
}
|
||||
bool rv = prv_enqueue(response);
|
||||
kernel_free(response);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static HcProtocolMessageHandler prv_get_handler_for_endpoint_id(HcEndpointID endpoint_id) {
|
||||
if (endpoint_id >= HcEndpointIDCount) {
|
||||
return NULL;
|
||||
}
|
||||
return g_hc_protocol_endpoints_table[endpoint_id];
|
||||
}
|
||||
|
||||
static void prv_dispatch_message_to_static_handler(HcProtocolMessage *message, bool *should_free) {
|
||||
const HcProtocolMessageHandler handler = prv_get_handler_for_endpoint_id(message->endpoint_id);
|
||||
if (!handler) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No handler for endpoint ID %u", message->endpoint_id);
|
||||
return;
|
||||
}
|
||||
|
||||
hc_protocol_cb_dispatch_handler(handler, message, should_free);
|
||||
}
|
||||
|
||||
static bool prv_expectation_for_message_list_filter_cb(ListNode *found_node, void *data) {
|
||||
HcExpectation *expectation = (HcExpectation *)found_node;
|
||||
const HcProtocolMessage *message = (const HcProtocolMessage *)data;
|
||||
if (!expectation->request) {
|
||||
// Already being handled or timed out.
|
||||
return false;
|
||||
}
|
||||
if (!message->transaction_id.is_response) {
|
||||
return false;
|
||||
}
|
||||
return (message->transaction_id.sn == expectation->request->transaction_id.sn);
|
||||
}
|
||||
|
||||
static HcExpectation *prv_expectation_for_message(HcProtocolMessage *message) {
|
||||
return (HcExpectation *)list_find((ListNode *)s_expectations_head,
|
||||
prv_expectation_for_message_list_filter_cb, message);
|
||||
}
|
||||
|
||||
static bool prv_try_handle_expectation(HcProtocolMessage *message, bool *should_free) {
|
||||
prv_lock();
|
||||
HcExpectation *expectation = prv_expectation_for_message(message);
|
||||
if (!expectation) {
|
||||
prv_unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make a heap copy if needed, or transfer ownership if the message is already heap-allocated:
|
||||
HcProtocolMessage *response_heap_copy;
|
||||
if (*should_free) {
|
||||
*should_free = false;
|
||||
response_heap_copy = message;
|
||||
} else {
|
||||
response_heap_copy = (HcProtocolMessage *) kernel_malloc_check(message->message_length);
|
||||
memcpy(response_heap_copy, message, message->message_length);
|
||||
}
|
||||
|
||||
expectation->response = response_heap_copy;
|
||||
xSemaphoreGive(expectation->semaphore);
|
||||
prv_unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
static HcProtocolMessage *prv_get_message(size_t length, bool *should_free) {
|
||||
uint8_t *rx_data = NULL;
|
||||
*should_free = host_transport_rx_read(&rx_data, length);
|
||||
return (HcProtocolMessage *) rx_data;
|
||||
}
|
||||
|
||||
void hc_protocol_process_receive_buffer(void) {
|
||||
if (!prv_check_initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t rx_length = host_transport_rx_get_length();
|
||||
while (rx_length) {
|
||||
if (rx_length < sizeof(HcProtocolMessage)) {
|
||||
// Header not received completely yet
|
||||
return;
|
||||
}
|
||||
|
||||
bool should_free = false;
|
||||
HcProtocolMessage *header = prv_get_message(sizeof(HcProtocolMessage), &should_free);
|
||||
const size_t message_length = header->message_length;
|
||||
if (should_free) {
|
||||
kernel_free(header);
|
||||
}
|
||||
|
||||
if (rx_length < message_length) {
|
||||
// Message not received completely yet
|
||||
return;
|
||||
}
|
||||
|
||||
HcProtocolMessage *message = prv_get_message(message_length, &should_free);
|
||||
if (!prv_try_handle_expectation(message, &should_free)) {
|
||||
prv_dispatch_message_to_static_handler(message, &should_free);
|
||||
}
|
||||
|
||||
host_transport_rx_consume(message_length);
|
||||
if (should_free) {
|
||||
kernel_free(message);
|
||||
}
|
||||
|
||||
rx_length -= message_length;
|
||||
}
|
||||
}
|
||||
|
||||
void hc_protocol_buffer_gained_space(void) {
|
||||
PBL_ASSERTN(s_retry_semph);
|
||||
xSemaphoreGive(s_retry_semph);
|
||||
}
|
||||
|
||||
//! Should be called only once at boot.
|
||||
void hc_protocol_boot(void) {
|
||||
if (s_hc_lock) {
|
||||
return;
|
||||
}
|
||||
s_hc_lock = mutex_create_recursive();
|
||||
}
|
||||
|
||||
//! Should be called when stack is brought up.
|
||||
void hc_protocol_init(void) {
|
||||
s_outstanding_enqueues = 0;
|
||||
s_hc_next_transaction_id = 0;
|
||||
s_retry_semph = xSemaphoreCreateBinary();
|
||||
prv_update_hc_state(HcProtoclStateIsInitialized);
|
||||
}
|
||||
|
||||
//! Should be called when stack is torn down.
|
||||
void hc_protocol_deinit(void) {
|
||||
prv_update_hc_state(HcProtoclStateDeinitializing);
|
||||
|
||||
while (prv_still_processing_enqueues()) {
|
||||
// Give the semaphore in case a task is waiting on it
|
||||
xSemaphoreGive(s_retry_semph);
|
||||
// Give the task some time to process it
|
||||
vTaskDelay(2);
|
||||
}
|
||||
|
||||
// At this point it should no longer be possible for someone to use the retry
|
||||
// semaphore so delete it
|
||||
SemaphoreHandle_t s_retry_semph_tmp = s_retry_semph;
|
||||
s_retry_semph = NULL;
|
||||
vSemaphoreDelete(s_retry_semph_tmp);
|
||||
|
||||
prv_lock();
|
||||
HcExpectation *expectation = s_expectations_head;
|
||||
while (expectation) {
|
||||
if (!expectation->request) {
|
||||
// Semaphore already given or timed out, so skip it.
|
||||
continue;
|
||||
}
|
||||
// Just give, the expectation + semaphore should get cleaned up automatically now.
|
||||
xSemaphoreGive(expectation->semaphore);
|
||||
expectation = (HcExpectation *) list_get_next(&expectation->node);
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
94
src/bluetooth-fw/da1468x/controller/board/board_robert.c
Normal file
94
src/bluetooth-fw/da1468x/controller/board/board_robert.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "board.h"
|
||||
|
||||
static const BoardConfigHostSPI s_host_spi = {
|
||||
.mcu_int = {
|
||||
.port = HW_GPIO_PORT_2,
|
||||
.pin = HW_GPIO_PIN_3,
|
||||
.function = HW_GPIO_FUNC_GPIO,
|
||||
},
|
||||
|
||||
.spi = {
|
||||
.peripheral = HW_SPI1,
|
||||
.cs = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_1,
|
||||
.function = HW_GPIO_FUNC_SPI_EN,
|
||||
},
|
||||
.cs_2 = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_7,
|
||||
.function = HW_GPIO_FUNC_GPIO,
|
||||
},
|
||||
.clk = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_0,
|
||||
.function = HW_GPIO_FUNC_SPI_CLK,
|
||||
},
|
||||
.miso_do = {
|
||||
.port = HW_GPIO_PORT_1,
|
||||
.pin = HW_GPIO_PIN_3,
|
||||
.function = HW_GPIO_FUNC_SPI_DO,
|
||||
},
|
||||
.mosi_di = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_4,
|
||||
.function = HW_GPIO_FUNC_SPI_DI,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// These are spare GPIOs on both the SILK and ROBERT bigboards which are broken out to a header
|
||||
// Check out debug_gpio.h for more info on how to leverage them for debug
|
||||
static BoardConfigGpioDebug s_debug_gpios = {
|
||||
.num_debug_gpios = 4,
|
||||
.debug_gpio = {
|
||||
[0] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_0,
|
||||
},
|
||||
[1] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_1,
|
||||
},
|
||||
[2] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_2,
|
||||
},
|
||||
[3] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_3,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const BoardConfigBTFEM s_bt_fem = {
|
||||
.rx = {
|
||||
.port = HW_GPIO_PORT_1,
|
||||
.pin = HW_GPIO_PIN_6,
|
||||
},
|
||||
.tx = {
|
||||
.port = HW_GPIO_PORT_1,
|
||||
.pin = HW_GPIO_PIN_7,
|
||||
},
|
||||
};
|
||||
|
||||
const BoardConfigHostSPI * const HOST_SPI = &s_host_spi;
|
||||
BoardConfigGpioDebug * const DEBUG_GPIOS = &s_debug_gpios;
|
||||
const BoardConfigBTFEM * const BT_FEM = &s_bt_fem;
|
81
src/bluetooth-fw/da1468x/controller/board/board_silk.c
Normal file
81
src/bluetooth-fw/da1468x/controller/board/board_silk.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "board.h"
|
||||
|
||||
static const BoardConfigHostSPI s_host_spi = {
|
||||
.mcu_int = {
|
||||
.port = HW_GPIO_PORT_2,
|
||||
.pin = HW_GPIO_PIN_3,
|
||||
.function = HW_GPIO_FUNC_GPIO,
|
||||
},
|
||||
|
||||
.spi = {
|
||||
.peripheral = HW_SPI1,
|
||||
.cs = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_1,
|
||||
.function = HW_GPIO_FUNC_SPI_EN,
|
||||
},
|
||||
.cs_2 = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_7,
|
||||
.function = HW_GPIO_FUNC_GPIO,
|
||||
},
|
||||
.clk = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_0,
|
||||
.function = HW_GPIO_FUNC_SPI_CLK,
|
||||
},
|
||||
.miso_do = {
|
||||
.port = HW_GPIO_PORT_1,
|
||||
.pin = HW_GPIO_PIN_3,
|
||||
.function = HW_GPIO_FUNC_SPI_DO,
|
||||
},
|
||||
.mosi_di = {
|
||||
.port = HW_GPIO_PORT_0,
|
||||
.pin = HW_GPIO_PIN_4,
|
||||
.function = HW_GPIO_FUNC_SPI_DI,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// These are spare GPIOs on both the SILK and ROBERT bigboards which are broken out to a header
|
||||
// Check out debug_gpio.h for more info on how to leverage them for debug
|
||||
static BoardConfigGpioDebug s_debug_gpios = {
|
||||
.num_debug_gpios = 4,
|
||||
.debug_gpio = {
|
||||
[0] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_0,
|
||||
},
|
||||
[1] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_1,
|
||||
},
|
||||
[2] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_2,
|
||||
},
|
||||
[3] = {
|
||||
.port = HW_GPIO_PORT_3,
|
||||
.pin = HW_GPIO_PIN_3,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const BoardConfigHostSPI * const HOST_SPI = &s_host_spi;
|
||||
BoardConfigGpioDebug * const DEBUG_GPIOS = &s_debug_gpios;
|
66
src/bluetooth-fw/da1468x/controller/board/include/board.h
Normal file
66
src/bluetooth-fw/da1468x/controller/board/include/board.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hw_gpio.h"
|
||||
#include "hw_spi.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct GPIOPortPin {
|
||||
HW_GPIO_PORT port;
|
||||
HW_GPIO_PIN pin;
|
||||
HW_GPIO_FUNC function;
|
||||
} GPIOPortPin;
|
||||
|
||||
typedef struct {
|
||||
// BT Controller to MCU INT line:
|
||||
GPIOPortPin mcu_int;
|
||||
|
||||
// BT Controller SPI slave:
|
||||
struct {
|
||||
HW_SPI_ID *peripheral;
|
||||
GPIOPortPin cs;
|
||||
GPIOPortPin cs_2;
|
||||
GPIOPortPin clk;
|
||||
GPIOPortPin miso_do;
|
||||
GPIOPortPin mosi_di;
|
||||
} spi;
|
||||
} BoardConfigHostSPI;
|
||||
|
||||
typedef struct DebugGPIOInfo {
|
||||
HW_GPIO_PORT port;
|
||||
HW_GPIO_PIN pin;
|
||||
bool is_active;
|
||||
} DebugGPIOInfo;
|
||||
|
||||
typedef struct BoardConfigGpioDebug {
|
||||
int num_debug_gpios;
|
||||
DebugGPIOInfo debug_gpio[];
|
||||
} BoardConfigGpioDebug;
|
||||
|
||||
typedef struct {
|
||||
// BT FEM
|
||||
GPIOPortPin rx;
|
||||
GPIOPortPin tx;
|
||||
} BoardConfigBTFEM;
|
||||
|
||||
extern const BoardConfigHostSPI * const HOST_SPI;
|
||||
extern BoardConfigGpioDebug * const DEBUG_GPIOS;
|
||||
extern const BoardConfigBTFEM * const BT_FEM;
|
||||
|
||||
#include "board_definitions.h"
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#if PLATFORM_SILK
|
||||
#include "board_silk.h"
|
||||
#elif PLATFORM_CALCULUS || PLATFORM_ROBERT
|
||||
#include "board_robert.h"
|
||||
#else
|
||||
#error "Unknown board definition"
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Note TX DMA channel is assumed to be RX DMA channel + 1, see SPI_SLAVE_TO_EXT_MASTER
|
||||
#define HOST_SPI_RX_DMA_CHANNEL (HW_DMA_CHANNEL_0)
|
||||
#define HOST_SPI_TX_DMA_CHANNEL (HW_DMA_CHANNEL_1)
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Note TX DMA channel is assumed to be RX DMA channel + 1, see SPI_SLAVE_TO_EXT_MASTER
|
||||
#define HOST_SPI_RX_DMA_CHANNEL (HW_DMA_CHANNEL_0)
|
||||
#define HOST_SPI_TX_DMA_CHANNEL (HW_DMA_CHANNEL_1)
|
27
src/bluetooth-fw/da1468x/controller/board/wscript_build
Normal file
27
src/bluetooth-fw/da1468x/controller/board/wscript_build
Normal file
|
@ -0,0 +1,27 @@
|
|||
def _build_board(source_file, name, config_h, idx):
|
||||
bld.objects(
|
||||
name=name,
|
||||
source=[source_file],
|
||||
use=['dialog_sdk_includes'],
|
||||
export_includes=['include'],
|
||||
includes=['include'],
|
||||
inject_include_files=[config_h],
|
||||
idx=idx,
|
||||
)
|
||||
|
||||
board = bld.env.BOARD
|
||||
if board in ('silk_evt', 'silk_bb', 'silk_bb2', 'silk'):
|
||||
_build_board('board_silk.c', 'dialog_board_main',
|
||||
'../main/config/custom_config_main.h', 3000)
|
||||
_build_board('board_silk.c', 'dialog_board_boot',
|
||||
'../boot/config/custom_config_boot.h', 4000)
|
||||
elif board in ('robert_bb', 'robert_bb2', 'robert_evt', 'cutts_bb'):
|
||||
_build_board('board_robert.c', 'dialog_board_main',
|
||||
'../main/config/custom_config_main.h', 3000)
|
||||
_build_board('board_robert.c', 'dialog_board_boot',
|
||||
'../boot/config/custom_config_boot.h', 4000)
|
||||
else:
|
||||
print bld.env
|
||||
bld.fatal('Unknown board {}'.format(board))
|
||||
|
||||
# vim:filetype=python
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void debug_print_str(const char *str);
|
||||
void debug_print_hex(int val);
|
||||
void debug_print_str_and_int(const char *str, int value);
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Initializes Dialog part as a SPI Slave and puts a firmware load in motion.
|
||||
// See host_transport.c for implementation details
|
||||
void host_transport_begin(void);
|
127
src/bluetooth-fw/da1468x/controller/boot/ldscripts/mem.ld.h
Normal file
127
src/bluetooth-fw/da1468x/controller/boot/ldscripts/mem.ld.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "da1468x_mem_map.h"
|
||||
|
||||
/* Linker script to place sections and symbol values. Should be used together
|
||||
* with other linker script that defines memory regions CODE_AND_RAM
|
||||
* It references following symbols, which must be defined in code:
|
||||
* Reset_Handler : Entry of reset handler
|
||||
*
|
||||
* It defines following symbols, which code can use without definition:
|
||||
* __zero_table_start__
|
||||
* __zero_table_end__
|
||||
* __etext
|
||||
* __data_start__
|
||||
* __data_end__
|
||||
* __bss_start__
|
||||
* __bss_end__
|
||||
* __StackLimit
|
||||
* __StackTop
|
||||
* __stack
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
CODE_AND_RAM (rwx): ORIGIN = DATA_RAM_BASE_ADDRESS, LENGTH = 10 * 1024
|
||||
}
|
||||
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
KEEP(*(.isr_vector))
|
||||
/* make sure that IVT doesn't cross 0xC0 */
|
||||
. = 0xC0;
|
||||
|
||||
KEEP(*(.patch_table))
|
||||
KEEP(*(.default_patch_code_handler_section))
|
||||
|
||||
*(.text*)
|
||||
|
||||
*(.rodata*)
|
||||
|
||||
KEEP(*(.eh_frame*))
|
||||
} > CODE_AND_RAM
|
||||
|
||||
/* To clear multiple BSS sections,
|
||||
* uncomment .zero.table section and,
|
||||
* define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */
|
||||
.zero.table :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__zero_table_start__ = .;
|
||||
LONG (__bss_start__)
|
||||
LONG (__bss_end__ - __bss_start__)
|
||||
__zero_table_end__ = .;
|
||||
} > CODE_AND_RAM
|
||||
|
||||
__etext = .;
|
||||
|
||||
/* The initialised data section is stored immediately
|
||||
at the end of the text section */
|
||||
.data : AT (__etext)
|
||||
{
|
||||
__data_start__ = .;
|
||||
*(vtable)
|
||||
*(.data*)
|
||||
|
||||
. = ALIGN(4);
|
||||
/* init_array/fini_array moved to flash, align preserved */
|
||||
|
||||
KEEP(*(.jcr*))
|
||||
. = ALIGN(4);
|
||||
/* All data end */
|
||||
__data_end__ = .;
|
||||
} > CODE_AND_RAM
|
||||
|
||||
/* GNU build id: This is a hash of parts of the binary that uniquely
|
||||
* identifies the binary. This hash gets inserted by the linker;
|
||||
* we're passing the flag --build-id=sha1 to do this.
|
||||
* The variable DIALOG_BUILD_ID is provided, so that the values can be used
|
||||
* in the firmware code. */
|
||||
.note.gnu.build-id : {
|
||||
PROVIDE(DIALOG_BUILD_ID = .);
|
||||
KEEP(*(.note.gnu.build-id))
|
||||
} > CODE_AND_RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__bss_start__ = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
*(retention_mem_zi)
|
||||
. = ALIGN(4);
|
||||
__bss_end__ = .;
|
||||
} > CODE_AND_RAM
|
||||
|
||||
/* .stack_dummy section doesn't contains any symbols. It is only a
|
||||
* a placeholder for where the ISR stack lives. */
|
||||
.stack_dummy (COPY):
|
||||
{
|
||||
__StackBottom = .;
|
||||
*(.stack*)
|
||||
} > CODE_AND_RAM
|
||||
|
||||
/* Set stack top to end of RAM, and stack limit move down by
|
||||
* size of stack_dummy section */
|
||||
__StackTop = ORIGIN(CODE_AND_RAM) + LENGTH(CODE_AND_RAM);
|
||||
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
|
||||
PROVIDE(__stack = __StackTop);
|
||||
}
|
35
src/bluetooth-fw/da1468x/controller/boot/src/debug_print.c
Normal file
35
src/bluetooth-fw/da1468x/controller/boot/src/debug_print.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "util/string.h"
|
||||
|
||||
extern int _write(int file, char *ptr, int len);
|
||||
|
||||
void debug_print_str(char *str) {
|
||||
_write(0, str, strlen(str));
|
||||
}
|
||||
|
||||
void debug_print_hex(int val) {
|
||||
char buffer[12] = { };
|
||||
itoa(val, buffer, sizeof(buffer));
|
||||
debug_print_str(buffer);
|
||||
}
|
||||
|
||||
void debug_print_str_and_int(char *str, int value) {
|
||||
debug_print_str(str);
|
||||
debug_print_hex(value);
|
||||
debug_print_str("\n");
|
||||
}
|
486
src/bluetooth-fw/da1468x/controller/boot/src/default_registers.c
Normal file
486
src/bluetooth-fw/da1468x/controller/boot/src/default_registers.c
Normal file
|
@ -0,0 +1,486 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "sdk_defs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// If Dialog ever updates the number of registers or the default values of the registers, one can:
|
||||
// 1. Set `DEFAULT_REGISTER_DEBUG` to 1
|
||||
// 2. Update/Add/Remove the register values using `REG_SETF`
|
||||
// 3. Flash and boot the dialog chip.
|
||||
// 4. Attach GDB to ble chip and type `p/x ALL_REGISTERS`
|
||||
// 5. Copy, reformat, and paste output to the squashed register values.
|
||||
|
||||
#define DEFAULT_REGISTER_DEBUG 0
|
||||
|
||||
#if DEFAULT_REGISTER_DEBUG
|
||||
typedef struct AllRegisters {
|
||||
// DCDC
|
||||
uint16_t DCDC_CTRL_0_REG;
|
||||
uint16_t DCDC_CTRL_1_REG;
|
||||
uint16_t DCDC_CTRL_2_REG;
|
||||
uint16_t DCDC_V14_0_REG;
|
||||
uint16_t DCDC_V14_1_REG;
|
||||
uint16_t DCDC_V18_0_REG;
|
||||
uint16_t DCDC_V18_1_REG;
|
||||
uint16_t DCDC_VDD_0_REG;
|
||||
uint16_t DCDC_VDD_1_REG;
|
||||
uint16_t DCDC_V18P_0_REG;
|
||||
uint16_t DCDC_V18P_1_REG;
|
||||
uint16_t DCDC_RET_0_REG;
|
||||
uint16_t DCDC_RET_1_REG;
|
||||
uint16_t DCDC_TRIM_REG;
|
||||
uint16_t DCDC_TEST_0_REG;
|
||||
uint16_t DCDC_TEST_1_REG;
|
||||
uint16_t DCDC_IRQ_CLEAR_REG;
|
||||
uint16_t DCDC_IRQ_MASK_REG;
|
||||
|
||||
// CRG_TOP
|
||||
uint16_t BANDGAP_REG;
|
||||
uint16_t BOD_STATUS_REG;
|
||||
uint16_t FORCE_SLEEP_REG;
|
||||
uint16_t LDOS_DISABLE_REG;
|
||||
uint16_t AON_SPARE_REG;
|
||||
|
||||
// QSPIC
|
||||
uint32_t QSPIC_CTRLMODE_REG;
|
||||
uint32_t QSPIC_BURSTCMDA_REG;
|
||||
uint32_t QSPIC_BURSTCMDB_REG;
|
||||
uint32_t QSPIC_WRITEDATA_REG;
|
||||
uint32_t QSPIC_DUMMYDATA_REG;
|
||||
uint32_t QSPIC_ERASECTRL_REG;
|
||||
uint32_t QSPIC_ERASECMDA_REG;
|
||||
uint32_t QSPIC_ERASECMDB_REG;
|
||||
uint32_t QSPIC_BURSTBRK_REG;
|
||||
uint32_t QSPIC_STATUSCMD_REG;
|
||||
uint32_t QSPIC_CHCKERASE_REG;
|
||||
uint32_t QSPIC_GP_REG;
|
||||
uint32_t QSPIC_UCODE_START;
|
||||
|
||||
// GPREG
|
||||
uint16_t PLL_SYS_CTRL1_REG;
|
||||
uint16_t PLL_SYS_CTRL2_REG;
|
||||
uint16_t PLL_SYS_CTRL3_REG;
|
||||
uint16_t PLL_SYS_TEST_REG;
|
||||
|
||||
// CACHE
|
||||
uint32_t CACHE_CTRL1_REG;
|
||||
uint32_t CACHE_LNSIZECFG_REG;
|
||||
uint32_t CACHE_ASSOCCFG_REG;
|
||||
uint32_t CACHE_CTRL2_REG;
|
||||
uint32_t CACHE_CTRL3_REG;
|
||||
uint32_t CACHE_MRM_HITS_REG;
|
||||
uint32_t CACHE_MRM_MISSES_REG;
|
||||
uint32_t CACHE_MRM_CTRL_REG;
|
||||
uint32_t CACHE_MRM_TINT_REG;
|
||||
uint32_t CACHE_MRM_THRES_REG;
|
||||
uint32_t SWD_RESET_REG;
|
||||
} AllRegisters;
|
||||
|
||||
static AllRegisters ALL_REGISTERS;
|
||||
|
||||
#endif // DEFAULT_REGISTER_DEBUG
|
||||
|
||||
|
||||
void default_registers_restore(void) {
|
||||
DCDC->DCDC_CTRL_0_REG = 0x2f24;
|
||||
DCDC->DCDC_CTRL_1_REG = 0x5410;
|
||||
DCDC->DCDC_CTRL_2_REG = 0x882d;
|
||||
DCDC->DCDC_V14_0_REG = 0xa1a4;
|
||||
DCDC->DCDC_V14_1_REG = 0xd890;
|
||||
DCDC->DCDC_V18_0_REG = 0xe3e4;
|
||||
DCDC->DCDC_V18_1_REG = 0xbc90;
|
||||
DCDC->DCDC_VDD_0_REG = 0xc304;
|
||||
DCDC->DCDC_VDD_1_REG = 0xec90;
|
||||
DCDC->DCDC_V18P_0_REG = 0xe3e4;
|
||||
DCDC->DCDC_V18P_1_REG = 0xbc90;
|
||||
DCDC->DCDC_RET_0_REG = 0xaaa6;
|
||||
DCDC->DCDC_RET_1_REG = 0xaa46;
|
||||
DCDC->DCDC_TRIM_REG = 0x0;
|
||||
DCDC->DCDC_TEST_0_REG = 0x0;
|
||||
DCDC->DCDC_TEST_1_REG = 0x0;
|
||||
DCDC->DCDC_IRQ_CLEAR_REG = 0x0;
|
||||
DCDC->DCDC_IRQ_MASK_REG = 0x0;
|
||||
CRG_TOP->BANDGAP_REG = 0x0;
|
||||
CRG_TOP->BOD_STATUS_REG = 0x7;
|
||||
CRG_TOP->FORCE_SLEEP_REG = 0x1;
|
||||
CRG_TOP->LDOS_DISABLE_REG = 0x0;
|
||||
CRG_TOP->AON_SPARE_REG = 0x0;
|
||||
QSPIC->QSPIC_CTRLBUS_REG = 0x0;
|
||||
QSPIC->QSPIC_CTRLMODE_REG = 0x0;
|
||||
QSPIC->QSPIC_BURSTCMDA_REG = 0x0;
|
||||
QSPIC->QSPIC_BURSTCMDB_REG = 0x0;
|
||||
QSPIC->QSPIC_WRITEDATA_REG = 0x0;
|
||||
QSPIC->QSPIC_DUMMYDATA_REG = 0x0;
|
||||
QSPIC->QSPIC_ERASECTRL_REG = 0x0;
|
||||
QSPIC->QSPIC_ERASECMDA_REG = 0x0;
|
||||
QSPIC->QSPIC_ERASECMDB_REG = 0x0;
|
||||
QSPIC->QSPIC_BURSTBRK_REG = 0x0;
|
||||
QSPIC->QSPIC_STATUSCMD_REG = 0x0;
|
||||
QSPIC->QSPIC_CHCKERASE_REG = 0x0;
|
||||
QSPIC->QSPIC_GP_REG = 0x0;
|
||||
QSPIC->QSPIC_UCODE_START = 0x0;
|
||||
GPREG->PLL_SYS_CTRL1_REG = 0x100;
|
||||
GPREG->PLL_SYS_CTRL2_REG = 0x2006;
|
||||
GPREG->PLL_SYS_CTRL3_REG = 0x3c09;
|
||||
GPREG->PLL_SYS_TEST_REG = 0x70;
|
||||
CACHE->CACHE_CTRL1_REG = 0x0;
|
||||
CACHE->CACHE_LNSIZECFG_REG = 0x0;
|
||||
CACHE->CACHE_ASSOCCFG_REG = 0x2;
|
||||
CACHE->CACHE_CTRL2_REG = 0x0;
|
||||
CACHE->CACHE_CTRL3_REG = 0x22;
|
||||
CACHE->CACHE_MRM_HITS_REG = 0x0;
|
||||
CACHE->CACHE_MRM_MISSES_REG = 0x0;
|
||||
CACHE->CACHE_MRM_CTRL_REG = 0x0;
|
||||
CACHE->CACHE_MRM_TINT_REG = 0x0;
|
||||
CACHE->CACHE_MRM_THRES_REG = 0x0;
|
||||
CACHE->SWD_RESET_REG = 0x0;
|
||||
|
||||
#if DEFAULT_REGISTER_DEBUG
|
||||
// DCDC
|
||||
REG_SETF(DCDC, DCDC_CTRL_0_REG, DCDC_FAST_STARTUP, 0x0);
|
||||
REG_SETF(DCDC, DCDC_CTRL_0_REG, DCDC_BROWNOUT_LV_MODE, 0x1);
|
||||
REG_SETF(DCDC, DCDC_CTRL_0_REG, DCDC_IDLE_CLK_DIV, 0x1);
|
||||
REG_SETF(DCDC, DCDC_CTRL_0_REG, DCDC_PRIORITY, 0xE4);
|
||||
REG_SETF(DCDC, DCDC_CTRL_0_REG, DCDC_FW_ENABLE, 0x1);
|
||||
REG_SETF(DCDC, DCDC_CTRL_0_REG, DCDC_MODE, 0);
|
||||
|
||||
REG_SETF(DCDC, DCDC_CTRL_1_REG, DCDC_STARTUP_DELAY, 0xA);
|
||||
REG_SETF(DCDC, DCDC_CTRL_1_REG, DCDC_GLOBAL_MAX_IDLE_TIME, 0x20);
|
||||
REG_SETF(DCDC, DCDC_CTRL_1_REG, DCDC_TIMEOUT, 0x10);
|
||||
|
||||
REG_SETF(DCDC, DCDC_CTRL_2_REG, DCDC_TIMEOUT_IRQ_TRIG, 0x8);
|
||||
REG_SETF(DCDC, DCDC_CTRL_2_REG, DCDC_TIMEOUT_IRQ_RES, 0x8);
|
||||
REG_SETF(DCDC, DCDC_CTRL_2_REG, DCDC_TUNE, 0x0);
|
||||
REG_SETF(DCDC, DCDC_CTRL_2_REG, DCDC_LSSUP_TRIM, 0x5);
|
||||
REG_SETF(DCDC, DCDC_CTRL_2_REG, DCDC_HSGND_TRIM, 0x5);
|
||||
|
||||
REG_SETF(DCDC, DCDC_V14_0_REG, DCDC_V14_FAST_RAMPING, 0x1);
|
||||
REG_SETF(DCDC, DCDC_V14_0_REG, DCDC_V14_VOLTAGE, 0x8);
|
||||
REG_SETF(DCDC, DCDC_V14_0_REG, DCDC_V14_CUR_LIM_MAX_HV, 0xD);
|
||||
REG_SETF(DCDC, DCDC_V14_0_REG, DCDC_V14_CUR_LIM_MIN, 0x4);
|
||||
|
||||
REG_SETF(DCDC, DCDC_V14_1_REG, DCDC_V14_ENABLE_HV, 0x1);
|
||||
REG_SETF(DCDC, DCDC_V14_1_REG, DCDC_V14_ENABLE_LV, 0x1);
|
||||
REG_SETF(DCDC, DCDC_V14_1_REG, DCDC_V14_CUR_LIM_MAX_LV, 0x6);
|
||||
REG_SETF(DCDC, DCDC_V14_1_REG, DCDC_V14_IDLE_HYST, 0x4);
|
||||
REG_SETF(DCDC, DCDC_V14_1_REG, DCDC_V14_IDLE_MIN, 0x10);
|
||||
|
||||
REG_SETF(DCDC, DCDC_V18_0_REG, DCDC_V18_FAST_RAMPING, 0x1);
|
||||
REG_SETF(DCDC, DCDC_V18_0_REG, DCDC_V18_VOLTAGE, 0x18);
|
||||
REG_SETF(DCDC, DCDC_V18_0_REG, DCDC_V18_CUR_LIM_MAX_HV, 0x1F);
|
||||
REG_SETF(DCDC, DCDC_V18_0_REG, DCDC_V18_CUR_LIM_MIN, 0x4);
|
||||
|
||||
REG_SETF(DCDC, DCDC_V18_1_REG, DCDC_V18_ENABLE_HV, 0x1);
|
||||
REG_SETF(DCDC, DCDC_V18_1_REG, DCDC_V18_ENABLE_LV, 0x0);
|
||||
REG_SETF(DCDC, DCDC_V18_1_REG, DCDC_V18_CUR_LIM_MAX_LV, 0xF);
|
||||
REG_SETF(DCDC, DCDC_V18_1_REG, DCDC_V18_IDLE_HYST, 0x4);
|
||||
REG_SETF(DCDC, DCDC_V18_1_REG, DCDC_V18_IDLE_MIN, 0x10);
|
||||
|
||||
REG_SETF(DCDC, DCDC_VDD_0_REG, DCDC_VDD_FAST_RAMPING, 0x1);
|
||||
REG_SETF(DCDC, DCDC_VDD_0_REG, DCDC_VDD_VOLTAGE, 0x10);
|
||||
REG_SETF(DCDC, DCDC_VDD_0_REG, DCDC_VDD_CUR_LIM_MAX_HV, 0x18);
|
||||
REG_SETF(DCDC, DCDC_VDD_0_REG, DCDC_VDD_CUR_LIM_MIN, 0x4);
|
||||
|
||||
REG_SETF(DCDC, DCDC_VDD_1_REG, DCDC_VDD_ENABLE_HV, 0x1);
|
||||
REG_SETF(DCDC, DCDC_VDD_1_REG, DCDC_VDD_ENABLE_LV, 0x1);
|
||||
REG_SETF(DCDC, DCDC_VDD_1_REG, DCDC_VDD_CUR_LIM_MAX_LV, 0xB);
|
||||
REG_SETF(DCDC, DCDC_VDD_1_REG, DCDC_VDD_IDLE_HYST, 0x4);
|
||||
REG_SETF(DCDC, DCDC_VDD_1_REG, DCDC_VDD_IDLE_MIN, 0x10);
|
||||
|
||||
REG_SETF(DCDC, DCDC_V18P_0_REG, DCDC_V18P_FAST_RAMPING, 0x1);
|
||||
REG_SETF(DCDC, DCDC_V18P_0_REG, DCDC_V18P_VOLTAGE, 0x18);
|
||||
REG_SETF(DCDC, DCDC_V18P_0_REG, DCDC_V18P_CUR_LIM_MAX_HV, 0x1F);
|
||||
REG_SETF(DCDC, DCDC_V18P_0_REG, DCDC_V18P_CUR_LIM_MIN, 0x4);
|
||||
|
||||
REG_SETF(DCDC, DCDC_V18P_1_REG, DCDC_V18P_ENABLE_HV, 0x1);
|
||||
REG_SETF(DCDC, DCDC_V18P_1_REG, DCDC_V18P_ENABLE_LV, 0x0);
|
||||
REG_SETF(DCDC, DCDC_V18P_1_REG, DCDC_V18P_CUR_LIM_MAX_LV, 0xF);
|
||||
REG_SETF(DCDC, DCDC_V18P_1_REG, DCDC_V18P_IDLE_HYST, 0x4);
|
||||
REG_SETF(DCDC, DCDC_V18P_1_REG, DCDC_V18P_IDLE_MIN, 0x10);
|
||||
|
||||
REG_SETF(DCDC, DCDC_RET_0_REG, DCDC_V18P_RET_CYCLES, 0x5);
|
||||
REG_SETF(DCDC, DCDC_RET_0_REG, DCDC_V18P_CUR_LIM_RET, 0xA);
|
||||
REG_SETF(DCDC, DCDC_RET_0_REG, DCDC_VDD_RET_CYCLES, 0x5);
|
||||
REG_SETF(DCDC, DCDC_RET_0_REG, DCDC_VDD_CUR_LIM_RET, 0x6);
|
||||
|
||||
REG_SETF(DCDC, DCDC_RET_1_REG, DCDC_V18_RET_CYCLES, 0x5);
|
||||
REG_SETF(DCDC, DCDC_RET_1_REG, DCDC_V18_CUR_LIM_RET, 0xA);
|
||||
REG_SETF(DCDC, DCDC_RET_1_REG, DCDC_V14_RET_CYCLES, 0x2);
|
||||
REG_SETF(DCDC, DCDC_RET_1_REG, DCDC_V14_CUR_LIM_RET, 0x6);
|
||||
|
||||
REG_SETF(DCDC, DCDC_TRIM_REG, DCDC_P_COMP_MAN_TRIM, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TRIM_REG, DCDC_P_COMP_TRIM, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TRIM_REG, DCDC_N_COMP_MAN_TRIM, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TRIM_REG, DCDC_N_COMP_TRIM, 0x0);
|
||||
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_COMP_CLK, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_CURRENT, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_OUTPUT_MONITOR, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_ANA_TEST, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_IDLE, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_V18P, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_VDD, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_V18, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_V14, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_FW, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_NSW, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_0_REG, DCDC_FORCE_PSW, 0x0);
|
||||
|
||||
REG_SETF(DCDC, DCDC_TEST_1_REG, DCDC_COMP_CLK, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_1_REG, DCDC_TEST_CURRENT, 0x0);
|
||||
REG_SETF(DCDC, DCDC_TEST_1_REG, DCDC_TEST_REG, 0x0);
|
||||
|
||||
REG_SETF(DCDC, DCDC_IRQ_CLEAR_REG, DCDC_BROWN_OUT_IRQ_CLEAR, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_CLEAR_REG, DCDC_V18P_TIMEOUT_IRQ_CLEAR, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_CLEAR_REG, DCDC_VDD_TIMEOUT_IRQ_CLEAR, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_CLEAR_REG, DCDC_V18_TIMEOUT_IRQ_CLEAR, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_CLEAR_REG, DCDC_V14_TIMEOUT_IRQ_CLEAR, 0x0);
|
||||
|
||||
REG_SETF(DCDC, DCDC_IRQ_MASK_REG, DCDC_BROWN_OUT_IRQ_MASK, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_MASK_REG, DCDC_V18P_TIMEOUT_IRQ_MASK, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_MASK_REG, DCDC_VDD_TIMEOUT_IRQ_MASK, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_MASK_REG, DCDC_V18_TIMEOUT_IRQ_MASK, 0x0);
|
||||
REG_SETF(DCDC, DCDC_IRQ_MASK_REG, DCDC_V14_TIMEOUT_IRQ_MASK, 0x0);
|
||||
|
||||
|
||||
// CRG_TOP
|
||||
REG_SETF(CRG_TOP, BANDGAP_REG, BYPASS_COLD_BOOT_DISABLE, 0x0);
|
||||
REG_SETF(CRG_TOP, BANDGAP_REG, LDO_SLEEP_TRIM, 0x0);
|
||||
REG_SETF(CRG_TOP, BANDGAP_REG, BGR_ITRIM, 0x0);
|
||||
REG_SETF(CRG_TOP, BANDGAP_REG, BGR_TRIM, 0x0);
|
||||
|
||||
REG_SETF(CRG_TOP, BOD_STATUS_REG, BOD_VBAT_LOW, 0x0);
|
||||
REG_SETF(CRG_TOP, BOD_STATUS_REG, BOD_V33_LOW, 0x0);
|
||||
REG_SETF(CRG_TOP, BOD_STATUS_REG, BOD_1V8_FLASH_LOW, 0x0);
|
||||
REG_SETF(CRG_TOP, BOD_STATUS_REG, BOD_1V8_PA_LOW, 0x0);
|
||||
REG_SETF(CRG_TOP, BOD_STATUS_REG, BOD_VDD_LOW, 0x0);
|
||||
|
||||
REG_SETF(CRG_TOP, FORCE_SLEEP_REG, FORCE_BLE_SLEEP, 0x0); // GUESS
|
||||
REG_SETF(CRG_TOP, FORCE_SLEEP_REG, FORCE_FTDF_SLEEP, 0x0); // GUESS
|
||||
|
||||
REG_SETF(CRG_TOP, LDOS_DISABLE_REG, LDOS_DISABLE, 0x0); // GUESS
|
||||
|
||||
REG_SETF(CRG_TOP, AON_SPARE_REG, OSC16_HOLD_AMP_REG, 0x0); // GUESS
|
||||
REG_SETF(CRG_TOP, AON_SPARE_REG, OSC16_SH_DISABLE, 0x0); // GUESS
|
||||
REG_SETF(CRG_TOP, AON_SPARE_REG, EN_BATSYS_RET, 0x0); // GUESS
|
||||
REG_SETF(CRG_TOP, AON_SPARE_REG, EN_BUSSYS_RET, 0x0); // GUESS
|
||||
|
||||
|
||||
// QSPIC
|
||||
REG_SETF(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_DIS_CS, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_EN_CS, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_QUAD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_DUAL, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_SINGLE, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_USE_32BA, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_FORCENSEQ_EN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_PCLK_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_RPIPE_EN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_RXD_NEG, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_HRDY_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO3_DAT, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO2_DAT, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO3_OEN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO2_OEN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_CLK_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_AUTO_MD, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDA_REG, QSPIC_DMY_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDA_REG, QSPIC_EXT_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDA_REG, QSPIC_ADR_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDA_REG, QSPIC_INST_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDA_REG, QSPIC_EXT_BYTE, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDA_REG, QSPIC_INST_WB, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDA_REG, QSPIC_INST, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_FORCE, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_CS_HIGH_MIN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_SIZE, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_LEN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_INST_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_NUM, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_EXT_HF_DS, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_EXT_BYTE_EN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DAT_RX_MD, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_WRITEDATA_REG, QSPIC_WRITEDATA, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_DUMMYDATA_REG, QSPIC_DUMMYDATA, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_ERASECTRL_REG, QSPIC_ERASE_EN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECTRL_REG, QSPIC_ERS_ADDR, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDA_REG, QSPIC_RES_INST, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDA_REG, QSPIC_SUS_INST, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDA_REG, QSPIC_WEN_INST, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDA_REG, QSPIC_ERS_INST, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_RESSUS_DLY, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_ERSRES_HLD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_ERS_CS_HI, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_EAD_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_RES_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_SUS_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_WEN_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_ERASECMDB_REG, QSPIC_ERS_TX_MD, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_BURSTBRK_REG, QSPIC_SEC_HF_DS, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTBRK_REG, QSPIC_BRK_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTBRK_REG, QSPIC_BRK_SZ, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTBRK_REG, QSPIC_BRK_EN, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_BURSTBRK_REG, QSPIC_BRK_WRD, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_STATUSCMD_REG, QSPIC_STSDLY_SEL, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_STATUSCMD_REG, QSPIC_RESSTS_DLY, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_STATUSCMD_REG, QSPIC_BUSY_VAL, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_STATUSCMD_REG, QSPIC_RSTAT_RX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_STATUSCMD_REG, QSPIC_RSTAT_TX_MD, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_STATUSCMD_REG, QSPIC_RSTAT_INST, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_CHCKERASE_REG, QSPIC_CHCKERASE, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_GP_REG, QSPIC_PADS_SLEW, 0x0);
|
||||
REG_SETF(QSPIC, QSPIC_GP_REG, QSPIC_PADS_DRV, 0x0);
|
||||
|
||||
REG_SETF(QSPIC, QSPIC_UCODE_START, QSPIC_UCODE_X, 0x0);
|
||||
|
||||
|
||||
// GPREG
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL1_REG, PLL_R_DIV, 0x1);
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL1_REG, LDO_PLL_VREF_HOLD, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL1_REG, LDO_PLL_ENABLE, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL1_REG, PLL_EN, 0x0);
|
||||
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL2_REG, PLL_SEL_MIN_CUR_INT, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL2_REG, PLL_DEL_SEL, 0x2);
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL2_REG, PLL_N_DIV, 0x6);
|
||||
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL3_REG, PLL_RECALIB, 0x0); // GUESS
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL3_REG, PLL_START_DEL, 0xF);
|
||||
REG_SETF(GPREG, PLL_SYS_CTRL3_REG, PLL_ICP_SEL, 0x9);
|
||||
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_LOCK_DET_RES_CNT, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_SEL_R_DIV_TEST, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_SEL_N_DIV_TEST, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_CHANGE, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_OPEN_LOOP, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_TEST_VCTR, 0x0);
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_MIN_CURRENT, 0x38);
|
||||
REG_SETF(GPREG, PLL_SYS_TEST_REG, PLL_DIS_LOOPFILT, 0x0);
|
||||
|
||||
|
||||
// CACHE
|
||||
REG_SETF(CACHE, CACHE_CTRL1_REG, CACHE_RES1, 0x0);
|
||||
REG_SETF(CACHE, CACHE_CTRL1_REG, CACHE_FLUSH, 0x0);
|
||||
|
||||
REG_SETF(CACHE, CACHE_LNSIZECFG_REG, CACHE_LINE, 0x0);
|
||||
|
||||
REG_SETF(CACHE, CACHE_ASSOCCFG_REG, CACHE_ASSOC, 0x2);
|
||||
|
||||
REG_SETF(CACHE, CACHE_CTRL2_REG, ENABLE_ALSO_QSPIFLASH_CACHED, 0x0);
|
||||
REG_SETF(CACHE, CACHE_CTRL2_REG, ENABLE_ALSO_OTP_CACHED, 0x0);
|
||||
REG_SETF(CACHE, CACHE_CTRL2_REG, CACHE_CGEN, 0x0);
|
||||
REG_SETF(CACHE, CACHE_CTRL2_REG, CACHE_WEN, 0x0);
|
||||
REG_SETF(CACHE, CACHE_CTRL2_REG, CACHE_LEN, 0x0);
|
||||
|
||||
REG_SETF(CACHE, CACHE_CTRL3_REG, CACHE_CONTROLLER_RESET, 0x0);
|
||||
REG_SETF(CACHE, CACHE_CTRL3_REG, CACHE_RAM_SIZE_RESET_VALUE, 0x2);
|
||||
REG_SETF(CACHE, CACHE_CTRL3_REG, CACHE_LINE_SIZE_RESET_VALUE, 0x0);
|
||||
REG_SETF(CACHE, CACHE_CTRL3_REG, CACHE_ASSOCIATIVITY_RESET_VALUE, 0x2);
|
||||
|
||||
REG_SETF(CACHE, CACHE_MRM_HITS_REG, MRM_HITS, 0x0);
|
||||
|
||||
REG_SETF(CACHE, CACHE_MRM_MISSES_REG, MRM_MISSES, 0x0);
|
||||
|
||||
REG_SETF(CACHE, CACHE_MRM_CTRL_REG, MRM_IRQ_THRES_STATUS, 0x0);
|
||||
REG_SETF(CACHE, CACHE_MRM_CTRL_REG, MRM_IRQ_TINT_STATUS, 0x0);
|
||||
REG_SETF(CACHE, CACHE_MRM_CTRL_REG, MRM_IRQ_MASK, 0x0);
|
||||
REG_SETF(CACHE, CACHE_MRM_CTRL_REG, MRM_START, 0x0);
|
||||
|
||||
REG_SETF(CACHE, CACHE_MRM_TINT_REG, MRM_TINT, 0x0);
|
||||
|
||||
REG_SETF(CACHE, CACHE_MRM_THRES_REG, MRM_THRES, 0x0);
|
||||
|
||||
REG_SETF(CACHE, SWD_RESET_REG, SWD_HW_RESET_REQ, 0x0);
|
||||
|
||||
// DCDC
|
||||
ALL_REGISTERS.DCDC_CTRL_0_REG = DCDC->DCDC_CTRL_0_REG;
|
||||
ALL_REGISTERS.DCDC_CTRL_1_REG = DCDC->DCDC_CTRL_1_REG;
|
||||
ALL_REGISTERS.DCDC_CTRL_2_REG = DCDC->DCDC_CTRL_2_REG;
|
||||
ALL_REGISTERS.DCDC_V14_0_REG = DCDC->DCDC_V14_0_REG;
|
||||
ALL_REGISTERS.DCDC_V14_1_REG = DCDC->DCDC_V14_1_REG;
|
||||
ALL_REGISTERS.DCDC_V18_0_REG = DCDC->DCDC_V18_0_REG;
|
||||
ALL_REGISTERS.DCDC_V18_1_REG = DCDC->DCDC_V18_1_REG;
|
||||
ALL_REGISTERS.DCDC_VDD_0_REG = DCDC->DCDC_VDD_0_REG;
|
||||
ALL_REGISTERS.DCDC_VDD_1_REG = DCDC->DCDC_VDD_1_REG;
|
||||
ALL_REGISTERS.DCDC_V18P_0_REG = DCDC->DCDC_V18P_0_REG;
|
||||
ALL_REGISTERS.DCDC_V18P_1_REG = DCDC->DCDC_V18P_1_REG;
|
||||
ALL_REGISTERS.DCDC_RET_0_REG = DCDC->DCDC_RET_0_REG;
|
||||
ALL_REGISTERS.DCDC_RET_1_REG = DCDC->DCDC_RET_1_REG;
|
||||
ALL_REGISTERS.DCDC_TRIM_REG = DCDC->DCDC_TRIM_REG;
|
||||
ALL_REGISTERS.DCDC_TEST_0_REG = DCDC->DCDC_TEST_0_REG;
|
||||
ALL_REGISTERS.DCDC_TEST_1_REG = DCDC->DCDC_TEST_1_REG;
|
||||
|
||||
ALL_REGISTERS.DCDC_IRQ_CLEAR_REG = DCDC->DCDC_IRQ_CLEAR_REG;
|
||||
ALL_REGISTERS.DCDC_IRQ_MASK_REG = DCDC->DCDC_IRQ_MASK_REG;
|
||||
|
||||
// CRG_TOP
|
||||
ALL_REGISTERS.BANDGAP_REG = CRG_TOP->BANDGAP_REG;
|
||||
ALL_REGISTERS.BOD_STATUS_REG = CRG_TOP->BOD_STATUS_REG;
|
||||
ALL_REGISTERS.FORCE_SLEEP_REG = CRG_TOP->FORCE_SLEEP_REG;
|
||||
ALL_REGISTERS.LDOS_DISABLE_REG = CRG_TOP->LDOS_DISABLE_REG;
|
||||
ALL_REGISTERS.AON_SPARE_REG = CRG_TOP->AON_SPARE_REG;
|
||||
|
||||
// QSPIC
|
||||
ALL_REGISTERS.QSPIC_CTRLMODE_REG = QSPIC->QSPIC_CTRLMODE_REG;
|
||||
ALL_REGISTERS.QSPIC_BURSTCMDA_REG = QSPIC->QSPIC_BURSTCMDA_REG;
|
||||
ALL_REGISTERS.QSPIC_BURSTCMDB_REG = QSPIC->QSPIC_BURSTCMDB_REG;
|
||||
ALL_REGISTERS.QSPIC_WRITEDATA_REG = QSPIC->QSPIC_WRITEDATA_REG;
|
||||
ALL_REGISTERS.QSPIC_DUMMYDATA_REG = QSPIC->QSPIC_DUMMYDATA_REG;
|
||||
ALL_REGISTERS.QSPIC_ERASECTRL_REG = QSPIC->QSPIC_ERASECTRL_REG;
|
||||
ALL_REGISTERS.QSPIC_ERASECMDA_REG = QSPIC->QSPIC_ERASECMDA_REG;
|
||||
ALL_REGISTERS.QSPIC_ERASECMDB_REG = QSPIC->QSPIC_ERASECMDB_REG;
|
||||
ALL_REGISTERS.QSPIC_BURSTBRK_REG = QSPIC->QSPIC_BURSTBRK_REG;
|
||||
ALL_REGISTERS.QSPIC_STATUSCMD_REG = QSPIC->QSPIC_STATUSCMD_REG;
|
||||
ALL_REGISTERS.QSPIC_CHCKERASE_REG = QSPIC->QSPIC_CHCKERASE_REG;
|
||||
ALL_REGISTERS.QSPIC_GP_REG = QSPIC->QSPIC_GP_REG;
|
||||
ALL_REGISTERS.QSPIC_UCODE_START = QSPIC->QSPIC_UCODE_START;
|
||||
|
||||
// GPREG
|
||||
ALL_REGISTERS.PLL_SYS_CTRL1_REG = GPREG->PLL_SYS_CTRL1_REG;
|
||||
ALL_REGISTERS.PLL_SYS_CTRL2_REG = GPREG->PLL_SYS_CTRL2_REG;
|
||||
ALL_REGISTERS.PLL_SYS_CTRL3_REG = GPREG->PLL_SYS_CTRL3_REG;
|
||||
ALL_REGISTERS.PLL_SYS_TEST_REG = GPREG->PLL_SYS_TEST_REG;
|
||||
|
||||
// CACHE
|
||||
ALL_REGISTERS.CACHE_CTRL1_REG = CACHE->CACHE_CTRL1_REG;
|
||||
ALL_REGISTERS.CACHE_LNSIZECFG_REG = CACHE->CACHE_LNSIZECFG_REG;
|
||||
ALL_REGISTERS.CACHE_ASSOCCFG_REG = CACHE->CACHE_ASSOCCFG_REG;
|
||||
ALL_REGISTERS.CACHE_CTRL2_REG = CACHE->CACHE_CTRL2_REG;
|
||||
ALL_REGISTERS.CACHE_CTRL3_REG = CACHE->CACHE_CTRL3_REG;
|
||||
ALL_REGISTERS.CACHE_MRM_HITS_REG = CACHE->CACHE_MRM_HITS_REG;
|
||||
ALL_REGISTERS.CACHE_MRM_MISSES_REG = CACHE->CACHE_MRM_MISSES_REG;
|
||||
ALL_REGISTERS.CACHE_MRM_CTRL_REG = CACHE->CACHE_MRM_CTRL_REG;
|
||||
ALL_REGISTERS.CACHE_MRM_TINT_REG = CACHE->CACHE_MRM_TINT_REG;
|
||||
ALL_REGISTERS.CACHE_MRM_THRES_REG = CACHE->CACHE_MRM_THRES_REG;
|
||||
ALL_REGISTERS.SWD_RESET_REG = CACHE->SWD_RESET_REG;
|
||||
|
||||
#endif // DEFAULT_REGISTER_DEBUG
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "debug_print.h"
|
||||
#include "hw_watchdog.h"
|
||||
#include "sdk_defs.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static void prv_handle_fault(void) {
|
||||
#if NO_WATCHDOG
|
||||
while (1) {};
|
||||
#else
|
||||
NVIC_SystemReset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void HardFault_HandlerC(uint32_t *fault_args) {
|
||||
debug_print_str("Hard fault");
|
||||
prv_handle_fault();
|
||||
}
|
||||
|
||||
void UsageFault_HandlerC(uint32_t *fault_args) {
|
||||
debug_print_str("Usage fault");
|
||||
prv_handle_fault();
|
||||
}
|
||||
|
||||
void BusFault_HandlerC(uint32_t *fault_args) {
|
||||
debug_print_str("Bus fault");
|
||||
prv_handle_fault();
|
||||
}
|
||||
|
||||
void MemManag_HandlerC(uint32_t *fault_args) {
|
||||
debug_print_str("MemManag fault");
|
||||
prv_handle_fault();
|
||||
}
|
||||
|
||||
void NMI_HandlerC(uint32_t *fault_args) {
|
||||
debug_print_str("NMI Handler");
|
||||
prv_handle_fault();
|
||||
}
|
207
src/bluetooth-fw/da1468x/controller/boot/src/host_transport.c
Normal file
207
src/bluetooth-fw/da1468x/controller/boot/src/host_transport.c
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "da1468x_mem_map.h"
|
||||
|
||||
#include "debug_print.h"
|
||||
#include "hw_gpio.h"
|
||||
#include "hw_spi.h"
|
||||
|
||||
#include "board.h"
|
||||
#include "host_transport.h"
|
||||
#include "util/crc32.h"
|
||||
|
||||
// Dialog SPI bootloader implementation
|
||||
//
|
||||
// See following doc for protocol specification:
|
||||
// https://docs.google.com/document/d/1PrnTsDhBZYsrlxa9-6OzdoEvtE50uVkSTufqEdQ2yWw/
|
||||
|
||||
// The arm vector table has a default size of 0x40 bytes. The rest of the space
|
||||
// is variable depending on the number of IRQn interrupt handlers implemented
|
||||
// by the platform. For the dialog part 32 IRQs are provided. There is also a
|
||||
// patch area used by the dialog BT ROM which must be loaded and comes directly
|
||||
// after the vector table, it's 128 bytes
|
||||
#define IVT_TABLE_SIZE (0x40 + 32 * 4 + 0x80)
|
||||
static uint8_t s_vector_table[IVT_TABLE_SIZE];
|
||||
|
||||
static volatile bool s_expected_bytes_received = false;
|
||||
static void prv_expect_byte_spi_int_cb(void *user_data, uint16_t transferred) {
|
||||
s_expected_bytes_received = true;
|
||||
debug_print_str_and_int("Bytes TX/RXed: ", transferred);
|
||||
}
|
||||
|
||||
// Interesting observation: The INT fires on writes once the data has been drained to the FIFO,
|
||||
// not when it actually gets drained.
|
||||
static void prv_write_or_read_bytes(void *byte_buffer, int num_bytes, bool do_write) {
|
||||
s_expected_bytes_received = false;
|
||||
if (do_write) {
|
||||
hw_spi_write_buf(HOST_SPI->spi.peripheral,
|
||||
byte_buffer, num_bytes, prv_expect_byte_spi_int_cb, NULL);
|
||||
} else {
|
||||
hw_spi_read_buf(HOST_SPI->spi.peripheral,
|
||||
byte_buffer, num_bytes, prv_expect_byte_spi_int_cb, NULL);
|
||||
}
|
||||
|
||||
hw_gpio_set_active(HOST_SPI->mcu_int.port, HOST_SPI->mcu_int.pin);
|
||||
// Probably not necessary but add a little delay so its easier to catch INT
|
||||
// on logic analyzer
|
||||
for (volatile int delay = 0; delay < 50; delay++) { }
|
||||
|
||||
while (!s_expected_bytes_received) { };
|
||||
hw_gpio_set_inactive(HOST_SPI->mcu_int.port, HOST_SPI->mcu_int.pin);
|
||||
}
|
||||
|
||||
static void prv_handle_hi_command(void) {
|
||||
debug_print_str("HI CMD\n");
|
||||
uint8_t response[] = { 'H', 'E', 'R', 'E' };
|
||||
prv_write_or_read_bytes(response, sizeof(response), true);
|
||||
}
|
||||
|
||||
static void prv_send_crc_of_mem_region(void *data_start, size_t len) {
|
||||
uint32_t crc = CRC32_INIT;
|
||||
crc = crc32(crc, data_start, len);
|
||||
|
||||
prv_write_or_read_bytes((uint8_t *)&crc, sizeof(crc), true);
|
||||
debug_print_str_and_int("Computed CRC: ", (int)crc);
|
||||
}
|
||||
|
||||
static void prv_handle_load_data_command(void) {
|
||||
debug_print_str("LD CMD\n");
|
||||
|
||||
struct __attribute__((packed)) {
|
||||
uint32_t copy_address;
|
||||
uint32_t length;
|
||||
} send_data_cmd_payload = { };
|
||||
|
||||
prv_write_or_read_bytes(&send_data_cmd_payload, sizeof(send_data_cmd_payload), false);
|
||||
|
||||
debug_print_str_and_int(" Address:", send_data_cmd_payload.copy_address);
|
||||
debug_print_str_and_int(" Length:", send_data_cmd_payload.length);
|
||||
|
||||
prv_write_or_read_bytes((uint8_t*)send_data_cmd_payload.copy_address,
|
||||
send_data_cmd_payload.length, false);
|
||||
|
||||
prv_send_crc_of_mem_region(
|
||||
(void *)send_data_cmd_payload.copy_address, send_data_cmd_payload.length);
|
||||
}
|
||||
|
||||
static void prv_handle_vector_table_update_command(void) {
|
||||
debug_print_str("VT CMD\n");
|
||||
|
||||
// reset vector table
|
||||
memset(s_vector_table, 0x0, sizeof(s_vector_table));
|
||||
|
||||
uint8_t number_of_entries = 0;
|
||||
prv_write_or_read_bytes(&number_of_entries, sizeof(number_of_entries), false);
|
||||
|
||||
size_t vector_table_copy_size = number_of_entries * 4;
|
||||
prv_write_or_read_bytes(s_vector_table, vector_table_copy_size, false);
|
||||
|
||||
prv_send_crc_of_mem_region(&s_vector_table[0], vector_table_copy_size);
|
||||
}
|
||||
|
||||
static void prv_handle_reboot_command(void) {
|
||||
debug_print_str("RT CMD\n");
|
||||
const uint32_t vt_start_addr = DATA_RAM_BASE_ADDRESS;
|
||||
|
||||
// We are about to overwrite the vector table. Disable interrupts while
|
||||
// this is taking place
|
||||
__disable_irq();
|
||||
|
||||
// Interrupt Clear Enable Register:
|
||||
NVIC->ICER[0] = ~0;
|
||||
// Interrupt Clear Pending Register:
|
||||
NVIC->ICPR[0] = ~0;
|
||||
|
||||
for (size_t i = 0; i < sizeof(s_vector_table); i++) {
|
||||
*(uint8_t *)(vt_start_addr + i) = s_vector_table[i];
|
||||
}
|
||||
|
||||
NVIC_SystemReset();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void prv_bootloader_loop(void) {
|
||||
debug_print_str("Beginning Bootloader Loop\n");
|
||||
while (1) {
|
||||
uint8_t cmd[2] = { };
|
||||
prv_write_or_read_bytes(cmd, sizeof(cmd), false);
|
||||
|
||||
if (cmd[0] == 'H' && cmd[1] == 'I') {
|
||||
prv_handle_hi_command();
|
||||
} else if (cmd[0] == 'L' && cmd[1] == 'D') {
|
||||
prv_handle_load_data_command();
|
||||
} else if (cmd[0] == 'V' && cmd[1] == 'T') {
|
||||
prv_handle_vector_table_update_command();
|
||||
} else if (cmd[0] == 'R' && cmd[1] == 'T') {
|
||||
prv_handle_reboot_command();
|
||||
} else {
|
||||
debug_print_str("Unknown CMD:");
|
||||
debug_print_str_and_int(" Byte 0:", cmd[0]);
|
||||
debug_print_str_and_int(" Byte 1:", cmd[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_configure_pins_for_spi_transfer(void) {
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.cs.port, HOST_SPI->spi.cs.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.cs.function);
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.cs_2.port, HOST_SPI->spi.cs_2.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.cs_2.function);
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.clk.port, HOST_SPI->spi.clk.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.clk.function);
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.mosi_di.port, HOST_SPI->spi.mosi_di.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.mosi_di.function);
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.miso_do.port, HOST_SPI->spi.miso_do.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.miso_do.function);
|
||||
|
||||
hw_gpio_configure_pin(HOST_SPI->mcu_int.port, HOST_SPI->mcu_int.pin,
|
||||
HW_GPIO_MODE_OUTPUT, HOST_SPI->mcu_int.function, false);
|
||||
}
|
||||
|
||||
static void prv_configure_spi_peripheral(void) {
|
||||
spi_config config = {
|
||||
.cs_pad = { 0, 0 },
|
||||
.word_mode = HW_SPI_WORD_8BIT,
|
||||
.smn_role = HW_SPI_MODE_SLAVE,
|
||||
.phase_mode = HW_SPI_PHA_MODE_0,
|
||||
.polarity_mode = HW_SPI_POL_LOW,
|
||||
.mint_mode = HW_SPI_MINT_DISABLE, // we are not using this feature
|
||||
.xtal_freq = 0,
|
||||
.fifo_mode = HW_SPI_FIFO_RX_TX,
|
||||
.disabled = 0,
|
||||
#ifdef HW_SPI_DMA_SUPPORT
|
||||
.use_dma = 1,
|
||||
.rx_dma_channel = HOST_SPI_RX_DMA_CHANNEL,
|
||||
.tx_dma_channel = HOST_SPI_TX_DMA_CHANNEL,
|
||||
#endif
|
||||
};
|
||||
|
||||
hw_spi_init(HOST_SPI->spi.peripheral, &config);
|
||||
}
|
||||
|
||||
void host_transport_begin(void) {
|
||||
prv_configure_pins_for_spi_transfer();
|
||||
|
||||
prv_configure_spi_peripheral();
|
||||
|
||||
prv_bootloader_loop();
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
36
src/bluetooth-fw/da1468x/controller/boot/src/passert.c
Normal file
36
src/bluetooth-fw/da1468x/controller/boot/src/passert.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <util/attributes.h>
|
||||
|
||||
#include "debug_print.h"
|
||||
|
||||
#include "sdk_defs.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//! Implementation for the bootloader.
|
||||
//! Because ASSERT_ERROR() and ASSERT_WARNING() macros in sdk_defs.h use this function,
|
||||
//! we need an implementation for the bootloader as well.
|
||||
NORETURN passert_failed_no_message(void) {
|
||||
debug_print_str_and_int("ASRT", (int)__builtin_return_address(0));
|
||||
#if NO_WATCHDOG
|
||||
while (1) {};
|
||||
#else
|
||||
NVIC_SystemReset();
|
||||
#endif
|
||||
__builtin_unreachable();
|
||||
}
|
79
src/bluetooth-fw/da1468x/controller/boot/wscript
Normal file
79
src/bluetooth-fw/da1468x/controller/boot/wscript
Normal file
|
@ -0,0 +1,79 @@
|
|||
import sys
|
||||
|
||||
import waftools.objcopy
|
||||
|
||||
from resources.types.resource_definition import ResourceDefinition
|
||||
from resources.types.resource_object import ResourceObject
|
||||
|
||||
CUSTOM_CONFIG_H_PATH = 'config/custom_config_boot.h'
|
||||
|
||||
|
||||
def build(bld):
|
||||
bld.env.append_value('DEFINES', ['BLE_BOOTLOADER=1'])
|
||||
|
||||
sys.path.append(bld.path.find_node('../..').abspath())
|
||||
from dialog_waf import get_sdk_node, collect_sdk_sources, generate_mem_ld
|
||||
|
||||
mem_ld_node = generate_mem_ld(bld, CUSTOM_CONFIG_H_PATH)
|
||||
|
||||
# Collect source files:
|
||||
source_dirs = ['src', '../common/src']
|
||||
sources = sum([bld.path.ant_glob('%s/**/*.c' % d)
|
||||
for d in source_dirs], [])
|
||||
sdk_sources = [
|
||||
'sdk/bsp/startup/vector_table.S',
|
||||
'sdk/bsp/startup/startup_ARMCM0.S',
|
||||
'sdk/bsp/startup/system_ARMCM0.c',
|
||||
'sdk/bsp/startup/config.c',
|
||||
'sdk/bsp/peripherals/src/hw_cpm.c',
|
||||
'sdk/bsp/peripherals/src/hw_dma.c',
|
||||
'sdk/bsp/peripherals/src/hw_gpio.c',
|
||||
'sdk/bsp/peripherals/src/hw_spi.c',
|
||||
'sdk/bsp/peripherals/src/hw_uart.c',
|
||||
'sdk/bsp/peripherals/src/hw_watchdog.c',
|
||||
|
||||
# Used by system_ARMCM0.c:
|
||||
'sdk/bsp/peripherals/src/hw_otpc.c',
|
||||
'sdk/bsp/peripherals/src/sys_tcs.c',
|
||||
]
|
||||
sources.extend(collect_sdk_sources(bld, sdk_sources))
|
||||
|
||||
linkflags = ['-Wl,-Map,bt_da14681_boot.map',
|
||||
'-Wl,--build-id=sha1']
|
||||
|
||||
# Includes:
|
||||
includes = ['include', '../common/include', '../../include', 'config']
|
||||
elf_node = bld.path.get_bld().make_node('bt_da14681_boot.elf')
|
||||
bld.program(features='c asm cprogram',
|
||||
source=sources,
|
||||
includes=includes,
|
||||
target=elf_node,
|
||||
lib=['gcc'],
|
||||
linkflags=linkflags,
|
||||
ldscript=mem_ld_node,
|
||||
inject_include_files=[CUSTOM_CONFIG_H_PATH],
|
||||
use=['dialog_sdk_includes', 'dialog_board_boot',
|
||||
'pblibc-cm0', 'libutil_includes', 'libutil-cm0'])
|
||||
|
||||
bld.add_manual_dependency(elf_node, mem_ld_node)
|
||||
|
||||
bin_node = elf_node.change_ext('.bin')
|
||||
bld(rule=waftools.objcopy.objcopy_bin, source=elf_node, target=bin_node)
|
||||
|
||||
def create_bt_patch_resource(task):
|
||||
bin_data = task.inputs[0].read('rb')
|
||||
reso = ResourceObject(ResourceDefinition('raw', 'BT_BOOT_IMAGE', None,
|
||||
storage=task.generator.storage), bin_data)
|
||||
reso.dump(task.outputs[0])
|
||||
|
||||
reso_node = elf_node.change_ext('.bin.reso')
|
||||
bld(rule=create_bt_patch_resource, source=bin_node, target=reso_node,
|
||||
storage=bld.get_bluetooth_fw_storage())
|
||||
|
||||
bld.DYNAMIC_RESOURCES.append(reso_node)
|
||||
|
||||
def configure(ctx):
|
||||
pass
|
||||
|
||||
|
||||
# vim:filetype=python
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ad_gpadc.h>
|
||||
#include <ad_i2c.h>
|
||||
#include <ad_spi.h>
|
||||
#include <ad_uart.h>
|
||||
|
||||
#include "board.h"
|
||||
|
||||
SPI_BUS(SPI1)
|
||||
SPI_SLAVE_TO_EXT_MASTER(SPI1, PEBBLE_HOST, CONFIG_SPI_IGNORE_CS,
|
||||
CONFIG_SPI_WORD_MODE, CONFIG_SPI_POL_MODE,
|
||||
CONFIG_SPI_PHASE_MODE, CONFIG_SPI_DMA_CHANNEL);
|
||||
SPI_BUS_END
|
||||
|
||||
#if dg_configGPADC_ADAPTER
|
||||
|
||||
/*
|
||||
* Define sources connected to GPADC
|
||||
*/
|
||||
|
||||
GPADC_SOURCE(TEMP_SENSOR, HW_GPADC_CLOCK_INTERNAL, HW_GPADC_INPUT_MODE_SINGLE_ENDED,
|
||||
HW_GPADC_INPUT_SE_TEMPSENS, 5, false, HW_GPADC_OVERSAMPLING_1_SAMPLE,
|
||||
HW_GPADC_INPUT_VOLTAGE_UP_TO_1V2)
|
||||
|
||||
#endif /* dg_configGPADC_ADAPTER */
|
0
src/bluetooth-fw/da1468x/controller/main/include/.gitignore
vendored
Normal file
0
src/bluetooth-fw/da1468x/controller/main/include/.gitignore
vendored
Normal file
43
src/bluetooth-fw/da1468x/controller/main/include/advert.h
Normal file
43
src/bluetooth-fw/da1468x/controller/main/include/advert.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "advert_state.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ble_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct ble_evt_gap_adv_completed ble_evt_gap_adv_completed_t;
|
||||
typedef struct BLEAdData BLEAdData;
|
||||
|
||||
ble_error_t advert_set_interval(uint16_t min_slots, uint16_t max_slots);
|
||||
|
||||
//! @return The current state, returned for debugging/logging purposes.
|
||||
//! It's possible this is not AdvertState_Running, in case advertising was paused or is still
|
||||
//! stopping.
|
||||
AdvertState advert_enable(void);
|
||||
|
||||
void advert_set_data(const BLEAdData *ad_data);
|
||||
|
||||
void advert_disable(void);
|
||||
|
||||
void advert_handle_completed(const ble_evt_gap_adv_completed_t *evt);
|
||||
|
||||
void advert_init(void);
|
27
src/bluetooth-fw/da1468x/controller/main/include/aes.h
Normal file
27
src/bluetooth-fw/da1468x/controller/main/include/aes.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define AES_128_BLOCK_SIZE (16)
|
||||
#define AES_128_KEY_SIZE (16)
|
||||
|
||||
bool aes_128_encrypt_block(const uint8_t key[AES_128_KEY_SIZE],
|
||||
const uint8_t plain_text_block[AES_128_BLOCK_SIZE],
|
||||
uint8_t cipher_text_block_out[AES_128_BLOCK_SIZE]);
|
26
src/bluetooth-fw/da1468x/controller/main/include/ble_task.h
Normal file
26
src/bluetooth-fw/da1468x/controller/main/include/ble_task.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <bluetooth/init.h>
|
||||
|
||||
void ble_task_assert_is_executing_on_ble_task(void);
|
||||
|
||||
//! Start the Dialog BLE stack with the given config and block until it's up and running.
|
||||
void ble_task_init(const BTDriverConfig *config);
|
23
src/bluetooth-fw/da1468x/controller/main/include/chip_id.h
Normal file
23
src/bluetooth-fw/da1468x/controller/main/include/chip_id.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dialog_chip_id.h"
|
||||
#include "util/attributes.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
bool dialog_chip_id_copy(DialogChipID *chip_id_out);
|
112
src/bluetooth-fw/da1468x/controller/main/include/connection.h
Normal file
112
src/bluetooth-fw/da1468x/controller/main/include/connection.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gatt_wrapper_types.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct Connection Connection;
|
||||
typedef struct BTDeviceInternal BTDeviceInternal;
|
||||
typedef struct BTDeviceAddress BTDeviceAddress;
|
||||
typedef struct BleConnectionParams BleConnectionParams;
|
||||
typedef struct PPoGATTWorkAroundState PPoGATTWorkAroundState;
|
||||
typedef void (*ConnectionForEachCallback)(Connection *connection, void *data);
|
||||
|
||||
// Call once on boot before doing anything with Bluetooth
|
||||
void connection_module_init(void);
|
||||
|
||||
// Call every time a BT connection is created/destroyed. These ops should
|
||||
// be driven from events received by the ble_task
|
||||
//! @param initial_addr The remote address at the time of connection establishment. See notes
|
||||
//! with the initial_addr field in the Connection struct.
|
||||
//! @param local_addr The local/own address that was used to advertise at the time of connection
|
||||
//! establishment.
|
||||
Connection *connection_create(uint16_t conn_idx, const BTDeviceInternal *initial_addr,
|
||||
const BTDeviceAddress *local_addr, const BleConnectionParams *params);
|
||||
void connection_destroy(Connection *connection);
|
||||
|
||||
// Returns true if the pointer given is in our list of connections
|
||||
bool connection_is_valid(Connection *connection);
|
||||
|
||||
// Enqueues a Gatt Operation to the list of outstanding Gatt Operations in the Connection object.
|
||||
void connection_enqueue_gatt_op(Connection *connection, uintptr_t context_ref,
|
||||
GattRespDest resp_dest, GattOpType op_type);
|
||||
// Dequeues a Gatt Operation from the list of outstanding Gatt Operations in the Connection object.
|
||||
// Returns true if the object was successfully dequeued and false if there are no operations known
|
||||
// to be in progress
|
||||
bool connection_dequeue_gatt_op(Connection *connection, uintptr_t *context_ref,
|
||||
GattRespDest *resp_dest, GattOpType expected_op_type);
|
||||
// Pops the most recent Gatt Operation appended to the list.
|
||||
bool connection_pop_gatt_op(Connection *connection);
|
||||
|
||||
//
|
||||
// Retrieve Connections
|
||||
//
|
||||
|
||||
Connection *connection_by_idx(uint16_t conn_idx);
|
||||
Connection *connection_by_idx_check(uint16_t conn_idx);
|
||||
Connection *connection_by_address(const BTDeviceInternal *addr_buf);
|
||||
Connection *connection_by_address_check(const BTDeviceInternal *addr_out);
|
||||
|
||||
// @note: internal lock will be held for the duration of this call
|
||||
void connection_for_each(ConnectionForEachCallback cb, void *data);
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
uint16_t connection_get_idx(Connection *connection);
|
||||
//! If valid, gets the updated_addr of the connection, or otherwise the initial_addr.
|
||||
void connection_get_address(const Connection *connection, BTDeviceInternal *addr_buf);
|
||||
//! Gets the local/own address that was used to advertise at the time of connection establishment.
|
||||
void connection_get_local_address(Connection *connection, BTDeviceAddress *addr_buf);
|
||||
void connection_get_address_by_idx_check(uint16_t conn_idx, BTDeviceInternal *addr_out);
|
||||
void connection_get_conn_params(const Connection *connection,
|
||||
BleConnectionParams *params_out);
|
||||
bool connection_is_subscribed_to_gatt_mtu_notifications(const Connection *connection);
|
||||
bool connection_is_gateway(Connection *connection);
|
||||
bool connection_is_subscribed_to_connection_status_notifications(const Connection *connection);
|
||||
bool connection_is_subscribed_to_conn_param_notifications(const Connection *connection);
|
||||
bool connection_should_pin_address(const Connection *connection);
|
||||
bool connection_should_auto_accept_re_pairing(const Connection *connection);
|
||||
bool connection_is_reversed_ppogatt_enabled(const Connection *connection);
|
||||
uint8_t connection_get_last_pairing_result(uint16_t conn_idx);
|
||||
PPoGATTWorkAroundState *connection_get_ppogatt_wa_state(Connection *connection);
|
||||
|
||||
//
|
||||
// Setters - Sets the requested value provided connection_is_valid(connection) returns true
|
||||
//
|
||||
|
||||
void connection_set_gateway(Connection *connection, bool is_gateway);
|
||||
void connection_set_subscribed_to_connection_status_notifications(
|
||||
Connection *connection, bool is_subscribed);
|
||||
void connection_set_subscribed_to_gatt_mtu_notifications(
|
||||
Connection *connection, bool is_subscribed);
|
||||
void connection_set_subscribed_to_conn_param_notifications(
|
||||
Connection *connection, bool is_subscribed);
|
||||
void connection_set_conn_params(Connection *connection, const BleConnectionParams *params);
|
||||
|
||||
void connection_update_address(Connection *connection, const BTDeviceInternal *updated_addr);
|
||||
void connection_set_should_pin_address(Connection *connection, bool should_pin_address);
|
||||
void connection_set_should_auto_accept_re_pairing(Connection *connection,
|
||||
bool should_auto_accept_re_pairing);
|
||||
void connection_set_reversed_ppogatt_enabled(Connection *connection,
|
||||
bool is_reversed_ppogatt_enabled);
|
||||
void connection_set_last_pairing_result(uint16_t conn_idx, uint8_t result);
|
||||
void connection_set_ppogatt_wa_state(Connection *connection, PPoGATTWorkAroundState *state);
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "connection.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <bluetooth/gap_le_connect.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct GattOperation GattOperation;
|
||||
|
||||
typedef enum {
|
||||
ConnectionFlag_IsSubscribedToConnectionStatusNotifications = 0,
|
||||
ConnectionFlag_IsSubscribedToGattMtuNotifications,
|
||||
ConnectionFlag_IsSubscribedToConnParamNotifications,
|
||||
ConnectionFlag_ShouldPinAddress,
|
||||
//! @note The flag in the Connection struct is only relevant during the pairing process.
|
||||
//! Once bonded, the value gets stored in the bonding list (storage.c / device_t).
|
||||
ConnectionFlag_ShouldAutoAcceptRePairing,
|
||||
//! @note The flag in the Connection struct is only relevant during the pairing process.
|
||||
//! Once bonded, the value gets stored in the bonding list (storage.c / device_t).
|
||||
ConnectionFlag_IsReversedPPoGATTEnabled,
|
||||
ConnectionFlagCount,
|
||||
} ConnectionFlag;
|
||||
|
||||
typedef struct Connection {
|
||||
ListNode node;
|
||||
|
||||
uint16_t conn_idx;
|
||||
|
||||
//! Remote address at the time the connection was established.
|
||||
//! This can be the actual connection address OR the resolved address.
|
||||
//! The former is the case for unbonded connections (including yet-to-be-bonded connections).
|
||||
//! The latter is the case for bonded reconnections: if we are bonded (have an IRK) and the
|
||||
//! underlying stack was able to resolve the address before passing the connection establishment
|
||||
//! event to ble_task.
|
||||
BTDeviceInternal initial_addr;
|
||||
|
||||
//! Updated remote address. In case the address got resolved some time after connecting,
|
||||
//! for example after pairing happened, the resolved address will be stored in this field.
|
||||
//! The initial address will stay stored in initial_addr and the resolved address will be set in
|
||||
//! this field. For bonded reconnections, this field will not be used (remain all zeroes).
|
||||
bool has_updated_addr;
|
||||
BTDeviceInternal updated_addr;
|
||||
|
||||
//! Local address at the time the connection was created.
|
||||
BTDeviceAddress local_addr;
|
||||
|
||||
GattOperation *gatt_op_list;
|
||||
|
||||
bool is_gateway;
|
||||
|
||||
//! @see pebble_pairing_service.c
|
||||
uint32_t flags;
|
||||
BleConnectionParams conn_params;
|
||||
uint8_t last_pairing_result;
|
||||
|
||||
//! @see ppogatt_emulated_server_wa.c
|
||||
PPoGATTWorkAroundState *ppogatt_wa_state;
|
||||
} Connection;
|
||||
|
||||
_Static_assert((sizeof(((Connection *)0)->flags) * 8) >= ConnectionFlagCount,
|
||||
"Bitfield 'flags' full!");
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
//! This is a debug utlility that makes it easy to toggle gpios to debug paths which are very
|
||||
//! sensitive to timing.
|
||||
//! To use it simply:
|
||||
//! 1) Call debug_gpio_init() before you want to track things
|
||||
//! 2) Call debug_gpio_toggle() to flip the gpio state
|
||||
|
||||
//! Initializs DEBUG_GPIOs defined in board config and drives them all low
|
||||
void debug_gpio_init(void);
|
||||
|
||||
//! Toggles the debug_gpio from it's current state
|
||||
void debug_gpio_toggle(int debug_gpio_num);
|
||||
|
||||
//! Drives the state of the specified debug gpio
|
||||
void debug_gpio_set_active(int debug_gpio, bool is_active);
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void debug_reboot_reason_print(void);
|
21
src/bluetooth-fw/da1468x/controller/main/include/debugger.h
Normal file
21
src/bluetooth-fw/da1468x/controller/main/include/debugger.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if NO_WATCHDOG
|
||||
void debugger_await(void);
|
||||
#endif
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hc_protocol/hc_endpoint_analytics.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
//! Called once during initialization.
|
||||
void analytics_init(void);
|
||||
|
||||
//! Called every time the Analytics module should reset its state and set all node values to 0.
|
||||
//! Typically called after all analytics have been flushed out to the Main FW (every hour or so).
|
||||
void analytics_reset_nodes(void);
|
||||
|
||||
//! Set a scalar metric
|
||||
//! @param metric The metric to set
|
||||
//! @param val The new value
|
||||
void analytics_set(DialogAnalyticsMetric metric, uint32_t val);
|
||||
|
||||
//! Increment a metric by 1
|
||||
//! @param metric The metric to increment
|
||||
void analytics_inc(DialogAnalyticsMetric metric);
|
||||
|
||||
//! Increment a metric
|
||||
//! @param metric The metric to increment
|
||||
//! @param amount The amount to increment by
|
||||
void analytics_add(DialogAnalyticsMetric metric, uint32_t amount);
|
||||
|
||||
//! Starts a stopwatch that integrates a "rate of things" over time.
|
||||
//! @param metric The metric of the stopwatch to start
|
||||
void analytics_stopwatch_start(DialogAnalyticsMetric metric);
|
||||
|
||||
//! Starts a stopwatch that integrates a "rate of things" over time.
|
||||
//! @param metric The metric for which to start the stopwatch.
|
||||
//! @param count_per_second The rate in number of things per second to count.
|
||||
//! For example, if you want to measure "bytes transferred" over time and know the transfer speed
|
||||
//! is 1024 bytes per second, then you would pass in 1024 as count_per_second.
|
||||
void analytics_stopwatch_start_at_rate(DialogAnalyticsMetric metric, uint32_t count_per_second);
|
||||
|
||||
//! Stops a stopwatch
|
||||
//! @param metric The metric of the stopwatch
|
||||
void analytics_stopwatch_stop(DialogAnalyticsMetric metric);
|
||||
|
||||
//
|
||||
// Consumer API
|
||||
//
|
||||
|
||||
typedef void (*AnalyticsEachCallback)(DialogAnalyticsMetric metric, uint32_t value, void *context);
|
||||
|
||||
void analytics_each(AnalyticsEachCallback cb, void *context);
|
23
src/bluetooth-fw/da1468x/controller/main/include/die.h
Normal file
23
src/bluetooth-fw/da1468x/controller/main/include/die.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/attributes.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
NORETURN reset_due_to_software_failure(void);
|
25
src/bluetooth-fw/da1468x/controller/main/include/dis_impl.h
Normal file
25
src/bluetooth-fw/da1468x/controller/main/include/dis_impl.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct DisInfo DisInfo;
|
||||
|
||||
void device_information_service_init(const DisInfo *dis_info);
|
||||
bool device_information_service_register(uint16_t start_hdl);
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "connection.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
|
||||
void gap_le_device_name_handle_set(const char *name);
|
||||
|
||||
void gap_le_device_name_handle_request(const BTDeviceInternal *addr);
|
||||
void gap_le_device_name_handle_request_all(void);
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct ble_evt_gattc_browse_svc_t ble_evt_gattc_browse_svc_t;
|
||||
typedef struct ble_evt_gattc_browse_completed_t ble_evt_gattc_browse_completed_t;
|
||||
typedef struct ble_evt_gattc_indication_t ble_evt_gattc_indication_t;
|
||||
|
||||
void gatt_client_discovery_process_service(const ble_evt_gattc_browse_svc_t *service);
|
||||
void gatt_client_discovery_handle_complete(const ble_evt_gattc_browse_completed_t *complete_event);
|
||||
|
||||
bool gatt_client_discovery_filter_service_changed(const ble_evt_gattc_indication_t *evt);
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define DEVICE_INFORMATION_SERVICE_EXPECTED_ATT_STARTING_HANDLE (12)
|
||||
#define HRM_SERVICE_EXPECTED_ATT_STARTING_HANDLE (35)
|
||||
#define HRM_SERVICE_EXPECTED_ATT_ENDING_HANDLE (42)
|
||||
#define PEBBLE_PAIRING_SERVICE_EXPECTED_ATT_STARTING_HANDLE (23)
|
||||
#define PEBBLE_PPOGATT_SERVICE_EXPECTED_ATT_STARTING_HANDLE (0xE000)
|
||||
|
||||
typedef struct BTDriverConfig BTDriverConfig;
|
||||
|
||||
void gatt_local_services_init(const BTDriverConfig *config);
|
||||
void gatt_local_services_register(void);
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ble_gattc.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum GattReqSource {
|
||||
GattReqSourceHost,
|
||||
GattReqSourceController,
|
||||
} GattReqSource;
|
||||
|
||||
// Gatt wrappers that call the underlying Dialog Gatt API's.
|
||||
// Wrapped because it also keeps track of the context_ref in a `Connection` object
|
||||
ble_error_t gatt_wrapper_read(uint16_t conn_idx, uint16_t handle, uintptr_t context_ref,
|
||||
GattReqSource source);
|
||||
|
||||
ble_error_t gatt_wrapper_read_by_uuid(uint16_t conn_idx, uint16_t start_h, uint16_t end_h,
|
||||
const att_uuid_t *uuid, uintptr_t context_ref,
|
||||
GattReqSource source);
|
||||
|
||||
ble_error_t gatt_wrapper_write(uint16_t conn_idx, uint16_t handle, uint16_t length,
|
||||
const uint8_t *value, uintptr_t context_ref, GattReqSource source);
|
||||
|
||||
ble_error_t gatt_wrapper_write_no_resp(uint16_t conn_idx, uint16_t handle, uint16_t length,
|
||||
const uint8_t *value);
|
||||
|
||||
// Gatt wrapper callback -- for use when calling gatt_wrapper_read|write from the controller.
|
||||
// Pass the callback fn pointer in context_ref.
|
||||
typedef void (*gatt_wrapper_read_cb)(const ble_evt_gattc_read_completed_t *evt);
|
||||
typedef void (*gatt_wrapper_write_cb)(const ble_evt_gattc_write_completed_t *evt);
|
||||
|
||||
//
|
||||
// Event Handlers (from ble_task)
|
||||
//
|
||||
void gatt_wrapper_handle_read_completed(const ble_evt_gattc_read_completed_t *evt);
|
||||
|
||||
void gatt_wrapper_handle_write_completed(const ble_evt_gattc_write_completed_t *evt);
|
||||
|
||||
void gatt_wrapper_handle_notification(const ble_evt_gattc_notification_t *evt);
|
||||
|
||||
void gatt_wrapper_handle_indication(const ble_evt_gattc_indication_t *evt);
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/list.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum GattOpType {
|
||||
GattOpType_Read,
|
||||
GattOpType_Write
|
||||
} GattOpType;
|
||||
|
||||
typedef enum GattRespDest {
|
||||
GattRespDestNone,
|
||||
GattRespDestHost,
|
||||
GattRespDestController,
|
||||
} GattRespDest;
|
||||
|
||||
typedef struct GattOperation {
|
||||
ListNode node;
|
||||
uintptr_t object_ref;
|
||||
GattRespDest resp_dest;
|
||||
GattOpType op_type;
|
||||
} GattOperation;
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ble_gattc.h"
|
||||
|
||||
void hc_endpoint_gap_service_gattc_read_complete(const ble_evt_gattc_read_completed_t *evt);
|
||||
void hc_endpoint_gap_service_gattc_discover_char(const ble_evt_gattc_discover_char_t *evt);
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ad_ble.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//! Injects an HCI command into Dialog's ROM even if the chip is not configured
|
||||
//! with BLE_STACK_PASSTHROUGH_MODE & BLE_EXTERNAL_HOST. This should be used
|
||||
//! conservatively as it effectively bypasses the dialog SDK stack. It does however
|
||||
//! allow one to add support for things not yet exported as an SDK API (i.e BLE
|
||||
//! Direct Test Modes)
|
||||
bool hci_rom_passthrough_send_cmd(
|
||||
uint16_t ogf, uint16_t ocf, const uint8_t *param_buf, uint8_t param_length);
|
||||
|
||||
//! Commands issued with hci_rom_passthrough_send_cmd() will bubble up
|
||||
//! HCI event(s). This callback is invoked from the app task to handle these events
|
||||
void hci_rom_passthrough_handle_evt(hci_evt_msg_t *msg);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void host_transport_init_periph(void);
|
||||
void host_transport_init(void);
|
||||
|
||||
typedef enum {
|
||||
SCSPinFunction_Wakeup_GPIO,
|
||||
SCSPinFunction_SPI_CS,
|
||||
} SCSPinFunction;
|
||||
|
||||
// Used by core dump.
|
||||
void host_transport_configure_spi_scs_pin(SCSPinFunction function);
|
||||
void host_transport_set_mcu_int(bool is_ready_to_transact);
|
32
src/bluetooth-fw/da1468x/controller/main/include/hrm_impl.h
Normal file
32
src/bluetooth-fw/da1468x/controller/main/include/hrm_impl.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bluetooth/hrm_service.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void hrm_service_init(bool is_hrm_supported_and_enabled);
|
||||
|
||||
void hrm_service_register(uint16_t start_hdl);
|
||||
|
||||
void hrm_service_handle_measurement(const BleHrmServiceMeasurement *measurement,
|
||||
const BTDeviceInternal *permitted_devices,
|
||||
size_t num_permitted_devices);
|
||||
|
||||
void hrm_service_handle_enable(bool enable);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Defined with prefix of `kernel` to allow for code to be shared
|
||||
// between normal firmware the bluetooth firmware.
|
||||
void kernel_heap_init(void);
|
||||
void *kernel_malloc(size_t bytes);
|
||||
void *kernel_zalloc(size_t bytes);
|
||||
void *kernel_malloc_check(size_t bytes);
|
||||
void *kernel_zalloc_check(size_t bytes);
|
||||
void kernel_free(void *ptr);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "FreeRTOSConfig.h"
|
||||
#include "portable.h"
|
||||
|
||||
static inline TickType_t milliseconds_to_ticks(uint32_t milliseconds) {
|
||||
return ((uint64_t)milliseconds * configTICK_RATE_HZ) / 1000;
|
||||
}
|
||||
|
||||
static inline uint32_t ticks_to_milliseconds(TickType_t ticks) {
|
||||
return ((uint64_t)ticks * 1000) / configTICK_RATE_HZ;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <stdbool.h>
|
||||
|
||||
typedef struct BTDeviceAddress BTDeviceAddress;
|
||||
|
||||
//! @see bt_driver_set_local_address
|
||||
void local_addr_set(bool allow_cycling, const BTDeviceAddress *pinned_address);
|
||||
|
||||
void local_addr_handle_update(const BTDeviceAddress *updated_address);
|
||||
|
||||
void local_addr_handle_adverts_stopped(void);
|
||||
|
||||
void local_addr_handle_disconnection(void);
|
||||
|
||||
void local_addr_init(void);
|
21
src/bluetooth-fw/da1468x/controller/main/include/mcu.h
Normal file
21
src/bluetooth-fw/da1468x/controller/main/include/mcu.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
// Stub for the <mcu.h> header.
|
||||
// This will include the right MCU header and ARM core header:
|
||||
#include "sdk_defs.h"
|
||||
|
||||
#undef CMSIS_COMPATIBLE
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct Connection Connection;
|
||||
|
||||
void pebble_pairing_service_init(void);
|
||||
|
||||
//! Registers the Pebble Pairing Service with the ROM stack.
|
||||
//! This needs to happen every time the ROM stack modifies the device configuration, see
|
||||
//! notes in ble_gap.h.
|
||||
void pebble_pairing_service_register(uint16_t start_hdl);
|
||||
|
||||
void pebble_pairing_service_handle_status_change(Connection *connection, uint16_t conn_idx);
|
||||
|
||||
void pebble_pairing_service_handle_gatt_mtu_change(Connection *connection, uint16_t conn_idx);
|
||||
|
||||
void pebble_pairing_service_handle_conn_params_change(Connection *connection, uint16_t conn_idx);
|
21
src/bluetooth-fw/da1468x/controller/main/include/power.h
Normal file
21
src/bluetooth-fw/da1468x/controller/main/include/power.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void power_init(void);
|
||||
|
||||
void power_enter_hibernation(void);
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct Connection Connection;
|
||||
typedef struct HcProtocolMessage HcProtocolMessage;
|
||||
typedef struct PPoGATTWorkAroundState PPoGATTWorkAroundState;
|
||||
|
||||
void ppogatt_service_init(void);
|
||||
void ppogatt_service_register(uint16_t start_hdl);
|
||||
|
||||
bool ppogatt_emulated_server_handle_msg(uint16_t conn_idx, Connection *conn,
|
||||
const HcProtocolMessage *msg);
|
||||
void ppogatt_inject_emulated_ppogatt_service_if_needed(uint16_t conn_idx);
|
||||
|
||||
void ppogatt_enable_emulated_server_wa(void);
|
||||
void ppogatt_emulated_notify_phone_ppogatt_server_found(Connection *conn);
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <bluetooth/sm_types.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct BTDeviceAddress BTDeviceAddress;
|
||||
|
||||
//! Generates private resolvable address with a random prand.
|
||||
void pra_generate(BTDeviceAddress *address_out);
|
||||
|
||||
//! Generates private resolvable address with a given prand and IRK.
|
||||
void pra_generate_with_prand_and_irk(BTDeviceAddress *address_out,
|
||||
uint32_t prand, const SMIdentityResolvingKey *irk);
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// NOTE: We include the reboot reason in analytics and the tools we use to analyze the analytics are
|
||||
// dependent on the position and ordering of these enumerated values. To keep the analysis tools
|
||||
// simpler, it is best to keep these enums in the same order and add new ones to the end.
|
||||
typedef enum {
|
||||
RebootReasonCode_Unknown = 0,
|
||||
RebootReasonCode_Shutdown,
|
||||
RebootReasonCode_Watchdog,
|
||||
RebootReasonCode_Assert,
|
||||
RebootReasonCode_StackOverflow,
|
||||
RebootReasonCode_HardFault,
|
||||
RebootReasonCode_BreakpointButNoDebuggerAttached,
|
||||
RebootReasonCode_DialogPlatformReset,
|
||||
RebootReasonCode_CoreDumpReentered,
|
||||
RebootReasonCode_CoreDumpRequested,
|
||||
RebootReasonCode_RomError,
|
||||
RebootReasonCode_NMI,
|
||||
} RebootReasonCode;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cookie;
|
||||
RebootReasonCode code;
|
||||
union {
|
||||
uint16_t data16;
|
||||
uint8_t data8[2];
|
||||
};
|
||||
uint32_t extra;
|
||||
} RebootReason;
|
||||
|
||||
void reboot_reason_set(RebootReason *reason);
|
||||
|
||||
bool reboot_reason_get(RebootReason *reason);
|
||||
|
||||
void reboot_reason_clear(void);
|
||||
|
||||
uint32_t reboot_reason_get_crash_lr(void);
|
||||
|
||||
RebootReasonCode reboot_reason_get_last_reboot_reason(void);
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <stdint.h>
|
||||
|
||||
void service_changed_send_indication_to_all(uint16_t start_handle, uint16_t end_handle);
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void hexdump_log_src(int level, const uint8_t *data, size_t length);
|
||||
|
||||
#define PBL_HEXDUMP(level, data, length) \
|
||||
hexdump_log_src(level, data, length);
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/attributes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Log domains
|
||||
|
||||
#define LOG_DOMAIN_MISC 1
|
||||
|
||||
#ifndef LOG_DOMAIN_GATT_DEBUG
|
||||
#define LOG_DOMAIN_GATT_DEBUG 0
|
||||
#endif
|
||||
|
||||
void pbl_log_init(void);
|
||||
void pbl_log_hashed(const uint32_t packed_loghash, ...);
|
||||
void pbl_log(uint8_t log_level, const char *src_filename, int src_line_number,
|
||||
const char *fmt, ...) FORMAT_PRINTF(4, 5);
|
||||
|
||||
void pbl_log_set_level(uint8_t level);
|
||||
uint8_t pbl_log_get_level(void);
|
||||
|
||||
// Log defines/stubs
|
||||
|
||||
#define LOG_LEVEL_ALWAYS 0
|
||||
#define LOG_LEVEL_ERROR 1
|
||||
#define LOG_LEVEL_WARNING 50
|
||||
#define LOG_LEVEL_INFO 100
|
||||
#define LOG_LEVEL_DEBUG 200
|
||||
#define LOG_LEVEL_DEBUG_VERBOSE 255
|
||||
|
||||
#ifdef PBL_LOGS_HASHED
|
||||
#define PBL_SHOULD_LOG(level) (true)
|
||||
#else
|
||||
#define PBL_SHOULD_LOG(level) (level <= LOG_LEVEL_ALWAYS)
|
||||
#endif
|
||||
|
||||
#include "logging/log_hashing.h"
|
||||
|
||||
#define LOG_COLOR_BLACK "BLACK" // Not so useful in general
|
||||
#define LOG_COLOR_RED "RED"
|
||||
#define LOG_COLOR_GREEN "GREEN"
|
||||
#define LOG_COLOR_YELLOW "YELLOW"
|
||||
#define LOG_COLOR_BLUE "BLUE"
|
||||
#define LOG_COLOR_MAGENTA "MAGENTA"
|
||||
#define LOG_COLOR_CYAN "CYAN"
|
||||
#define LOG_COLOR_GREY "GREY"
|
||||
// Reserved for bold. Use sparingly
|
||||
#define LOG_COLOR_LIGHT_GREY "LIGHT_GREY"
|
||||
#define LOG_COLOR_LIGHT_RED "LIGHT_RED"
|
||||
#define LOG_COLOR_LIGHT_GREEN "LIGHT_GREEN"
|
||||
#define LOG_COLOR_LIGHT_YELLOW "LIGHT_YELLOW"
|
||||
#define LOG_COLOR_LIGHT_BLUE "LIGHT_BLUE"
|
||||
#define LOG_COLOR_LIGHT_MAGENTA "LIGHT_MAGENTA"
|
||||
#define LOG_COLOR_LIGHT_CYAN "LIGHT_CYAN"
|
||||
#define LOG_COLOR_WHITE "WHITE"
|
||||
|
||||
// Allow the default color for a src file to be set by defining FILE_LOG_COLOR
|
||||
// Can be called directly with PBL_LOG_COLOR
|
||||
#ifdef FILE_LOG_COLOR
|
||||
#define DEFAULT_LOG_COLOR FILE_LOG_COLOR
|
||||
#else
|
||||
#define DEFAULT_LOG_COLOR LOG_COLOR_GREY
|
||||
#endif
|
||||
|
||||
|
||||
#define SPLIT_64_BIT_ARG(x) (uint32_t)((x >> 32) & 0xFFFFFFFF), (uint32_t)(x & 0xFFFFFFFF)
|
||||
|
||||
#ifndef DEFAULT_LOG_DOMAIN
|
||||
#define DEFAULT_LOG_DOMAIN LOG_DOMAIN_MISC
|
||||
#endif // DEFAULT_LOG_DOMAIN
|
||||
|
||||
#ifdef PBL_LOGS_HASHED
|
||||
|
||||
#define PBL_LOG_D(domain, level, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
NEW_LOG_HASH(pbl_log_hashed, level, DEFAULT_LOG_COLOR, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
NEW_LOG_HASH(pbl_log_hashed, level, color, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else // PBL_LOGS_HASHED
|
||||
|
||||
#define PBL_LOG_D(domain, level, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
pbl_log(level, __FILE_NAME__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
pbl_log(level, __FILE_NAME__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // PBL_LOGS_HASHED
|
||||
|
||||
#define PBL_LOG(level, fmt, ...) \
|
||||
PBL_LOG_D(DEFAULT_LOG_DOMAIN, level, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define PBL_LOG_COLOR(level, color, fmt, ...) \
|
||||
PBL_LOG_COLOR_D(DEFAULT_LOG_DOMAIN, level, color, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define GATT_LOG_DEBUG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_GATT_DEBUG, LOG_LEVEL_DEBUG, fmt, ## args)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/attributes.h>
|
||||
#include <util/likely.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
NORETURN passert_failed_no_message(void);
|
||||
|
||||
NORETURN passert_failed_no_message_with_lr(uint32_t lr);
|
||||
|
||||
NORETURN passert_rom_error_no_message_with_errortype(uint32_t error_type_stat);
|
||||
|
||||
#define PBL_ASSERTN(expr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
passert_failed_no_message(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define PBL_ASSERTN_LR(expr, lr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
passert_failed_no_message_with_lr(lr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define WTF PBL_ASSERTN(0)
|
55
src/bluetooth-fw/da1468x/controller/main/include/tasks.h
Normal file
55
src/bluetooth-fw/da1468x/controller/main/include/tasks.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "osal.h"
|
||||
|
||||
/*
|
||||
* Centralized Task Management
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
DialogTask_SysInit = 0,
|
||||
DialogTask_Ble,
|
||||
DialogTask_HostTrans,
|
||||
DialogTask_Logging,
|
||||
DialogTask_Last,
|
||||
DialogTask_Error = DialogTask_Last,
|
||||
DialogTask_ISR = 0xF,
|
||||
} DialogTask;
|
||||
|
||||
extern OS_TASK DialogTaskList[DialogTask_Last];
|
||||
|
||||
/*
|
||||
* Task Register
|
||||
* Directly write to DialogTaskList[] for easy compatibility with OS_TASK_CREATE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Task Unregister
|
||||
*/
|
||||
void task_unregister_task(OS_TASK task);
|
||||
void task_unregister_dialogtask(DialogTask task);
|
||||
|
||||
/* Given FreeRTOS task handle, return DialogTask enum. DialogTask_Error if not found. */
|
||||
DialogTask task_to_dialogtask(OS_TASK task);
|
||||
|
||||
/* For logging: get the current task ID (will respond with DialogTask_ISR if appropriate) */
|
||||
DialogTask task_get_dialogtask(void);
|
||||
|
||||
/* Dumps to the console, the amount of untouched stack space for each registered task */
|
||||
void tasks_dump_free_space(void);
|
297
src/bluetooth-fw/da1468x/controller/main/ldscripts/mem.ld.h
Normal file
297
src/bluetooth-fw/da1468x/controller/main/ldscripts/mem.ld.h
Normal file
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
/* Linker script to place sections and symbol values. Should be used together
|
||||
* with other linker script that defines memory regions ROM, RetRAM0 and RAM.
|
||||
* It references following symbols, which must be defined in code:
|
||||
* Reset_Handler : Entry of reset handler
|
||||
*
|
||||
* It defines following symbols, which code can use without definition:
|
||||
* __zero_table_start__
|
||||
* __zero_table_end__
|
||||
* __etext
|
||||
* __data_start__
|
||||
* __data_end__
|
||||
* __bss_start__
|
||||
* __bss_end__
|
||||
* __end__
|
||||
* end
|
||||
* __StackLimit
|
||||
* __StackTop
|
||||
*/
|
||||
|
||||
#include "da1468x_mem_map.h"
|
||||
|
||||
#define VECTOR_TABLE_MAX_SIZE (0xC0 + 0x80)
|
||||
#define VECTOR_TABLE_ADDRESS (DATA_RAM_BASE_ADDRESS)
|
||||
#define HEAP_START_ADDRESS (VECTOR_TABLE_ADDRESS + VECTOR_TABLE_MAX_SIZE)
|
||||
#define CODE_AND_RAM_BASE_ADDRESS (HEAP_START_ADDRESS + configTOTAL_HEAP_SIZE)
|
||||
|
||||
#define INFOBLOB_SIZE (VECTOR_TABLE_MAX_SIZE + 8 /* size of fields in .ObjectBinInfo below */ )
|
||||
|
||||
MEMORY
|
||||
{
|
||||
VT (rx):
|
||||
ORIGIN = VECTOR_TABLE_ADDRESS,
|
||||
LENGTH = VECTOR_TABLE_MAX_SIZE
|
||||
INFOBLOB (r):
|
||||
ORIGIN = CODE_AND_RAM_BASE_ADDRESS - INFOBLOB_SIZE,
|
||||
LENGTH = INFOBLOB_SIZE
|
||||
CODE_AND_RAM (rwx):
|
||||
ORIGIN = CODE_AND_RAM_BASE_ADDRESS,
|
||||
LENGTH = DATA_RAM_BASE_ADDRESS + DATA_RAM_SIZE - CODE_AND_RAM_BASE_ADDRESS
|
||||
CACHE_RAM (rwx):
|
||||
ORIGIN = CACHE_RAM_BASE_ADDRESS
|
||||
LENGTH = CACHE_RAM_SIZE
|
||||
|
||||
/* Allocate log strings here for the console. Not loaded to memory. */
|
||||
LOG_STRINGS (r) :
|
||||
ORIGIN = 0xC0000000,
|
||||
LENGTH = 512K
|
||||
|
||||
/* Allocate Firmware Metadata here. Not loaded to memory. See below. */
|
||||
FW_VERSION (r) :
|
||||
ORIGIN = 0xD0000000,
|
||||
LENGTH = 1K
|
||||
}
|
||||
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.vt :
|
||||
{
|
||||
KEEP(*(.isr_vector))
|
||||
/* make sure that IVT doesn't cross 0xC0 */
|
||||
. = 0xC0;
|
||||
KEEP(*(.patch_table))
|
||||
/* make sure that IVT is the correct size */
|
||||
. = VECTOR_TABLE_MAX_SIZE;
|
||||
} > VT
|
||||
/* The heap starts immediately after the patch table and runs to the start of .zero.table */
|
||||
__heap_start = .;
|
||||
__HEAP_START_ADDR_CHECK = HEAP_START_ADDRESS;
|
||||
ASSERT((__heap_start == __HEAP_START_ADDR_CHECK), "Heap start address is incorrect!")
|
||||
|
||||
// Note: There is a struct in dialog_spi_bootloader.c that matches this section
|
||||
.ObjectBinInfo :
|
||||
{
|
||||
LONG(CODE_AND_RAM_BASE_ADDRESS)
|
||||
LONG(VECTOR_TABLE_MAX_SIZE)
|
||||
// Note: if you add/remove something, don't forget to change INFOBLOB_SIZE above
|
||||
} > INFOBLOB
|
||||
|
||||
// When we build the binary with objcopy, we pack the vector & patch table here
|
||||
.vt_stash_region (COPY) :
|
||||
{
|
||||
. = . + VECTOR_TABLE_MAX_SIZE;
|
||||
} > INFOBLOB
|
||||
|
||||
/* GNU build id: This is a hash of parts of the binary that uniquely
|
||||
* identifies the binary. This hash gets inserted by the linker;
|
||||
* we're passing the flag --build-id=sha1 to do this.
|
||||
* The variable DIALOG_BUILD_ID is provided, so that the values can be used
|
||||
* in the firmware code. */
|
||||
.note.gnu.build-id : {
|
||||
PROVIDE(DIALOG_BUILD_ID = .);
|
||||
KEEP(*(.note.gnu.build-id))
|
||||
} > CODE_AND_RAM
|
||||
|
||||
/* To clear multiple BSS sections,
|
||||
* uncomment .zero.table section and,
|
||||
* define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */
|
||||
.zero.table :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__zero_table_start__ = .;
|
||||
LONG (HEAP_START_ADDRESS)
|
||||
LONG (configTOTAL_HEAP_SIZE)
|
||||
LONG (__zero_initialized_start__)
|
||||
LONG (__zero_initialized_end__ - __zero_initialized_start__)
|
||||
LONG (__ble_vars_start__)
|
||||
LONG (__ble_vars_end__ - __ble_vars_start__)
|
||||
LONG (__cache_ram_zi_start__)
|
||||
LONG (__cache_ram_zi_end__ - __cache_ram_zi_start__)
|
||||
LONG (__log_buffer_start__)
|
||||
LONG (__log_buffer_end - __log_buffer_start__)
|
||||
__zero_table_end__ = .;
|
||||
} > CODE_AND_RAM
|
||||
|
||||
__etext = .;
|
||||
|
||||
/* The initialised data section is stored immediately
|
||||
at the end of the text section */
|
||||
.data : AT (__etext)
|
||||
{
|
||||
__data_start__ = .;
|
||||
*(vtable)
|
||||
*(.data*)
|
||||
|
||||
. = ALIGN(4);
|
||||
/* init_array/fini_array moved to flash, align preserved */
|
||||
|
||||
KEEP(*(.jcr*))
|
||||
. = ALIGN(4);
|
||||
*(retention_mem_rw)
|
||||
*(privileged_data_rw)
|
||||
*(.retention)
|
||||
. = ALIGN(4);
|
||||
|
||||
/* All data end */
|
||||
__data_end__ = .;
|
||||
|
||||
} > CODE_AND_RAM
|
||||
|
||||
.text :
|
||||
{
|
||||
__text_start = .;
|
||||
/*
|
||||
* Dialog assigns their jump table to a special section (I'm not
|
||||
* sure why). Need to add it since the linker will otherwise try
|
||||
* to place it in the first available region (the vector table region).
|
||||
*/
|
||||
KEEP(*(jump_table_mem_area))
|
||||
KEEP(*(.default_patch_code_handler_section))
|
||||
|
||||
*(.text*)
|
||||
. = ALIGN(4);
|
||||
*(text_retained)
|
||||
|
||||
*(.rodata*)
|
||||
|
||||
KEEP(*(.eh_frame*))
|
||||
. = ALIGN(4);
|
||||
} > CODE_AND_RAM = 0x00
|
||||
__text_end = .;
|
||||
|
||||
.text_crc32 :
|
||||
{
|
||||
LONG(0xDEADBEEF)
|
||||
} > CODE_AND_RAM
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
} > CODE_AND_RAM = 0x00
|
||||
|
||||
__exidx_start = .;
|
||||
.ARM.exidx :
|
||||
{
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
} > CODE_AND_RAM = 0x00
|
||||
__exidx_end = .;
|
||||
|
||||
__StackTop = __StackLimit + __STACK_SIZE;
|
||||
PROVIDE(__stack = __StackTop);
|
||||
|
||||
__zero_initialized_start__ = .;
|
||||
|
||||
/* .stack_dummy section doesn't contains any symbols. It is only a
|
||||
* a placeholder for where the ISR stack lives. It's put right after the
|
||||
* .text section, so that when it overflows, it's easy to figure out because
|
||||
* .text is not supposed to be changed.
|
||||
*/
|
||||
.stack_dummy (NOLOAD):
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__StackLimit = .;
|
||||
. += __STACK_SIZE;
|
||||
} > CODE_AND_RAM
|
||||
|
||||
.bss (NOLOAD):
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
*(retention_mem_zi)
|
||||
. = ALIGN(4);
|
||||
} > CODE_AND_RAM
|
||||
|
||||
. = ALIGN(4); /* zero initing startup-code assumes word-alignment */
|
||||
__zero_initialized_end__ = .;
|
||||
|
||||
__debug_region_start__ = .;
|
||||
.debug_region (NOLOAD):
|
||||
{
|
||||
KEEP(*(reboot_reason))
|
||||
} > CODE_AND_RAM
|
||||
__debug_region_end__ = .;
|
||||
|
||||
|
||||
/* Put the debug log buffer in it's own section. This allows us to dynamically grow it
|
||||
* to use all available RAM (well, up to RETENTION_BLE).
|
||||
*/
|
||||
.log_buffer (NOLOAD) :
|
||||
{
|
||||
__log_buffer_start__ = .;
|
||||
KEEP(*(.log_buffer))
|
||||
__log_buffer_end = .;
|
||||
} > CODE_AND_RAM
|
||||
|
||||
|
||||
/* BLE ROM (RivieraWaves) variables -- see BLE_VAR_ADDR
|
||||
This region extends all the way to the end of SysRAM!
|
||||
*/
|
||||
RETENTION_BLE BLE_VAR_ADDR (NOLOAD) :
|
||||
{
|
||||
__ble_vars_start__ = .;
|
||||
KEEP(*(ble_variables))
|
||||
} > CODE_AND_RAM
|
||||
|
||||
. = ALIGN(4); /* zero initing startup-code assumes word-alignment */
|
||||
__ble_vars_end__ = .;
|
||||
|
||||
.cache_ram (NOLOAD):
|
||||
{
|
||||
__cache_ram_zi_start__ = .;
|
||||
*(ble_env_heap)
|
||||
*(ble_msg_heap)
|
||||
*(ble_db_heap)
|
||||
. = ALIGN(4);
|
||||
*(privileged_data_zi)
|
||||
|
||||
. = ALIGN(4); /* zero initing startup-code assumes word-alignment */
|
||||
__cache_ram_zi_end__ = .;
|
||||
} > CACHE_RAM
|
||||
|
||||
/* Unloaded section containing our log strings. */
|
||||
.log_strings (INFO) : {
|
||||
KEEP(*(.log_string.header))
|
||||
KEEP(*(.log_strings))
|
||||
} >LOG_STRINGS
|
||||
|
||||
/* Unloaded section containing our Firmware Metadata.
|
||||
* If there is ever enough RAM to keep this in the RAM image (at the very end of .text)
|
||||
* this section can be safely removed without causing errors in the coredump/feelsbadman tools
|
||||
*/
|
||||
.fw_version (INFO) : {
|
||||
KEEP(*(.pbl_fw_version))
|
||||
} >FW_VERSION
|
||||
|
||||
|
||||
|
||||
|
||||
/* Symbols for the core_dump memory table */
|
||||
__vector_table_length = LENGTH(VT);
|
||||
__heap_length = __zero_table_start__ - __heap_start;
|
||||
__text_length = __text_end - __text_start;
|
||||
__rwdata_length = __data_end__ - __data_start__;
|
||||
__stack_bss_length = __zero_initialized_end__ - __zero_initialized_start__;
|
||||
__debug_region_length = __debug_region_end__ - __debug_region_start__;
|
||||
__ble_vars_length = __ble_vars_end__ - __ble_vars_start__;
|
||||
__cache_ram_length = __cache_ram_zi_end__ - __cache_ram_zi_start__;
|
||||
}
|
306
src/bluetooth-fw/da1468x/controller/main/src/advert.c
Normal file
306
src/bluetooth-fw/da1468x/controller/main/src/advert.c
Normal file
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "advert.h"
|
||||
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include "host_transport.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ble_gap.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "light_mutex.h"
|
||||
#include "semphr.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <os/mutex.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern void local_addr_handle_adverts_stopped(void);
|
||||
|
||||
// Accesses to these statics must be protected by prv_lock() calls
|
||||
static AdvertState s_adv_state = AdvertState_Off;
|
||||
|
||||
static AdvertState s_desired_host_adv_state = AdvertState_Off;
|
||||
static struct {
|
||||
bool needs_updating;
|
||||
BLEAdData ad_data;
|
||||
uint8_t data_buffer[2 * GAP_LE_AD_REPORT_DATA_MAX_LENGTH];
|
||||
} s_desired_ad_data;
|
||||
|
||||
static PebbleRecursiveMutex *s_adv_mutex;
|
||||
|
||||
static void prv_lock(void) {
|
||||
mutex_lock_recursive(s_adv_mutex);
|
||||
}
|
||||
|
||||
static void prv_unlock(void) {
|
||||
mutex_unlock_recursive(s_adv_mutex);
|
||||
}
|
||||
|
||||
static gap_disc_mode_t prv_dialog_discoverable_type_for_flag(uint8_t flags) {
|
||||
if (flags & GAP_LE_AD_FLAGS_GEN_DISCOVERABLE_MASK) {
|
||||
return GAP_DISC_MODE_GEN_DISCOVERABLE;
|
||||
} else if (flags & GAP_LE_AD_FLAGS_LIM_DISCOVERABLE_MASK) {
|
||||
return GAP_DISC_MODE_LIM_DISCOVERABLE;
|
||||
} else {
|
||||
return GAP_DISC_MODE_NON_DISCOVERABLE;
|
||||
}
|
||||
}
|
||||
|
||||
ble_error_t advert_set_interval(uint16_t min_slots, uint16_t max_slots) {
|
||||
// No need to lock / take here, this API just sets a couple vars in ble_mgr_dev_params,
|
||||
// while holding the ble_mgr_dev_params lock.
|
||||
ble_error_t rv = ble_gap_adv_intv_set(min_slots, max_slots);
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error: ble_gap_adv_intv_set - rv: %d", rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ble_error_t prv_set_mode_and_data(void) {
|
||||
const BLEAdData *ad_data = &s_desired_ad_data.ad_data;
|
||||
// FIXME PBL-34428: We are chopping off the beginning of the advertisement data because that is
|
||||
// where the flags are stored. We parse the flags and translate to a specific type of discovery
|
||||
// mode required by the Dialog part. Do some error checking while we are at it.
|
||||
const uint8_t *adv_bytes = ad_data->data;
|
||||
size_t adv_bytes_len = ad_data->ad_data_length;
|
||||
gap_disc_mode_t disc_mode = GAP_DISC_MODE_NON_DISCOVERABLE;
|
||||
|
||||
// Make sure the whole packet data is at least 3 bytes.
|
||||
// Make sure that the length of the first field is at least 2 (contains the type and flags byte)
|
||||
// Make sure that the type is GAP_DATA_TYPE_FLAGS
|
||||
if ((ad_data->ad_data_length >= 3)
|
||||
&& (*adv_bytes >= 2)
|
||||
&& *(adv_bytes + 1) == GAP_DATA_TYPE_FLAGS) {
|
||||
// Start the iter at the flags byte
|
||||
const uint8_t *iter = adv_bytes + 2;
|
||||
disc_mode = prv_dialog_discoverable_type_for_flag(*iter);
|
||||
|
||||
// Move iter past the flags
|
||||
iter += 1;
|
||||
|
||||
adv_bytes = iter;
|
||||
adv_bytes_len -= 3;
|
||||
}
|
||||
|
||||
ble_error_t rv = ble_gap_adv_mode_set(disc_mode);
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error: ble_gap_adv_mode_set - rv: %d", rv);
|
||||
// Not returning, probably better to try to advertise regardless of this error.
|
||||
}
|
||||
|
||||
rv = ble_gap_adv_data_set(adv_bytes_len, adv_bytes,
|
||||
ad_data->scan_resp_data_length,
|
||||
&ad_data->data[ad_data->ad_data_length]);
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error: ble_gap_adv_data_set - rv: %d", rv);
|
||||
}
|
||||
|
||||
s_desired_ad_data.needs_updating = false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ble_error_t prv_advert_enable(void) {
|
||||
ble_error_t rv = BLE_ERROR_FAILED;
|
||||
if (s_desired_ad_data.needs_updating) {
|
||||
rv = prv_set_mode_and_data();
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv = ble_gap_adv_start(GAP_CONN_MODE_UNDIRECTED);
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_adv_start: status = 0x%x", rv);
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Adverts (re)started");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ble_error_t prv_advert_disable(void) {
|
||||
ble_error_t rv = ble_gap_adv_stop();
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_adv_stop: status = 0x%x", rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ble_error_t prv_try_to_enter_host_desired_state(void) {
|
||||
ble_error_t rv = BLE_STATUS_OK;
|
||||
prv_lock();
|
||||
{
|
||||
// The only valid desired host states are Off & Running
|
||||
switch (s_desired_host_adv_state) {
|
||||
case AdvertState_Running:
|
||||
case AdvertState_Off:
|
||||
break;
|
||||
default:
|
||||
WTF;
|
||||
}
|
||||
|
||||
// Are we already in the state we desire
|
||||
if (s_desired_host_adv_state == s_adv_state) {
|
||||
rv = BLE_ERROR_ALREADY_DONE;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
||||
switch (s_adv_state) {
|
||||
case AdvertState_Pausing:
|
||||
case AdvertState_Paused:
|
||||
case AdvertState_Stopping:
|
||||
// We are transitioning, these are handled by other routines
|
||||
goto unlock;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (s_desired_host_adv_state == AdvertState_Running) {
|
||||
// TODO: Does the dialog stack gracefully handle calling stop before adds
|
||||
// are truly running. Is there an event we can wait on and have a Starting state?
|
||||
s_adv_state = AdvertState_Running;
|
||||
rv = prv_advert_enable();
|
||||
|
||||
} else if (s_desired_host_adv_state == AdvertState_Off) {
|
||||
s_adv_state = AdvertState_Stopping;
|
||||
rv = prv_advert_disable();
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
return rv;
|
||||
}
|
||||
|
||||
AdvertState advert_enable(void) {
|
||||
AdvertState rv;
|
||||
prv_lock();
|
||||
{
|
||||
s_desired_host_adv_state = AdvertState_Running;
|
||||
prv_try_to_enter_host_desired_state();
|
||||
rv = s_adv_state;
|
||||
}
|
||||
prv_unlock();
|
||||
return rv;
|
||||
}
|
||||
|
||||
void advert_disable(void) {
|
||||
prv_lock();
|
||||
{
|
||||
s_desired_host_adv_state = AdvertState_Off;
|
||||
prv_try_to_enter_host_desired_state();
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void advert_set_data(const BLEAdData *ad_data) {
|
||||
prv_lock();
|
||||
{
|
||||
memcpy(&s_desired_ad_data.ad_data, ad_data,
|
||||
sizeof(BLEAdData) + ad_data->ad_data_length + ad_data->scan_resp_data_length);
|
||||
s_desired_ad_data.needs_updating = true;
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
static void prv_resume_if_needed(void) {
|
||||
if (s_desired_host_adv_state == AdvertState_Running) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Unpausing advertisements");
|
||||
}
|
||||
s_adv_state = AdvertState_Off;
|
||||
prv_try_to_enter_host_desired_state();
|
||||
}
|
||||
|
||||
void advert_handle_completed(const ble_evt_gap_adv_completed_t *evt) {
|
||||
prv_lock();
|
||||
{
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "advert_handle_completed %"PRIu8, evt->status);
|
||||
if (s_adv_state == AdvertState_Pausing) {
|
||||
s_adv_state = AdvertState_Paused;
|
||||
} else {
|
||||
s_adv_state = AdvertState_Off;
|
||||
}
|
||||
|
||||
// Note: Ideally, the controller would inform us why the advertisement had stopped. For
|
||||
// example, if we knew that the reason was due to a slave device connecting we would not
|
||||
// restart advertisements. However, I don't see a robust way to conclude this based on the
|
||||
// events the ROM stack emits.
|
||||
prv_try_to_enter_host_desired_state();
|
||||
}
|
||||
prv_unlock();
|
||||
|
||||
local_addr_handle_adverts_stopped();
|
||||
|
||||
prv_lock();
|
||||
{
|
||||
// Make sure we never stay paused once local_addr has a chance to do whatever it needs to do
|
||||
// while advertisements were paused.
|
||||
if (s_adv_state == AdvertState_Paused) {
|
||||
prv_resume_if_needed();
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
//! @note This is here exclusively for local_addr.c!
|
||||
//! If you also need to use advert_pause/resume, it's time to add a refcount.
|
||||
bool advert_execute_cb_if_adverts_are_paused(void (*cb)(void *), void *ctx) {
|
||||
bool cb_executed = false;
|
||||
prv_lock();
|
||||
{
|
||||
switch (s_adv_state) {
|
||||
// States where we can immediately transition to paused
|
||||
case AdvertState_Off:
|
||||
case AdvertState_Paused:
|
||||
s_adv_state = AdvertState_Paused;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (s_adv_state != AdvertState_Paused) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Pausing advertisements, state = 0x%x",
|
||||
s_adv_state);
|
||||
if (s_adv_state == AdvertState_Running) {
|
||||
prv_advert_disable();
|
||||
}
|
||||
|
||||
s_adv_state = AdvertState_Pausing;
|
||||
} else if (s_adv_state == AdvertState_Paused) {
|
||||
// It's safe to call the cb
|
||||
cb(ctx);
|
||||
cb_executed = true;
|
||||
prv_resume_if_needed();
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
return cb_executed;
|
||||
}
|
||||
|
||||
void advert_init(void) {
|
||||
s_adv_state = AdvertState_Off;
|
||||
s_desired_host_adv_state = AdvertState_Off;
|
||||
s_desired_ad_data.needs_updating = false;
|
||||
s_desired_ad_data.ad_data.ad_data_length = 0;
|
||||
s_desired_ad_data.ad_data.scan_resp_data_length = 0;
|
||||
s_adv_mutex = mutex_create_recursive();
|
||||
}
|
54
src/bluetooth-fw/da1468x/controller/main/src/aes.c
Normal file
54
src/bluetooth-fw/da1468x/controller/main/src/aes.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "aes.h"
|
||||
|
||||
#include "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "hw_aes_hash.h"
|
||||
#include "sdk_defs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
bool aes_128_encrypt_block(const uint8_t key[AES_128_KEY_SIZE],
|
||||
const uint8_t plain_text_block[AES_128_BLOCK_SIZE],
|
||||
uint8_t cipher_text_block_out[AES_128_BLOCK_SIZE]) {
|
||||
|
||||
// Nothing else should currently be using the crypto block, just in case this changes:
|
||||
PBL_ASSERTN(!REG_GETF(CRG_TOP, CLK_AMBA_REG, AES_CLK_ENABLE));
|
||||
|
||||
hw_aes_hash_setup aes_setup = {
|
||||
.mode = HW_AES_ECB, // we're just encrypting one block, so just use "code book"
|
||||
.aesDirection = HW_AES_ENCRYPT,
|
||||
.aesKeySize = HW_AES_128,
|
||||
.aesKeyExpand = true,
|
||||
.aesKeys = (uintptr_t)(void *)key,
|
||||
.aesWriteBackAll = false,
|
||||
.moreDataToCome = false,
|
||||
.sourceAddress = (uintptr_t)(void *)plain_text_block,
|
||||
.destinationAddress = (uintptr_t)(void *)cipher_text_block_out,
|
||||
.dataSize = AES_128_BLOCK_SIZE,
|
||||
.enableInterrupt = false,
|
||||
};
|
||||
hw_aes_hash_init(&aes_setup);
|
||||
hw_aes_hash_start();
|
||||
hw_aes_hash_disable(true /* wait_until_inactive */);
|
||||
|
||||
return true;
|
||||
}
|
693
src/bluetooth-fw/da1468x/controller/main/src/ble_task.c
Normal file
693
src/bluetooth-fw/da1468x/controller/main/src/ble_task.c
Normal file
|
@ -0,0 +1,693 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ble_gap.h>
|
||||
|
||||
#include "advert.h"
|
||||
#include "dialog_analytics/analytics.h"
|
||||
#include "bonding_flags.h"
|
||||
#include "bonding_sync_impl.h"
|
||||
#include "connection.h"
|
||||
#include "debug_reboot_reason.h"
|
||||
#include "dialog_utils.h"
|
||||
#include "gatt_local_services.h"
|
||||
#include "hci_rom_passthrough.h"
|
||||
#include "host_transport.h"
|
||||
#include "host_transport_impl.h"
|
||||
#include "local_addr_impl.h"
|
||||
#include "pebble_pairing_service_impl.h"
|
||||
#include "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "tasks.h"
|
||||
#include "hc_protocol/hc_endpoint_analytics.h"
|
||||
#include "hc_protocol/hc_endpoint_responsiveness.h"
|
||||
#include "hc_protocol/hc_endpoint_gap_le_connect.h"
|
||||
#include "hc_protocol/hc_endpoint_gap_service.h"
|
||||
#include "hc_protocol/hc_endpoint_gatt.h"
|
||||
#include "hc_protocol/hc_endpoint_pairing.h"
|
||||
#include "gatt_client_discovery.h"
|
||||
#include "gatt_wrapper.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ad_ble.h"
|
||||
#include "ble_att.h"
|
||||
#include "ble_common.h"
|
||||
#include "ble_gap.h"
|
||||
#include "ble_gattc.h"
|
||||
#include "ble_gatts.h"
|
||||
#include "ble_mgr.h"
|
||||
#include "ble_mgr_irb_common.h"
|
||||
#include "ble_service.h"
|
||||
#include "ble_uuid.h"
|
||||
#include "osal.h"
|
||||
#include "smp_common.h"
|
||||
#include "storage.h"
|
||||
#include "sys_watchdog.h"
|
||||
#include "timers.h"
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <bluetooth/gap_le_connect.h>
|
||||
#include <bluetooth/gatt_service_types.h>
|
||||
#include <bluetooth/hci_types.h>
|
||||
#include <bluetooth/init.h>
|
||||
#include <bluetooth/mtu.h>
|
||||
#include <bluetooth/sm_types.h>
|
||||
|
||||
#include <util/uuid.h>
|
||||
|
||||
#define mainBLE_PERIPHERAL_TASK_PRIORITY (tskIDLE_PRIORITY + 1)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Defines to configure the different test scenarios (enable only one)
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#define ADV_MAX_INTVL_SLOTS (400)
|
||||
|
||||
extern void test_hci_passthrough(void);
|
||||
#define TEST_HCI_ROM_PASSTHROUGH (0)
|
||||
|
||||
typedef struct {
|
||||
SemaphoreHandle_t semph;
|
||||
const BTDriverConfig *config;
|
||||
} BleTaskInitInfo;
|
||||
|
||||
/*
|
||||
* Main code
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint16_t conn_idx; //! The connection that has the demo server
|
||||
|
||||
uint16_t write_hdl;
|
||||
uint16_t write_cccd_hdl;
|
||||
|
||||
uint16_t notify_hdl;
|
||||
uint16_t notify_cccd_hdl;
|
||||
|
||||
uint32_t expected_read_header; //! the payload index we are expecting to receiver from the server
|
||||
uint32_t current_write_header; //! the current header we are writing to App
|
||||
} demo_server_hdl_t;
|
||||
|
||||
static int8_t s_ble_perip_wdog_id;
|
||||
|
||||
static void prv_log_conn_params(const gap_conn_params_t *conn_params) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Current conn params: intvl min: %d max: %d latency: %d STO: %d",
|
||||
(int)conn_params->interval_min, (int)conn_params->interval_max,
|
||||
(int)conn_params->slave_latency, (int)conn_params->sup_timeout);
|
||||
}
|
||||
|
||||
static bool prv_get_device_and_irk_by_conn_idx(uint16_t conn_idx,
|
||||
bool *is_resolved_out,
|
||||
SMIdentityResolvingKey *irk_out) {
|
||||
bool success = true;
|
||||
storage_acquire();
|
||||
{
|
||||
device_t *dev = find_device_by_conn_idx(conn_idx);
|
||||
if (!dev) {
|
||||
success = false;
|
||||
goto release;
|
||||
}
|
||||
bool is_resolved = false;
|
||||
if (dev && dev->irk) {
|
||||
is_resolved = true;
|
||||
if (irk_out) {
|
||||
memcpy(irk_out, dev->irk->key, sizeof(*irk_out));
|
||||
}
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Address not resolved");
|
||||
}
|
||||
if (is_resolved_out) {
|
||||
*is_resolved_out = is_resolved;
|
||||
}
|
||||
}
|
||||
release:
|
||||
storage_release();
|
||||
return success;
|
||||
}
|
||||
|
||||
static void handle_evt_gap_connected(ble_evt_gap_connected_t *evt) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Connected, idx=%d!", (int)evt->conn_idx);
|
||||
prv_log_conn_params(&evt->conn_params);
|
||||
|
||||
if (!evt->is_master) {
|
||||
// To be compliant with BT Core Spec section 7.8.9, stop advertising when the chip gets
|
||||
// connected to. (The host expectes advertisements to have stopped in this scenario (see
|
||||
// gap_le_advert_handle_connect_as_slave)) Ideally we would handle this in our advert.c wrapper
|
||||
// but we don't have enough state available to conclude this in advert_handle_completed()
|
||||
advert_disable();
|
||||
}
|
||||
|
||||
BTDeviceInternal addr;
|
||||
dialog_utils_bd_address_to_bt_device(&evt->peer_address, &addr);
|
||||
|
||||
HcGapLeConnectionData event = {
|
||||
.connection_complete_event = {
|
||||
.conn_params = {
|
||||
.conn_interval_1_25ms = evt->conn_params.interval_min,
|
||||
.slave_latency_events = evt->conn_params.slave_latency,
|
||||
.supervision_timeout_10ms = evt->conn_params.sup_timeout,
|
||||
},
|
||||
.peer_address = addr,
|
||||
.status = HciStatusCode_Success,
|
||||
.is_master = evt->is_master,
|
||||
.handle = evt->conn_idx,
|
||||
},
|
||||
};
|
||||
|
||||
// Store the current local address with the connection. It's possible the local address will
|
||||
// cycle while being connected. If we're going to pair and the device wants to use address
|
||||
// pinning, we need to make sure to pin the current address and not a newly generated one.
|
||||
const BTDeviceAddress *own_addr = (const BTDeviceAddress *)evt->own_addr.addr;
|
||||
const BleConnectionParams *params = &event.connection_complete_event.conn_params;
|
||||
connection_create(evt->conn_idx, &addr, own_addr, params);
|
||||
|
||||
ble_error_t rv = ble_gattc_get_mtu(evt->conn_idx, &event.mtu);
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "ble_gattc_get_mtu failed with status: %d", rv);
|
||||
}
|
||||
|
||||
// If the address got resolved, copy the IRK into the event:
|
||||
BleConnectionCompleteEvent *conn_evt = &event.connection_complete_event;
|
||||
prv_get_device_and_irk_by_conn_idx(evt->conn_idx, &conn_evt->is_resolved, &conn_evt->irk);
|
||||
|
||||
hc_endpoint_gap_le_connect_send_connection_complete(&event);
|
||||
}
|
||||
|
||||
static void prv_handle_conn_param_update_request_response(
|
||||
const ble_evt_gap_conn_param_update_completed_t *event) {
|
||||
// This callback getting invoked means that conn param request was acknowledged. Could be either
|
||||
// successful or not. If a failure, we return the failure up to the host
|
||||
if (event->status == BLE_STATUS_OK) {
|
||||
// We'll be getting an conn_param_update very soon, no need to alert the host.
|
||||
return;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Connection parameter update request failed with status: %d",
|
||||
event->status);
|
||||
|
||||
Connection *conn = connection_by_idx(event->conn_idx);
|
||||
if (conn == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No connection for idx=%d", event->conn_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
BTDeviceInternal addr;
|
||||
connection_get_address(conn, &addr);
|
||||
|
||||
hc_endpoint_responsiveness_notify_update(NULL, &addr, HciStatusCode_VS_Base + event->status);
|
||||
}
|
||||
|
||||
static void prv_handle_conn_param_update_request(const ble_evt_gap_conn_param_update_req_t *evt) {
|
||||
gap_conn_params_t conn_params = evt->conn_params;
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Master requesting conn param change, Conn Idx: %d - (%d %d %d %d)",
|
||||
evt->conn_idx, (int)conn_params.interval_min, (int)conn_params.interval_max,
|
||||
(int)conn_params.slave_latency, (int)conn_params.sup_timeout);
|
||||
|
||||
// accept the change
|
||||
ble_gap_conn_param_update_reply(evt->conn_idx, true);
|
||||
}
|
||||
|
||||
static void prv_handle_conn_param_update(const ble_evt_gap_conn_param_updated_t *event) {
|
||||
// This callback getting invoked means that conn param request was successful
|
||||
// Note: If this cb is not invoked within 30s after issuing an update request
|
||||
// we will be auto-disconnected (according to the ble_gap_conn_param_update
|
||||
// API)
|
||||
prv_log_conn_params(&event->conn_params);
|
||||
|
||||
const uint16_t conn_idx = event->conn_idx;
|
||||
Connection *conn = connection_by_idx(conn_idx);
|
||||
if (conn == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No connection for idx=%d", conn_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
BTDeviceInternal addr;
|
||||
connection_get_address(conn, &addr);
|
||||
|
||||
BleConnectionParams params = {
|
||||
.conn_interval_1_25ms = event->conn_params.interval_min,
|
||||
.slave_latency_events = event->conn_params.slave_latency,
|
||||
.supervision_timeout_10ms = event->conn_params.sup_timeout,
|
||||
};
|
||||
connection_set_conn_params(conn, ¶ms);
|
||||
pebble_pairing_service_handle_conn_params_change(conn, conn_idx);
|
||||
hc_endpoint_responsiveness_notify_update(¶ms, &addr, HciStatusCode_Success);
|
||||
}
|
||||
|
||||
static void prv_handle_evt_gap_disconnected(const ble_evt_gap_disconnected_t *evt) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Disconnected: idx=%"PRIu16", reason=0x%"PRIx8,
|
||||
evt->conn_idx, evt->reason);
|
||||
local_addr_handle_disconnection();
|
||||
|
||||
Connection *conn = connection_by_idx_check(evt->conn_idx);
|
||||
|
||||
BTDeviceInternal addr;
|
||||
connection_get_address(conn, &addr);
|
||||
|
||||
BleDisconnectionCompleteEvent event = {
|
||||
.peer_address = addr,
|
||||
.status = HciStatusCode_Success,
|
||||
.reason = evt->reason,
|
||||
.handle = evt->conn_idx,
|
||||
};
|
||||
|
||||
hc_endpoint_gap_le_connect_send_disconnection_complete(&event);
|
||||
|
||||
connection_destroy(connection_by_idx(evt->conn_idx));
|
||||
}
|
||||
|
||||
static void prv_handle_evt_gap_sec_level_changed(const ble_evt_gap_sec_level_changed_t *evt) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Security level changed to: %u", evt->level);
|
||||
Connection *conn = connection_by_idx(evt->conn_idx);
|
||||
if (conn == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No connection for idx=%d", evt->conn_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
pebble_pairing_service_handle_status_change(conn, evt->conn_idx);
|
||||
|
||||
BTDeviceInternal addr;
|
||||
connection_get_address(conn, &addr);
|
||||
|
||||
BleEncryptionChange event = {
|
||||
.dev_address = addr.address,
|
||||
.status = HciStatusCode_Success,
|
||||
.encryption_enabled = (evt->level > GAP_SEC_LEVEL_1),
|
||||
};
|
||||
|
||||
hc_endpoint_gap_le_connect_send_encryption_changed(&event);
|
||||
}
|
||||
|
||||
//! @return True iff there is an existing pairing for this connection with the
|
||||
//! "BleBondingFlag_ShouldAutoAcceptRePairing" bit set AND the bit has been set again through the
|
||||
//! Trigger Pairing characteristic. The second part of the condition may not be true in case a user
|
||||
//! updated the phone to an Android version that does not have the bug (PBL-39369).
|
||||
static bool prv_is_auto_accept_repairing_mode(Connection *conn, uint16_t conn_idx) {
|
||||
bool should_auto_accept_re_pairing;
|
||||
storage_acquire();
|
||||
{
|
||||
const device_t *dev = find_device_by_conn_idx(conn_idx);
|
||||
should_auto_accept_re_pairing = (dev &&
|
||||
(dev->flags & BleBondingFlag_ShouldAutoAcceptRePairing));
|
||||
}
|
||||
storage_release();
|
||||
should_auto_accept_re_pairing &= connection_should_auto_accept_re_pairing(conn);
|
||||
|
||||
return should_auto_accept_re_pairing;
|
||||
}
|
||||
|
||||
static void prv_handle_pairing_request(const ble_evt_gap_pair_req_t *evt) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Received pairing request.");
|
||||
|
||||
const uint16_t conn_idx = evt->conn_idx;
|
||||
Connection *conn = connection_by_idx(conn_idx);
|
||||
if (conn == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No connection for idx=%d", conn_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
BTDeviceInternal device = {};
|
||||
connection_get_address(conn, &device);
|
||||
|
||||
if (prv_is_auto_accept_repairing_mode(conn, conn_idx)) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Auto-accepting pairing request");
|
||||
extern void pair_reply(uint16_t conn_idx, bool is_confirmed);
|
||||
pair_reply(conn_idx, true);
|
||||
return;
|
||||
}
|
||||
|
||||
hc_endpoint_pairing_send_pairing_request(&device);
|
||||
}
|
||||
|
||||
static void prv_handle_address_resolved(const ble_evt_gap_address_resolved_t *evt) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "IRK exchanged and address resolved/updated.");
|
||||
// Update the IRK + Address (Dialog SDK swaps the connection address for the identity address,
|
||||
// even if the pairing process failed mid-way):
|
||||
BleAddressAndIRKChange e = {};
|
||||
if (!prv_get_device_and_irk_by_conn_idx(evt->conn_idx, &e.is_resolved, &e.irk)) {
|
||||
// Disconnected in the mean time
|
||||
return;
|
||||
}
|
||||
|
||||
e.is_address_updated = true;
|
||||
dialog_utils_bd_address_to_bt_device(&evt->address, &e.device);
|
||||
dialog_utils_bd_address_to_bt_device(&evt->resolved_address, &e.new_device);
|
||||
|
||||
// Also update the new address in the local connection list:
|
||||
Connection *connection = connection_by_idx_check(evt->conn_idx);
|
||||
connection_update_address(connection, &e.new_device);
|
||||
|
||||
hc_endpoint_gap_le_connect_send_address_and_irk_changed(&e);
|
||||
}
|
||||
|
||||
static void prv_handle_pairing_completed(const ble_evt_gap_pair_completed_t *evt) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Pairing completed. Bond=%u, MITM=%u, status=0x%"PRIx8,
|
||||
evt->bond, evt->mitm, evt->status);
|
||||
const uint16_t conn_idx = evt->conn_idx;
|
||||
Connection *conn = connection_by_idx(conn_idx);
|
||||
if (conn == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No connection for idx=%d", conn_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (prv_is_auto_accept_repairing_mode(conn, conn_idx)) {
|
||||
// Don't sync the new pairing, we're dealing with an Android device that has to re-pair
|
||||
// upon every reconnection. Don't sync to avoid re-writing the pairing info upon every
|
||||
// reconnection. @see https://pebbletechnology.atlassian.net/browse/PBL-39369
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Skipping syncing new pairing info...");
|
||||
} else {
|
||||
const bool success = (evt->status == BLE_STATUS_OK);
|
||||
if (success && evt->bond) {
|
||||
// Sync the new bonding:
|
||||
bonding_sync_handle_pairing_completed(conn, conn_idx);
|
||||
}
|
||||
BTDeviceInternal device = {};
|
||||
connection_get_address(conn, &device);
|
||||
hc_endpoint_pairing_send_pairing_complete(&device, evt->status);
|
||||
}
|
||||
|
||||
// Convert to BT Spec error codes and update Pebble Pairing Service's Conn Status characteristic:
|
||||
const uint8_t smp_status = SMP_GET_PAIR_FAIL_REASON(evt->status);
|
||||
connection_set_last_pairing_result(conn_idx, smp_status);
|
||||
pebble_pairing_service_handle_status_change(conn, conn_idx);
|
||||
}
|
||||
|
||||
static void prv_handle_gatt_mtu_changed(const ble_evt_gattc_mtu_changed_t *event) {
|
||||
Connection *connection = connection_by_idx_check(event->conn_idx);
|
||||
if (!connection) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "No connection for idx %d", event->conn_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_INFO, "MTU updated: %"PRIu16, event->mtu);
|
||||
pebble_pairing_service_handle_gatt_mtu_change(connection, event->conn_idx);
|
||||
hc_endpoint_gap_service_mtu_changed(connection, event->mtu);
|
||||
}
|
||||
|
||||
static void prv_configure_local_device_properties(const BTDriverConfig *config) {
|
||||
ble_error_t e;
|
||||
|
||||
e = ble_gap_appearance_set(BLE_GAP_APPEARANCE_GENERIC_WATCH, ATT_PERM_READ);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_appearance_set: %u", e);
|
||||
}
|
||||
|
||||
#if 0 // FIXME: PBL-36556 -- How to configure the identity address?
|
||||
const BTDeviceAddress *addr = &config->identity_addr;
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Setting Local Device Addr to:");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)addr, sizeof(*addr));
|
||||
own_address_t own_addr = {
|
||||
.addr_type = PRIVATE_STATIC_ADDRESS
|
||||
};
|
||||
memcpy(&own_addr.addr, addr, sizeof(own_addr.addr));
|
||||
|
||||
e = ble_gap_address_set(&own_addr, 0);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_address_set: %u", e);
|
||||
}
|
||||
#endif
|
||||
|
||||
e = ble_gap_mtu_size_set(ATT_MAX_SUPPORTED_MTU);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_mtu_size_set: %u", e);
|
||||
}
|
||||
|
||||
e = ble_gap_role_set(GAP_PERIPHERAL_ROLE);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_role_set: %u", e);
|
||||
}
|
||||
|
||||
// FIXME: PBL-23399: Single-source this.
|
||||
const gap_conn_params_t preferred_params = {
|
||||
.interval_min = 6, // 7.5ms
|
||||
.interval_max = 24, // 30ms
|
||||
.slave_latency = 4,
|
||||
.sup_timeout = 600, // 6000ms
|
||||
};
|
||||
e = ble_gap_per_pref_conn_params_set(&preferred_params);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_per_pref_conn_params_set: %u", e);
|
||||
}
|
||||
}
|
||||
|
||||
void ble_task_assert_is_executing_on_ble_task(void) {
|
||||
PBL_ASSERTN(xTaskGetCurrentTaskHandle() == DialogTaskList[DialogTask_Ble]);
|
||||
}
|
||||
|
||||
static void prv_configure_irk(const BTDriverConfig *config) {
|
||||
// Use the Identity Root directly as the local Identity Resolving Key:
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Setting local IRK:");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (const uint8_t *)&config->root_keys[SMRootKeyTypeIdentity],
|
||||
sizeof(config->root_keys[SMRootKeyTypeIdentity]));
|
||||
|
||||
ble_dev_params_t *dev_params = ble_mgr_dev_params_acquire();
|
||||
memcpy(&dev_params->irk, &config->root_keys[SMRootKeyTypeIdentity], sizeof(dev_params->irk));
|
||||
ble_mgr_dev_params_release();
|
||||
}
|
||||
|
||||
static void prv_handle_get_peer_version_complete(const ble_evt_gap_get_peer_version_t *evt) {
|
||||
Connection *conn = connection_by_idx_check(evt->conn_idx);
|
||||
|
||||
BTDeviceInternal addr;
|
||||
connection_get_address(conn, &addr);
|
||||
|
||||
BleRemoteVersionInfoReceivedEvent event = {
|
||||
.peer_address = addr,
|
||||
.remote_version_info = {
|
||||
.version_number = evt->version,
|
||||
.company_identifier = evt->company_identifier,
|
||||
.subversion_number = evt->subversion,
|
||||
}
|
||||
};
|
||||
|
||||
hc_endpoint_gap_le_connect_send_peer_version_info(&event);
|
||||
}
|
||||
|
||||
static void prv_ble_peripheral_task(void *params) {
|
||||
const BleTaskInitInfo *init_info = (const BleTaskInitInfo *)params;
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "At least I started");
|
||||
|
||||
analytics_init();
|
||||
hc_endpoint_analytics_send_reboot_info();
|
||||
debug_reboot_reason_print();
|
||||
|
||||
s_ble_perip_wdog_id = sys_watchdog_register(false);
|
||||
|
||||
// FIXME:
|
||||
// srand(time(NULL));
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "hope for the best");
|
||||
connection_module_init();
|
||||
|
||||
prv_configure_irk(init_info->config);
|
||||
|
||||
ble_error_t e = ble_enable();
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_enable: %u", e);
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "go go go");
|
||||
ble_register_app();
|
||||
|
||||
advert_init();
|
||||
local_addr_init();
|
||||
|
||||
// Do all the things that clear the ATT table before registering the local services:
|
||||
prv_configure_local_device_properties(init_info->config);
|
||||
|
||||
gatt_local_services_init(init_info->config);
|
||||
gatt_local_services_register();
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "start the loop");
|
||||
|
||||
#if TEST_HCI_ROM_PASSTHROUGH
|
||||
test_hci_passthrough();
|
||||
#endif
|
||||
|
||||
// Signal to the initing task that the BLE stack is up and running now!
|
||||
xSemaphoreGive(init_info->semph);
|
||||
|
||||
for (;;) {
|
||||
BaseType_t ret;
|
||||
uint32_t notif;
|
||||
|
||||
sys_watchdog_notify(s_ble_perip_wdog_id);
|
||||
sys_watchdog_suspend(s_ble_perip_wdog_id);
|
||||
|
||||
ret = xTaskNotifyWait(0, (uint32_t) -1, ¬if, portMAX_DELAY);
|
||||
configASSERT(ret == pdTRUE);
|
||||
|
||||
sys_watchdog_resume(s_ble_perip_wdog_id);
|
||||
|
||||
hc_protocol_process_receive_buffer();
|
||||
|
||||
extern bool should_log_about_mic_error(uint32_t *max_subsequent_failures);
|
||||
uint32_t num_subsequent_failures = 0;
|
||||
if (should_log_about_mic_error(&num_subsequent_failures)) {
|
||||
hc_endpoint_analytics_log_mic_error_detected(num_subsequent_failures);
|
||||
}
|
||||
|
||||
/* notified from BLE manager, can get event */
|
||||
if (notif & BLE_APP_NOTIFY_MASK) {
|
||||
ble_evt_hdr_t *hdr;
|
||||
|
||||
hdr = ble_get_event(false);
|
||||
if (!hdr) {
|
||||
goto no_event;
|
||||
}
|
||||
|
||||
if (ble_service_handle_event(hdr)) {
|
||||
goto handled;
|
||||
}
|
||||
|
||||
switch (hdr->evt_code) {
|
||||
// GAP Events:
|
||||
case BLE_EVT_GAP_CONNECTED:
|
||||
handle_evt_gap_connected((ble_evt_gap_connected_t *) hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_DISCONNECTED:
|
||||
prv_handle_evt_gap_disconnected((ble_evt_gap_disconnected_t *) hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_ADV_COMPLETED:
|
||||
advert_handle_completed((ble_evt_gap_adv_completed_t *) hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_CONN_PARAM_UPDATED:
|
||||
prv_handle_conn_param_update((const ble_evt_gap_conn_param_updated_t *)hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_CONN_PARAM_UPDATE_COMPLETED:
|
||||
prv_handle_conn_param_update_request_response(
|
||||
(const ble_evt_gap_conn_param_update_completed_t *)hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_CONN_PARAM_UPDATE_REQ:
|
||||
prv_handle_conn_param_update_request((const ble_evt_gap_conn_param_update_req_t *)hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_SEC_LEVEL_CHANGED:
|
||||
prv_handle_evt_gap_sec_level_changed((ble_evt_gap_sec_level_changed_t *)hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_PAIR_REQ:
|
||||
prv_handle_pairing_request((const ble_evt_gap_pair_req_t *)hdr);
|
||||
break;
|
||||
case BLE_EVT_GAP_PAIR_COMPLETED: {
|
||||
ble_evt_gap_pair_completed_t *evt = (ble_evt_gap_pair_completed_t *)hdr;
|
||||
prv_handle_pairing_completed(evt);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_EVT_GAP_ADDRESS_RESOLVED: {
|
||||
const ble_evt_gap_address_resolved_t *evt = (const ble_evt_gap_address_resolved_t *)hdr;
|
||||
prv_handle_address_resolved(evt);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_EVT_GAP_DEV_ADDR_UPDATED: {
|
||||
const ble_evt_gap_dev_address_updated_t *evt =
|
||||
(const ble_evt_gap_dev_address_updated_t *)hdr;
|
||||
const BTDeviceAddress *addr = (const BTDeviceAddress *)&evt->address.addr;
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Local address updated to "BT_DEVICE_ADDRESS_FMT,
|
||||
BT_DEVICE_ADDRESS_XPLODE_PTR(addr));
|
||||
local_addr_handle_update(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_EVT_GAP_GET_PEER_VERSION_COMPLETE:
|
||||
prv_handle_get_peer_version_complete((const ble_evt_gap_get_peer_version_t *)hdr);
|
||||
break;
|
||||
|
||||
// GATT Client Events:
|
||||
case BLE_EVT_GATTC_MTU_CHANGED:
|
||||
prv_handle_gatt_mtu_changed((const ble_evt_gattc_mtu_changed_t *)hdr);
|
||||
break;
|
||||
|
||||
case BLE_EVT_GATTC_BROWSE_SVC:
|
||||
gatt_client_discovery_process_service((const ble_evt_gattc_browse_svc_t *)hdr);
|
||||
break;
|
||||
|
||||
case BLE_EVT_GATTC_BROWSE_COMPLETED:
|
||||
gatt_client_discovery_handle_complete((const ble_evt_gattc_browse_completed_t *)hdr);
|
||||
break;
|
||||
|
||||
case BLE_EVT_GATTC_READ_COMPLETED:
|
||||
gatt_wrapper_handle_read_completed((const ble_evt_gattc_read_completed_t *)hdr);
|
||||
break;
|
||||
|
||||
case BLE_EVT_GATTC_WRITE_COMPLETED:
|
||||
gatt_wrapper_handle_write_completed((const ble_evt_gattc_write_completed_t *)hdr);
|
||||
break;
|
||||
|
||||
case BLE_EVT_GATTC_NOTIFICATION:
|
||||
gatt_wrapper_handle_notification((const ble_evt_gattc_notification_t *)hdr);
|
||||
break;
|
||||
|
||||
case BLE_EVT_GATTC_INDICATION:
|
||||
gatt_wrapper_handle_indication((const ble_evt_gattc_indication_t *)hdr);
|
||||
break;
|
||||
|
||||
case IRB_BLE_STACK_MSG:
|
||||
if (((irb_ble_stack_msg_t *)hdr)->msg_type == HCI_EVT_MSG) {
|
||||
hci_rom_passthrough_handle_evt(&((irb_ble_stack_msg_t *)hdr)->msg.hci.evt);
|
||||
#if TEST_HCI_ROM_PASSTHROUGH
|
||||
test_hci_passthrough();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
// FALLTHROUGH
|
||||
|
||||
default: // Unhandled
|
||||
ble_handle_event_default(hdr);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Unhandled BLE event: 0x%"PRIx16, hdr->evt_code);
|
||||
break;
|
||||
}
|
||||
|
||||
handled:
|
||||
OS_FREE(hdr);
|
||||
|
||||
no_event:
|
||||
// notify again if there are more events to process in queue
|
||||
if (ble_has_event()) {
|
||||
xTaskNotify(OS_GET_CURRENT_TASK(), BLE_APP_NOTIFY_MASK, eSetBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ble_task_init(const BTDriverConfig *config) {
|
||||
const BleTaskInitInfo init_info = {
|
||||
.semph = xSemaphoreCreateBinary(),
|
||||
.config = config,
|
||||
};
|
||||
PBL_ASSERTN(init_info.semph);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Starting BLE Task...");
|
||||
|
||||
OS_TASK_CREATE("BT", prv_ble_peripheral_task, (void *)&init_info,
|
||||
1280 /* bytes */, mainBLE_PERIPHERAL_TASK_PRIORITY,
|
||||
DialogTaskList[DialogTask_Ble]);
|
||||
OS_ASSERT(DialogTaskList[DialogTask_Ble]);
|
||||
|
||||
xSemaphoreTake(init_info.semph, portMAX_DELAY);
|
||||
vSemaphoreDelete(init_info.semph);
|
||||
}
|
189
src/bluetooth-fw/da1468x/controller/main/src/bonding_sync.c
Normal file
189
src/bluetooth-fw/da1468x/controller/main/src/bonding_sync.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <bluetooth/bonding_sync.h>
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <bluetooth/sm_types.h>
|
||||
|
||||
#include <btutil/sm_util.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bonding_flags.h"
|
||||
#include "connection.h"
|
||||
#include "dialog_utils.h"
|
||||
#include "hc_protocol/hc_endpoint_bonding_sync.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "ppogatt_emulated_server_wa.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ble_common.h"
|
||||
#include "ble_gap.h"
|
||||
#include "storage.h"
|
||||
|
||||
static key_ltk_t *prv_create_remote_ltk_from_info(const SMRemoteEncryptionInfo *enc_info) {
|
||||
key_ltk_t *ltk = kernel_zalloc_check(sizeof(key_ltk_t));
|
||||
ltk->rand = enc_info->rand;
|
||||
ltk->ediv = enc_info->ediv;
|
||||
memcpy(ltk->key, enc_info->ltk.data, sizeof(ltk->key));
|
||||
ltk->key_size = sizeof(ltk->key);
|
||||
return ltk;
|
||||
}
|
||||
|
||||
static key_ltk_t *prv_create_local_ltk_from_info(const SMLocalEncryptionInfo *enc_info) {
|
||||
key_ltk_t *ltk = kernel_zalloc_check(sizeof(key_ltk_t));
|
||||
ltk->rand = enc_info->rand;
|
||||
ltk->ediv = enc_info->ediv;
|
||||
memcpy(ltk->key, enc_info->ltk.data, sizeof(ltk->key));
|
||||
ltk->key_size = sizeof(ltk->key);
|
||||
return ltk;
|
||||
}
|
||||
|
||||
void bonding_sync_handle_hc_add(const BleBonding *bonding) {
|
||||
storage_acquire();
|
||||
const SMPairingInfo *info = &bonding->pairing_info;
|
||||
bd_address_t addr;
|
||||
dialog_utils_bt_device_to_bd_address(&bonding->pairing_info.identity, &addr);
|
||||
device_t *dev = find_device_by_addr(&addr, true /* create */);
|
||||
|
||||
dev->paired = true;
|
||||
dev->bonded = true;
|
||||
dev->is_gateway = bonding->is_gateway;
|
||||
dev->flags = bonding->flags;
|
||||
dev->mitm = info->is_mitm_protection_enabled;
|
||||
|
||||
// The LTK that's used when the local device is the slave.
|
||||
if (info->is_remote_encryption_info_valid) {
|
||||
dev->ltk = prv_create_remote_ltk_from_info(&info->remote_encryption_info);
|
||||
}
|
||||
|
||||
// The LTK that's used when the local device is the master
|
||||
// (we call it "local", Dialog calls it "remote"... :-S )
|
||||
if (info->is_local_encryption_info_valid) {
|
||||
dev->remote_ltk = prv_create_local_ltk_from_info(&info->local_encryption_info);
|
||||
}
|
||||
|
||||
if (info->is_remote_identity_info_valid) {
|
||||
if (sm_is_pairing_info_irk_not_used(&info->irk)) {
|
||||
dev->irk = NULL;
|
||||
} else {
|
||||
key_irk_t *irk = kernel_zalloc_check(sizeof(key_irk_t));
|
||||
memcpy(&irk->key, info->irk.data, sizeof(irk->key));
|
||||
dev->irk = irk;
|
||||
}
|
||||
}
|
||||
|
||||
storage_release();
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Added pairing for "BT_DEVICE_ADDRESS_FMT,
|
||||
BT_DEVICE_ADDRESS_XPLODE(info->identity.address));
|
||||
}
|
||||
|
||||
void bonding_sync_handle_hc_remove(const BleBonding *bonding) {
|
||||
const SMPairingInfo *info = &bonding->pairing_info;
|
||||
uint16_t conn_idx = BLE_CONN_IDX_INVALID;
|
||||
storage_acquire();
|
||||
bd_address_t addr;
|
||||
dialog_utils_bt_device_to_bd_address(&bonding->pairing_info.identity, &addr);
|
||||
device_t *dev = find_device_by_addr(&addr, false /* create */);
|
||||
if (dev) {
|
||||
conn_idx = dev->conn_idx;
|
||||
device_remove_pairing(dev);
|
||||
}
|
||||
storage_release();
|
||||
|
||||
// set is_gateway to false within the Connection
|
||||
if (conn_idx != BLE_CONN_IDX_INVALID) {
|
||||
Connection *conn = connection_by_idx(conn_idx);
|
||||
if (conn) {
|
||||
connection_set_gateway(conn, false);
|
||||
}
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Removed pairing for "BT_DEVICE_ADDRESS_FMT,
|
||||
BT_DEVICE_ADDRESS_XPLODE(info->identity.address));
|
||||
}
|
||||
|
||||
void bonding_sync_handle_pairing_completed(Connection *connection, uint16_t conn_idx) {
|
||||
BleBonding bonding = {};
|
||||
SMPairingInfo *info = &bonding.pairing_info;
|
||||
bool success = false;
|
||||
|
||||
storage_acquire();
|
||||
device_t *dev = find_device_by_conn_idx(conn_idx);
|
||||
if (dev && dev->bonded) {
|
||||
// The LTK that's used when the local device is the master.
|
||||
if (dev->ltk) {
|
||||
memcpy(&info->remote_encryption_info.ltk, dev->ltk->key,
|
||||
sizeof(info->remote_encryption_info.ltk));
|
||||
info->remote_encryption_info.rand = dev->ltk->rand;
|
||||
info->remote_encryption_info.ediv = dev->ltk->ediv;
|
||||
info->is_remote_encryption_info_valid = true;
|
||||
}
|
||||
// The LTK that's used when the local device is the slave
|
||||
// (we call it "local", Dialog calls it "remote"... :-S )
|
||||
if (dev->remote_ltk) {
|
||||
memcpy(&info->local_encryption_info.ltk, dev->remote_ltk->key,
|
||||
sizeof(info->local_encryption_info.ltk));
|
||||
info->local_encryption_info.ediv = dev->remote_ltk->ediv;
|
||||
info->local_encryption_info.rand = dev->remote_ltk->rand;
|
||||
info->is_local_encryption_info_valid = true;
|
||||
}
|
||||
if (dev->irk) {
|
||||
memcpy(&info->irk, dev->irk->key, sizeof(info->irk));
|
||||
dialog_utils_bd_address_to_bt_device(&dev->addr, &info->identity);
|
||||
info->is_remote_identity_info_valid = true;
|
||||
} else {
|
||||
// If the device did not exchange an IRK, we have been given the devices identity address and
|
||||
// this is how we will associate our connection with the device in the future. Therefore, mark
|
||||
// is_remote_identity_info_valid as true but leave the irk 0'ed out to indicate it is unused
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Device did not exchange an IRK");
|
||||
info->is_remote_identity_info_valid = true;
|
||||
dialog_utils_bd_address_to_bt_device(&dev->addr, &info->identity);
|
||||
}
|
||||
info->is_mitm_protection_enabled = dev->mitm;
|
||||
bonding.is_gateway = connection_is_gateway(connection);
|
||||
|
||||
if (connection_should_pin_address(connection)) {
|
||||
bonding.should_pin_address = true;
|
||||
connection_get_local_address(connection, &bonding.pinned_address);
|
||||
}
|
||||
|
||||
BleBondingFlag flags = 0;
|
||||
|
||||
if (connection_should_auto_accept_re_pairing(connection)) {
|
||||
flags |= BleBondingFlag_ShouldAutoAcceptRePairing;
|
||||
}
|
||||
|
||||
if (connection_is_reversed_ppogatt_enabled(connection)) {
|
||||
flags |= BleBondingFlag_IsReversedPPoGATTEnabled;
|
||||
}
|
||||
|
||||
bonding.flags = flags;
|
||||
dev->flags = flags;
|
||||
|
||||
success = true;
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Pairing fail? conn=%p, dev=%p", connection, dev);
|
||||
}
|
||||
storage_release();
|
||||
|
||||
if (success) {
|
||||
hc_endpoint_bonding_sync_add(&bonding);
|
||||
}
|
||||
}
|
77
src/bluetooth-fw/da1468x/controller/main/src/chip_id.c
Normal file
77
src/bluetooth-fw/da1468x/controller/main/src/chip_id.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "chip_id.h"
|
||||
|
||||
#include "bsp_definitions.h"
|
||||
#include "da1468x_mem_map.h"
|
||||
#include "hw_otpc.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define OTP_CELL_SIZE_BYTES (8)
|
||||
|
||||
static uint32_t prv_get_cell_offset_for_address(uintptr_t otp_addr) {
|
||||
return (otp_addr - MEMORY_OTP_BASE) / OTP_CELL_SIZE_BYTES;
|
||||
}
|
||||
|
||||
static bool prv_read_otp_bytes(uintptr_t otp_addr, uint8_t *buffer_out, size_t length_bytes) {
|
||||
// Ceil!
|
||||
size_t num_words = (length_bytes + (sizeof(uint32_t) - 1)) / sizeof(uint32_t);
|
||||
|
||||
// Ensure our accesses are aligned by using this temporary buffer:
|
||||
uint32_t temp_buffer[num_words];
|
||||
bool rv = hw_otpc_fifo_read(temp_buffer, prv_get_cell_offset_for_address(otp_addr),
|
||||
HW_OTPC_WORD_LOW, num_words, false /* spare_rows */);
|
||||
memcpy(buffer_out, temp_buffer, length_bytes);
|
||||
|
||||
if (!rv) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Failed to read OTP %p", (void *)otp_addr);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool dialog_chip_id_copy(DialogChipID *chip_id_out) {
|
||||
hw_otpc_init();
|
||||
|
||||
#if dg_configEXT_CRYSTAL_FREQ == EXT_CRYSTAL_IS_16M
|
||||
HW_OTPC_SYS_CLK_FREQ freq = HW_OTPC_SYS_CLK_FREQ_16;
|
||||
#elif dg_configEXT_CRYSTAL_FREQ == EXT_CRYSTAL_IS_32M
|
||||
HW_OTPC_SYS_CLK_FREQ freq = HW_OTPC_SYS_CLK_FREQ_32;
|
||||
#else
|
||||
# error "Unsupported sysclk frequency"
|
||||
#endif
|
||||
|
||||
hw_otpc_set_speed(freq);
|
||||
|
||||
// Read the position/package/timestamp info:
|
||||
bool rv = prv_read_otp_bytes(OTP_POS_PKG_TIMESTAMP_ADDRESS, (uint8_t *)&chip_id_out->info,
|
||||
sizeof(chip_id_out->info));
|
||||
|
||||
// Read the chip identifier string:
|
||||
rv &= prv_read_otp_bytes(OTP_CHIP_ID_ADDRESS, (uint8_t *)&chip_id_out->chip_id,
|
||||
sizeof(chip_id_out->chip_id));
|
||||
|
||||
// Zero terminate, just in case:
|
||||
chip_id_out->chip_id[sizeof(chip_id_out->chip_id) - 1] = '\0';
|
||||
|
||||
hw_otpc_disable();
|
||||
|
||||
return rv;
|
||||
}
|
487
src/bluetooth-fw/da1468x/controller/main/src/connection.c
Normal file
487
src/bluetooth-fw/da1468x/controller/main/src/connection.c
Normal file
|
@ -0,0 +1,487 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "connection.h"
|
||||
#include "connection_private.h"
|
||||
|
||||
#include "ble_task.h"
|
||||
#include "util/list.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "os/mutex.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <btutil/bt_device.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void ppogatt_destroy_state(PPoGATTWorkAroundState *state);
|
||||
|
||||
typedef struct ForEachCbData {
|
||||
ConnectionForEachCallback cb;
|
||||
void *data;
|
||||
} ForEachCbData;
|
||||
|
||||
static Connection *s_connections;
|
||||
|
||||
// We expect several threads to call this module (i.e ble_task, host_transport) so
|
||||
// make it threadsafe
|
||||
static PebbleRecursiveMutex *s_connection_lock = NULL;
|
||||
|
||||
void connection_module_init(void) {
|
||||
s_connection_lock = mutex_create_recursive();
|
||||
}
|
||||
|
||||
static void prv_lock(void) {
|
||||
mutex_lock_recursive(s_connection_lock);
|
||||
}
|
||||
|
||||
static void prv_unlock(void) {
|
||||
mutex_unlock_recursive(s_connection_lock);
|
||||
}
|
||||
|
||||
Connection *connection_create(uint16_t conn_idx, const BTDeviceInternal *initial_addr,
|
||||
const BTDeviceAddress *local_addr,
|
||||
const BleConnectionParams *params) {
|
||||
ble_task_assert_is_executing_on_ble_task();
|
||||
Connection *connection;
|
||||
prv_lock();
|
||||
{
|
||||
connection = kernel_malloc_check(sizeof(Connection));
|
||||
*connection = (Connection) {
|
||||
.conn_idx = conn_idx,
|
||||
.initial_addr = *initial_addr,
|
||||
.local_addr = *local_addr,
|
||||
.conn_params = *params,
|
||||
};
|
||||
s_connections = (Connection *)list_prepend((ListNode *)s_connections, (ListNode *)connection);
|
||||
}
|
||||
prv_unlock();
|
||||
return connection;
|
||||
}
|
||||
|
||||
void connection_destroy(Connection *connection) {
|
||||
ble_task_assert_is_executing_on_ble_task();
|
||||
prv_lock();
|
||||
{
|
||||
if (connection_is_valid(connection)) {
|
||||
list_remove((ListNode *)connection, (ListNode **)&s_connections, NULL);
|
||||
|
||||
ListNode *tmp = (ListNode *) connection->gatt_op_list;
|
||||
while (tmp) {
|
||||
ListNode *next = list_get_next((ListNode *)tmp);
|
||||
kernel_free(tmp);
|
||||
tmp = next;
|
||||
}
|
||||
|
||||
if (connection->ppogatt_wa_state) {
|
||||
ppogatt_destroy_state(connection->ppogatt_wa_state);
|
||||
}
|
||||
|
||||
kernel_free(connection);
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
bool connection_is_valid(Connection *connection) {
|
||||
bool rv;
|
||||
prv_lock();
|
||||
{
|
||||
rv = list_contains((ListNode *)s_connections, (const ListNode *)connection);
|
||||
}
|
||||
prv_unlock();
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool prv_connection_by_idx_list_filter_cb(ListNode *found_node, void *data) {
|
||||
uint16_t conn_idx = (uintptr_t)data;
|
||||
return (((Connection *)found_node)->conn_idx == conn_idx);
|
||||
}
|
||||
|
||||
Connection *connection_by_idx(uint16_t conn_idx) {
|
||||
Connection *connection;
|
||||
prv_lock();
|
||||
{
|
||||
connection = (Connection *)list_find(
|
||||
(ListNode *)s_connections, prv_connection_by_idx_list_filter_cb,
|
||||
(void *)(uintptr_t)conn_idx);
|
||||
}
|
||||
prv_unlock();
|
||||
return connection;
|
||||
}
|
||||
|
||||
Connection *connection_by_idx_check(uint16_t conn_idx) {
|
||||
Connection *connection;
|
||||
prv_lock();
|
||||
{
|
||||
connection = connection_by_idx(conn_idx);
|
||||
PBL_ASSERTN(connection);
|
||||
}
|
||||
prv_unlock();
|
||||
return connection;
|
||||
}
|
||||
|
||||
static bool prv_connection_by_address_list_filter_cb(ListNode *found_node, void *data) {
|
||||
const BTDeviceInternal *desired_addr = (const BTDeviceInternal *)data;
|
||||
const Connection *connection = (const Connection *)found_node;
|
||||
return (bt_device_internal_equal(desired_addr, &connection->initial_addr) ||
|
||||
(connection->has_updated_addr &&
|
||||
bt_device_internal_equal(desired_addr, &connection->updated_addr)));
|
||||
}
|
||||
|
||||
Connection *connection_by_address(const BTDeviceInternal *addr) {
|
||||
Connection *connection;
|
||||
prv_lock();
|
||||
{
|
||||
connection = (Connection *)list_find(
|
||||
(ListNode *)s_connections, prv_connection_by_address_list_filter_cb, (void *)addr);
|
||||
}
|
||||
prv_unlock();
|
||||
return connection;
|
||||
}
|
||||
|
||||
Connection *connection_by_address_check(const BTDeviceInternal *addr) {
|
||||
Connection *connection;
|
||||
prv_lock();
|
||||
{
|
||||
connection = connection_by_address(addr);
|
||||
PBL_ASSERTN(connection);
|
||||
}
|
||||
prv_unlock();
|
||||
return connection;
|
||||
}
|
||||
|
||||
static bool prv_connection_for_each_cb(ListNode *found_node, void *data) {
|
||||
ForEachCbData *cb_data = (ForEachCbData *)data;
|
||||
cb_data->cb((Connection *)found_node, cb_data->data);
|
||||
return false; // Force not found -- iterate the entire list
|
||||
}
|
||||
|
||||
void connection_for_each(ConnectionForEachCallback cb, void *data) {
|
||||
prv_lock();
|
||||
ForEachCbData cb_data = { .cb = cb, .data = data };
|
||||
{
|
||||
list_find((ListNode *)s_connections, prv_connection_for_each_cb, &cb_data);
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
// Note: Using a lock on the getters doesn't really accomplish anything since
|
||||
// the fields we read are either set on creation or atomic operations. We do
|
||||
// run the risk that the connection handle is no longer valid and could add
|
||||
// some extra checks in the future to protect against that I suppose
|
||||
|
||||
uint16_t connection_get_idx(Connection *connection) {
|
||||
uint16_t idx;
|
||||
prv_lock();
|
||||
{
|
||||
idx = connection->conn_idx;
|
||||
}
|
||||
prv_unlock();
|
||||
return idx;
|
||||
}
|
||||
|
||||
void connection_get_address(const Connection *connection, BTDeviceInternal *addr_buf) {
|
||||
prv_lock();
|
||||
{
|
||||
if (connection->has_updated_addr) {
|
||||
*addr_buf = connection->updated_addr;
|
||||
} else {
|
||||
*addr_buf = connection->initial_addr;
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void connection_get_local_address(Connection *connection, BTDeviceAddress *addr_buf) {
|
||||
prv_lock();
|
||||
{
|
||||
*addr_buf = connection->local_addr;
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void connection_get_conn_params(const Connection *connection,
|
||||
BleConnectionParams *params_out) {
|
||||
prv_lock();
|
||||
{
|
||||
*params_out = connection->conn_params;
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void connection_get_address_by_idx_check(uint16_t conn_idx, BTDeviceInternal *addr_out) {
|
||||
prv_lock();
|
||||
{
|
||||
Connection *connection = connection_by_idx_check(conn_idx);
|
||||
connection_get_address(connection, addr_out);
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
uint8_t connection_get_last_pairing_result(uint16_t conn_idx) {
|
||||
uint8_t rv = 0;
|
||||
prv_lock();
|
||||
{
|
||||
Connection *connection = connection_by_idx(conn_idx);
|
||||
if (connection) {
|
||||
rv = connection->last_pairing_result;
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
return rv;
|
||||
}
|
||||
|
||||
PPoGATTWorkAroundState *connection_get_ppogatt_wa_state(Connection *connection) {
|
||||
PPoGATTWorkAroundState *state;
|
||||
prv_lock();
|
||||
{
|
||||
state = connection->ppogatt_wa_state;
|
||||
}
|
||||
prv_unlock();
|
||||
return state;
|
||||
}
|
||||
|
||||
bool connection_is_gateway(Connection *connection) {
|
||||
bool val;
|
||||
prv_lock();
|
||||
{
|
||||
val = connection->is_gateway;
|
||||
}
|
||||
prv_unlock();
|
||||
return val;
|
||||
}
|
||||
|
||||
static bool prv_get_flag(const Connection *connection, ConnectionFlag flag) {
|
||||
bool val;
|
||||
prv_lock();
|
||||
{
|
||||
uint32_t bit = (1 << flag);
|
||||
val = ((connection->flags & bit) == bit);
|
||||
}
|
||||
prv_unlock();
|
||||
return val;
|
||||
}
|
||||
|
||||
bool connection_is_subscribed_to_connection_status_notifications(const Connection *connection) {
|
||||
return prv_get_flag(connection, ConnectionFlag_IsSubscribedToConnectionStatusNotifications);
|
||||
}
|
||||
|
||||
bool connection_should_pin_address(const Connection *connection) {
|
||||
return prv_get_flag(connection, ConnectionFlag_ShouldPinAddress);
|
||||
}
|
||||
|
||||
bool connection_should_auto_accept_re_pairing(const Connection *connection) {
|
||||
return prv_get_flag(connection, ConnectionFlag_ShouldAutoAcceptRePairing);
|
||||
}
|
||||
|
||||
bool connection_is_reversed_ppogatt_enabled(const Connection *connection) {
|
||||
return prv_get_flag(connection, ConnectionFlag_IsReversedPPoGATTEnabled);
|
||||
}
|
||||
|
||||
bool connection_is_subscribed_to_gatt_mtu_notifications(const Connection *connection) {
|
||||
return prv_get_flag(connection, ConnectionFlag_IsSubscribedToGattMtuNotifications);
|
||||
}
|
||||
|
||||
bool connection_is_subscribed_to_conn_param_notifications(const Connection *connection) {
|
||||
return prv_get_flag(connection, ConnectionFlag_IsSubscribedToConnParamNotifications);
|
||||
}
|
||||
|
||||
//
|
||||
// Setters
|
||||
//
|
||||
|
||||
void connection_set_gateway(Connection *connection, bool is_gateway) {
|
||||
prv_lock();
|
||||
{
|
||||
if (connection_is_valid(connection)) {
|
||||
connection->is_gateway = is_gateway;
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
static char prv_debug_char_for_flag(ConnectionFlag flag) {
|
||||
static const char s_debug_chars[] = {
|
||||
[ConnectionFlag_IsSubscribedToConnectionStatusNotifications] = 'S',
|
||||
[ConnectionFlag_IsSubscribedToGattMtuNotifications] = 'M',
|
||||
[ConnectionFlag_IsSubscribedToConnParamNotifications] = 'C',
|
||||
[ConnectionFlag_ShouldPinAddress] = 'P',
|
||||
[ConnectionFlag_ShouldAutoAcceptRePairing] = 'A',
|
||||
[ConnectionFlag_IsReversedPPoGATTEnabled] = 'R',
|
||||
};
|
||||
return s_debug_chars[flag];
|
||||
}
|
||||
|
||||
static void prv_set_flag(Connection *connection, bool enabled, ConnectionFlag flag) {
|
||||
prv_lock();
|
||||
{
|
||||
if (connection_is_valid(connection)) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Changing connection flag: %c=%u",
|
||||
prv_debug_char_for_flag(flag), enabled);
|
||||
if (enabled) {
|
||||
connection->flags |= (1 << flag);
|
||||
} else {
|
||||
connection->flags &= ~(1 << flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void connection_set_subscribed_to_connection_status_notifications(
|
||||
Connection *connection, bool is_subscribed) {
|
||||
prv_set_flag(connection, is_subscribed,
|
||||
ConnectionFlag_IsSubscribedToConnectionStatusNotifications);
|
||||
}
|
||||
|
||||
void connection_set_subscribed_to_gatt_mtu_notifications(
|
||||
Connection *connection, bool is_subscribed) {
|
||||
prv_set_flag(connection, is_subscribed,
|
||||
ConnectionFlag_IsSubscribedToGattMtuNotifications);
|
||||
}
|
||||
|
||||
void connection_set_subscribed_to_conn_param_notifications(
|
||||
Connection *connection, bool is_subscribed) {
|
||||
prv_set_flag(connection, is_subscribed,
|
||||
ConnectionFlag_IsSubscribedToConnParamNotifications);
|
||||
}
|
||||
|
||||
void connection_set_should_pin_address(Connection *connection, bool should_pin_address) {
|
||||
prv_set_flag(connection, should_pin_address,
|
||||
ConnectionFlag_ShouldPinAddress);
|
||||
}
|
||||
|
||||
void connection_set_should_auto_accept_re_pairing(Connection *connection,
|
||||
bool should_auto_accept_re_pairing) {
|
||||
prv_set_flag(connection, should_auto_accept_re_pairing,
|
||||
ConnectionFlag_ShouldAutoAcceptRePairing);
|
||||
}
|
||||
|
||||
void connection_set_reversed_ppogatt_enabled(Connection *connection,
|
||||
bool is_reversed_ppogatt_enabled) {
|
||||
prv_set_flag(connection, is_reversed_ppogatt_enabled,
|
||||
ConnectionFlag_IsReversedPPoGATTEnabled);
|
||||
}
|
||||
|
||||
void connection_set_last_pairing_result(uint16_t conn_idx, uint8_t result) {
|
||||
prv_lock();
|
||||
{
|
||||
Connection *connection = connection_by_idx(conn_idx);
|
||||
if (connection) {
|
||||
connection->last_pairing_result = result;
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void connection_set_ppogatt_wa_state(Connection *connection, PPoGATTWorkAroundState *state) {
|
||||
prv_lock();
|
||||
{
|
||||
connection->ppogatt_wa_state = state;
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void connection_set_conn_params(Connection *connection, const BleConnectionParams *params) {
|
||||
prv_lock();
|
||||
{
|
||||
if (connection_is_valid(connection)) {
|
||||
connection->conn_params = *params;
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void connection_update_address(Connection *connection, const BTDeviceInternal *updated_addr) {
|
||||
prv_lock();
|
||||
{
|
||||
if (connection_is_valid(connection)) {
|
||||
connection->updated_addr = *updated_addr;
|
||||
connection->has_updated_addr = true;
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
//
|
||||
// Other functions
|
||||
//
|
||||
|
||||
void connection_enqueue_gatt_op(Connection *connection, uintptr_t context_ref,
|
||||
GattRespDest resp_dest, GattOpType op_type) {
|
||||
prv_lock();
|
||||
{
|
||||
GattOperation *node = kernel_malloc_check(sizeof(GattOperation));
|
||||
*node = (GattOperation) {
|
||||
.object_ref = context_ref,
|
||||
.resp_dest = resp_dest,
|
||||
.op_type = op_type,
|
||||
};
|
||||
connection->gatt_op_list = (GattOperation *)
|
||||
list_get_head(list_append((ListNode *)connection->gatt_op_list, (ListNode *)node));
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
bool connection_dequeue_gatt_op(Connection *connection, uintptr_t *context_ref,
|
||||
GattRespDest *resp_dest, GattOpType expected_op_type) {
|
||||
bool rv = true;
|
||||
prv_lock();
|
||||
{
|
||||
GattOperation *tmp = connection->gatt_op_list;
|
||||
if (!tmp) {
|
||||
rv = false;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
*context_ref = tmp->object_ref;
|
||||
*resp_dest = tmp->resp_dest;
|
||||
|
||||
PBL_ASSERTN(tmp->op_type == expected_op_type);
|
||||
|
||||
connection->gatt_op_list = (GattOperation *)list_pop_head((ListNode *)connection->gatt_op_list);
|
||||
kernel_free(tmp);
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool connection_pop_gatt_op(Connection *connection) {
|
||||
bool rv = true;
|
||||
prv_lock();
|
||||
{
|
||||
GattOperation *tmp_tail = (GattOperation *)list_get_tail((ListNode *)connection->gatt_op_list);
|
||||
if (!tmp_tail) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Gatt: Attempted to pop recent op when list empty");
|
||||
PBL_ASSERTN(0);
|
||||
}
|
||||
|
||||
connection->gatt_op_list = (GattOperation *)
|
||||
list_get_head(list_pop_tail((ListNode *)connection->gatt_op_list));
|
||||
kernel_free(tmp_tail);
|
||||
}
|
||||
prv_unlock();
|
||||
return rv;
|
||||
}
|
606
src/bluetooth-fw/da1468x/controller/main/src/core_dump.c
Normal file
606
src/bluetooth-fw/da1468x/controller/main/src/core_dump.c
Normal file
|
@ -0,0 +1,606 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "core_dump.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sdk_defs.h"
|
||||
#include "hw_spi.h"
|
||||
#include "hw_timer0.h"
|
||||
#include "hw_timer1.h"
|
||||
#include "hw_watchdog.h"
|
||||
#include "sys_power_mgr.h"
|
||||
#include "sys_clock_mgr.h"
|
||||
|
||||
#include "da1468x_mem_map.h"
|
||||
#include "platform_devices.h"
|
||||
#include "util/build_id.h"
|
||||
#include "util/crc32.h"
|
||||
|
||||
#include "host_transport_impl.h"
|
||||
#include "host_transport_protocol.h"
|
||||
#include "reboot_reason.h"
|
||||
|
||||
#define TIMER0_CLK_RATE (32000)
|
||||
#define TIMER0_RELOAD_VALUE (65535)
|
||||
#define TIMER0_1_S (TIMER0_CLK_RATE / 1)
|
||||
#define TIMER0_1_MS (TIMER0_CLK_RATE / 1000)
|
||||
#define TIMER0_10_MS (TIMER0_CLK_RATE / 100)
|
||||
|
||||
// Modified from ad_spi.c. We don't want to use DMA here!
|
||||
#define CORE_DUMP_SPI_SLAVE_TO_EXT_MASTER(bus, name, _word_mode, pol_mode, _phase_mode, \
|
||||
_dma_channel) \
|
||||
const spi_device_config dev_##name = { \
|
||||
.bus_id = HW_##bus, \
|
||||
.bus_res_id = RES_ID_##bus, \
|
||||
.hw_init.smn_role = HW_SPI_MODE_SLAVE, \
|
||||
.hw_init.cs_pad = { 0, 0 }, \
|
||||
.hw_init.word_mode = _word_mode, \
|
||||
.hw_init.polarity_mode = pol_mode, \
|
||||
.hw_init.phase_mode = _phase_mode, \
|
||||
.hw_init.xtal_freq = 0, \
|
||||
.hw_init.fifo_mode = HW_SPI_FIFO_RX_TX, \
|
||||
.hw_init.disabled = 0, \
|
||||
.hw_init.use_dma = _dma_channel >= 0, \
|
||||
.hw_init.rx_dma_channel = _dma_channel, \
|
||||
.hw_init.tx_dma_channel = _dma_channel + 1, \
|
||||
};
|
||||
|
||||
CORE_DUMP_SPI_SLAVE_TO_EXT_MASTER(SPI1, CORE_DUMP_SPI, CONFIG_SPI_WORD_MODE,
|
||||
CONFIG_SPI_POL_MODE, CONFIG_SPI_PHASE_MODE,
|
||||
CONFIG_SPI_DMA_CHANNEL);
|
||||
|
||||
void DMA_Handler(void); // This is the Dialog SPI/DMA Interrupt handler
|
||||
|
||||
|
||||
static volatile bool s_spi_dma_complete;
|
||||
|
||||
static const spi_device_config * const s_spi = &dev_CORE_DUMP_SPI;
|
||||
|
||||
static bool s_core_dump_initiated = false;
|
||||
extern uint32_t *g_stacked_regs;
|
||||
|
||||
// The following structures are accessed below in __asm. Because the Cortex-M0 doesn't handle
|
||||
// unaligned accesses automatically, and we don't want to do it ourselves, let's force the
|
||||
// elements we care about to be aligned correctly.
|
||||
typedef struct {
|
||||
int8_t padding[3];
|
||||
CoreDumpThreadInfo ti;
|
||||
} CoreDumpThreadInfo_Wrapper;
|
||||
static CoreDumpThreadInfo_Wrapper ALIGN(4) s_thread_info;
|
||||
_Static_assert(((uint32_t)&s_thread_info.ti.registers[0] % 4) == 0,
|
||||
"Structure not correctly aligned");
|
||||
static CoreDumpExtraRegInfo ALIGN(4) s_extra_info;
|
||||
|
||||
|
||||
extern void debug_uart_init(void);
|
||||
|
||||
// Linker symbols
|
||||
extern const uint8_t __heap_start;
|
||||
extern const uint8_t __zero_table_start;
|
||||
extern const uint8_t __text_start;
|
||||
extern const uint8_t __text_end;
|
||||
extern const uint8_t __data_start__;
|
||||
extern const uint8_t __zero_initialized_start__;
|
||||
extern const uint8_t __ble_vars_start__;
|
||||
extern const uint8_t __cache_ram_zi_start__;
|
||||
extern const uint8_t __debug_region_start__;
|
||||
|
||||
extern const uint32_t __vector_table_length[];
|
||||
extern const uint32_t __heap_length[];
|
||||
extern const uint32_t __text_length[];
|
||||
extern const uint32_t __rwdata_length[];
|
||||
extern const uint32_t __stack_bss_length[];
|
||||
extern const uint32_t __ble_vars_length[];
|
||||
extern const uint32_t __cache_ram_length[];
|
||||
extern const uint32_t __debug_region_length[];
|
||||
|
||||
extern const ElfExternalNote DIALOG_BUILD_ID;
|
||||
|
||||
static const MemoryRegion s_MemoryRegions[MemoryRegionTagCount] = {
|
||||
{ .tag = MemoryRegionTag_Text, // .text is first so it's easily excluded on crc check
|
||||
.start = &__text_start,
|
||||
.length = (uint32_t)__text_length,
|
||||
},
|
||||
{ .tag = MemoryRegionTag_VectorTable,
|
||||
.start = (void *)DATA_RAM_BASE_ADDRESS,
|
||||
.length = (uint32_t)__vector_table_length,
|
||||
},
|
||||
{ .tag = MemoryRegionTag_Heap,
|
||||
.start = &__heap_start,
|
||||
.length = (uint32_t)__heap_length,
|
||||
},
|
||||
{ .tag = MemoryRegionTag_RwData,
|
||||
.start = &__data_start__,
|
||||
.length = (uint32_t)__rwdata_length,
|
||||
},
|
||||
{ .tag = MemoryRegionTag_StackAndBss,
|
||||
.start = &__zero_initialized_start__,
|
||||
.length = (uint32_t)__stack_bss_length,
|
||||
},
|
||||
{ .tag = MemoryRegionTag_BleVariables,
|
||||
.start = &__ble_vars_start__,
|
||||
.length = (uint32_t)__ble_vars_length,
|
||||
},
|
||||
{ .tag = MemoryRegionTag_CacheRam,
|
||||
.start = &__cache_ram_zi_start__,
|
||||
.length = (uint32_t)__cache_ram_length,
|
||||
},
|
||||
{
|
||||
.tag = MemoryRegionTag_RebootReason,
|
||||
.start = &__debug_region_start__,
|
||||
.length = (uint32_t)__debug_region_length,
|
||||
},
|
||||
};
|
||||
|
||||
static uint16_t prv_timer_get_ticks(void) {
|
||||
return hw_timer0_get_on();
|
||||
}
|
||||
|
||||
static uint16_t prv_timer_delta(uint16_t start_ticks, uint16_t end_ticks) {
|
||||
// The timer is configured to repeatedly count down from 65535, resetting at 0 back to 65535.
|
||||
// The delta calculated here takes advantage of the underflow to produce the correct result.
|
||||
return start_ticks - end_ticks;
|
||||
}
|
||||
|
||||
static bool prv_timer_check_delta(uint16_t start_ticks, uint16_t delta_ticks) {
|
||||
uint16_t curr_ticks = prv_timer_get_ticks();
|
||||
bool s = (prv_timer_delta(start_ticks, curr_ticks) < delta_ticks);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void prv_timer_delay_1ms(void) {
|
||||
uint16_t start_ticks = prv_timer_get_ticks();
|
||||
while (prv_timer_check_delta(start_ticks, TIMER0_1_MS)) {};
|
||||
}
|
||||
|
||||
|
||||
static NORETURN prv_low_power_mode(void) {
|
||||
// Give the host a few seconds to reboot the chip before we attempt to power everthing off (If
|
||||
// everything gets powered off the BT reboot reason persisted in RAM (and flushed to analytics on
|
||||
// reboot) will be lost)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
hw_watchdog_set_pos_val(0xFF);
|
||||
uint16_t wd_start_ticks = prv_timer_get_ticks();
|
||||
while (prv_timer_check_delta(wd_start_ticks, TIMER0_1_S)) {
|
||||
}
|
||||
}
|
||||
|
||||
REG_SET_BIT(CRG_TOP, CLK_RADIO_REG, BLE_LP_RESET); /* reset BLE LP timer */
|
||||
|
||||
hw_timer0_disable();
|
||||
hw_timer1_disable();
|
||||
|
||||
// The following block of code was lifted from sys_power_mgr.c:apply_wfi().
|
||||
//
|
||||
// TODO: It's not entirely clear what's going on, but it does reduce current draw to ~0 uA. At
|
||||
// some point, it's probably worth figuring out how to cleanly hibernate (PBL-42430)
|
||||
hw_cpm_enable_clockless();
|
||||
hw_cpm_disable_xtal32k();
|
||||
SCB->SCR |= (1 << 2);
|
||||
cm_sys_clk_sleep(true);
|
||||
hw_cpm_pll_sys_off();
|
||||
hw_cpm_activate_pad_latches();
|
||||
hw_cpm_power_down_periph_pd();
|
||||
hw_cpm_wait_per_power_down();
|
||||
hw_cpm_wait_rad_power_down();
|
||||
hw_cpm_rfcu_clk_off();
|
||||
hw_cpm_disable_rc16();
|
||||
hw_cpm_dcdc_off();
|
||||
hw_cpm_ldo_io_ret_off();
|
||||
|
||||
hw_watchdog_freeze();
|
||||
|
||||
while (true) {
|
||||
__WFI();
|
||||
}
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
// Timer0 is very limited -- there is no way to reset the count until it's triggered, so it's
|
||||
// not useful as a generic one-shot. We'll let it run free and calculate deltas.
|
||||
static void prv_timer_enable(void) {
|
||||
timer0_config cfg = {
|
||||
.clk_src = HW_TIMER0_CLK_SRC_SLOW,
|
||||
.on_clock_div = false,
|
||||
.on_reload = TIMER0_RELOAD_VALUE,
|
||||
};
|
||||
hw_timer0_init(&cfg);
|
||||
hw_timer0_enable();
|
||||
}
|
||||
|
||||
// The initial handshake is required because a) we can't use INT as a busy/ready signal, and b)
|
||||
// we can't be sure that the host isn't still clocking out data from the previous command.
|
||||
// Shift incoming data through a pattern buffer. When we get a match, send our response.
|
||||
static bool prv_initial_handshake(void) {
|
||||
uint8_t rx_index = 0;
|
||||
uint8_t tx_index = 0;
|
||||
uint8_t buffer[sizeof(core_dump_connect_ping)];
|
||||
uint16_t tx_start_ticks;
|
||||
bool rx_mode;
|
||||
|
||||
printf("Waiting for host:\n");
|
||||
|
||||
for (int count = 10; count > 0; --count) {
|
||||
printf(".");
|
||||
// Feed the watchdog
|
||||
hw_watchdog_set_pos_val(0xFF);
|
||||
|
||||
// Configure for RX mode
|
||||
rx_mode = true;
|
||||
hw_spi_change_fifo_mode(s_spi->bus_id, HW_SPI_FIFO_RX_ONLY);
|
||||
|
||||
// Spin for 1 second before handling the watchdog/INT line
|
||||
uint16_t wd_start_ticks = prv_timer_get_ticks();
|
||||
while (prv_timer_check_delta(wd_start_ticks, TIMER0_1_S)) {
|
||||
if (rx_mode) {
|
||||
// Has a byte arrived?
|
||||
if (!hw_spi_get_interrupt_status(s_spi->bus_id)) {
|
||||
continue;
|
||||
}
|
||||
hw_spi_clear_interrupt(s_spi->bus_id);
|
||||
buffer[rx_index++] = hw_spi_fifo_read8(s_spi->bus_id);
|
||||
|
||||
if (rx_index == sizeof(core_dump_connect_ping)) {
|
||||
if (memcmp(buffer, core_dump_connect_ping, sizeof(core_dump_connect_ping)) == 0) {
|
||||
// Handshake received. Switch to tx mode.
|
||||
hw_spi_change_fifo_mode(s_spi->bus_id, HW_SPI_FIFO_TX_ONLY);
|
||||
rx_mode = false;
|
||||
tx_index = 0;
|
||||
tx_start_ticks = prv_timer_get_ticks();
|
||||
continue;
|
||||
} else {
|
||||
// Buffer is full. Shift everything down one byte.
|
||||
rx_index--;
|
||||
memmove(&buffer[0], &buffer[1], rx_index);
|
||||
}
|
||||
}
|
||||
} else { // Tx Mode
|
||||
if (hw_spi_is_tx_fifo_full(s_spi->bus_id)) {
|
||||
// Don't spin for too long, just in case the host doesn't clock out our response
|
||||
if (!prv_timer_check_delta(tx_start_ticks, TIMER0_10_MS)) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
hw_spi_fifo_write8(s_spi->bus_id, core_dump_connect_response[tx_index++]);
|
||||
if (tx_index == sizeof(core_dump_connect_response)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pulse the INT line to wake the host
|
||||
host_transport_set_mcu_int(true);
|
||||
prv_timer_delay_1ms(); // The timer IRQ just fired so the counter won't be near roll-over
|
||||
host_transport_set_mcu_int(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback for SPI DMA complete
|
||||
static void prv_spi_dma_tx_cb(void *user_data, uint16_t transferred) {
|
||||
s_spi_dma_complete = true;
|
||||
}
|
||||
|
||||
// Wait for DMA to complete. Note that since we don't have interrupts enabled, we're polling for
|
||||
// the interrupt and then calling the DMA Interrupt Handler directly. This will cause the
|
||||
// callback above to be called.
|
||||
static void prv_wait_for_dma(bool is_tx_only) {
|
||||
while (!s_spi_dma_complete) {
|
||||
if (NVIC_GetPendingIRQ(DMA_IRQn)) {
|
||||
NVIC_ClearPendingIRQ(DMA_IRQn);
|
||||
DMA_Handler();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: It looks like SPI/DMA complete interrupts will fire when the data fills the FIFO, not
|
||||
// when the FIFO is actually empty. Let's spin until the FIFO is drained, if we're transmitting.
|
||||
// If we're receiving, this function will spin forever. See ad_spi.c:322.
|
||||
if (is_tx_only) {
|
||||
hw_spi_wait_while_busy(s_spi->bus_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_get_cmd(uint8_t *buffer, uint8_t len) {
|
||||
|
||||
|
||||
s_spi_dma_complete = false;
|
||||
hw_spi_read_buf(s_spi->bus_id, buffer, len, prv_spi_dma_tx_cb, NULL);
|
||||
|
||||
// Now signal the host that we're ready to RX.
|
||||
host_transport_set_mcu_int(true);
|
||||
prv_wait_for_dma(false);
|
||||
host_transport_set_mcu_int(false);
|
||||
}
|
||||
|
||||
static void prv_send_response(const uint8_t *buffer, uint16_t len) {
|
||||
uint32_t crc = crc32(CRC32_INIT, buffer, len);
|
||||
|
||||
s_spi_dma_complete = false;
|
||||
hw_spi_write_buf(s_spi->bus_id, buffer, len, prv_spi_dma_tx_cb, NULL);
|
||||
|
||||
// Now signal the host that we're ready to TX.
|
||||
host_transport_set_mcu_int(true);
|
||||
prv_wait_for_dma(true);
|
||||
host_transport_set_mcu_int(false);
|
||||
|
||||
prv_timer_delay_1ms();
|
||||
|
||||
// Now send the CRC
|
||||
s_spi_dma_complete = false;
|
||||
hw_spi_write_buf(s_spi->bus_id, (void *)&crc, sizeof(crc), prv_spi_dma_tx_cb, NULL);
|
||||
|
||||
host_transport_set_mcu_int(true);
|
||||
prv_wait_for_dma(true);
|
||||
host_transport_set_mcu_int(false);
|
||||
}
|
||||
|
||||
static void prv_cmd_handler(bool text_crc_matches) {
|
||||
CoreDumpSPICmd cmd;
|
||||
|
||||
printf("\nHost connected\n");
|
||||
|
||||
while (true) {
|
||||
// Feed the watchdog
|
||||
hw_watchdog_set_pos_val(0xFF);
|
||||
|
||||
prv_get_cmd((uint8_t *)&cmd, sizeof(cmd));
|
||||
if (crc32(CRC32_INIT, &cmd, sizeof(cmd)) != CRC32_RESIDUE) {
|
||||
printf("cmd CRC failed");
|
||||
prv_low_power_mode();
|
||||
}
|
||||
|
||||
// Feed the watchdog
|
||||
hw_watchdog_set_pos_val(0xFF);
|
||||
|
||||
switch (cmd.cmd) {
|
||||
case CoreDumpCmd_GetTextCRC: {
|
||||
uint16_t response = text_crc_matches; // This can't be a bool (1-byte). The Dialog SPI/DMA
|
||||
// Tx will lock-up and not signal completion.
|
||||
printf("get text crc %d\n", sizeof(response));
|
||||
prv_send_response((uint8_t *)&response, sizeof(response));
|
||||
}
|
||||
break;
|
||||
|
||||
case CoreDumpCmd_GetBuildID:
|
||||
printf("build id\n");
|
||||
prv_send_response((uint8_t *)&DIALOG_BUILD_ID, BUILD_ID_TOTAL_EXPECTED_LEN);
|
||||
break;
|
||||
|
||||
case CoreDumpCmd_ReadRegionTable:
|
||||
printf("region table\n");
|
||||
prv_send_response((uint8_t *)s_MemoryRegions, sizeof(s_MemoryRegions));
|
||||
break;
|
||||
|
||||
case CoreDumpCmd_ReadMemory: {
|
||||
printf("read memory %p %d\n", cmd.addr, cmd.len);
|
||||
prv_send_response(cmd.addr, cmd.len);
|
||||
}
|
||||
break;
|
||||
|
||||
case CoreDumpCmd_ReadRunningThreadInfo:
|
||||
printf("read TI\n");
|
||||
prv_send_response((uint8_t *)&s_thread_info.ti, sizeof(s_thread_info.ti));
|
||||
break;
|
||||
|
||||
case CoreDumpCmd_ReadExtraThreadInfo:
|
||||
printf("read ExtraTB\n");
|
||||
prv_send_response((uint8_t *)&s_extra_info, sizeof(s_extra_info));
|
||||
break;
|
||||
|
||||
case CoreDumpCmd_LowPowerMode:
|
||||
printf("low power mode\n");
|
||||
prv_low_power_mode();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NORETURN core_dump(bool user_requested) {
|
||||
// Feed the watchdog before we continue
|
||||
hw_watchdog_set_pos_val(0xFF);
|
||||
|
||||
// Disable interrupts, should we not have been called from fault context
|
||||
portDISABLE_INTERRUPTS();
|
||||
|
||||
// Reconfig debug serial
|
||||
debug_uart_init();
|
||||
|
||||
printf("\n\nStarting Core Dump\n");
|
||||
|
||||
// Big problem if we re-enter here - it likely means we encountered and exception during
|
||||
// the core dump
|
||||
if (s_core_dump_initiated) {
|
||||
printf("CD: re-entered\n");
|
||||
|
||||
// Update the reboot reason. Preserve the LR.
|
||||
RebootReason reason;
|
||||
reboot_reason_get(&reason);
|
||||
reason.code = RebootReasonCode_CoreDumpReentered;
|
||||
reboot_reason_set(&reason);
|
||||
|
||||
// Go into low-power mode. Hard reset isn't useful.
|
||||
prv_low_power_mode();
|
||||
}
|
||||
s_core_dump_initiated = true;
|
||||
|
||||
|
||||
if (user_requested) {
|
||||
printf("CD: user requested\n");
|
||||
RebootReason reason = { .code = RebootReasonCode_CoreDumpRequested };
|
||||
reboot_reason_set(&reason);
|
||||
}
|
||||
|
||||
// Save the registers that would have been stacked by a fault into CoreDumpThreadInfo struct
|
||||
uint32_t *regs = (uint32_t *)s_thread_info.ti.registers;
|
||||
if (user_requested) {
|
||||
__asm volatile (
|
||||
" mov r0, %[regs] \n"
|
||||
" add r0, r0, #4 \n" // skip over r0, we can't save it
|
||||
" stmia r0!, {r1-r3} \n" // save r1-r3
|
||||
" add r0, r0, #32 \n" // skip r4-r11 (8 * 4bytes)
|
||||
|
||||
" mov r1, r12 \n" // save r12
|
||||
" str r1, [r0] \n"
|
||||
" add r0, r0, #4 \n"
|
||||
|
||||
" mov r1, lr \n" // save lr
|
||||
" str r1, [r0] \n"
|
||||
" add r0, r0, #4 \n"
|
||||
|
||||
" mov r1, pc \n" // save pc
|
||||
" str r1, [r0] \n"
|
||||
" add r0, r0, #4 \n"
|
||||
|
||||
" mrs r1, xpsr \n" // save xpsr
|
||||
" str r1, [r0] \n"
|
||||
" add r0, r0, #4 \n"
|
||||
|
||||
: [regs] "+r" (regs)
|
||||
:
|
||||
: "r0", "r1"
|
||||
);
|
||||
} else if (g_stacked_regs != NULL) {
|
||||
// Copy the stacked registers from the stacked register set
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_R0] = g_stacked_regs[Stacked_Register_R0];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_R1] = g_stacked_regs[Stacked_Register_R1];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_R2] = g_stacked_regs[Stacked_Register_R2];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_R3] = g_stacked_regs[Stacked_Register_R3];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_R12] = g_stacked_regs[Stacked_Register_R12];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_LR] = g_stacked_regs[Stacked_Register_LR];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_PC] = g_stacked_regs[Stacked_Register_PC];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_XPSR] = \
|
||||
g_stacked_regs[Stacked_Register_xPSR];
|
||||
s_thread_info.ti.registers[portCANONICAL_REG_INDEX_SP] = (uint32_t)g_stacked_regs;
|
||||
}
|
||||
|
||||
// Copy the remaining registers
|
||||
regs = (uint32_t *)&s_thread_info.ti.registers;
|
||||
__asm volatile (
|
||||
" mov r0, %[regs] \n"
|
||||
" add r0, r0, #16 \n" // skip r0-r3
|
||||
" stmia r0!, {r4-r7} \n" // save r4-r7
|
||||
|
||||
" mov r4, r8 \n" // save r8-r11
|
||||
" mov r5, r9 \n"
|
||||
" mov r6, r10 \n"
|
||||
" mov r7, r11 \n"
|
||||
" stmia r0!, {r4-r7} \n"
|
||||
|
||||
: [regs] "+r" (regs)
|
||||
:
|
||||
: "r0", "r1", "r4", "r5", "r6", "r7"
|
||||
);
|
||||
|
||||
// Copy the extra registers
|
||||
regs = (uint32_t *)&s_extra_info;
|
||||
__asm volatile (
|
||||
" mov r0, %[regs] \n"
|
||||
|
||||
" mrs r1, msp \n" // msp
|
||||
" mrs r2, psp \n" // psp
|
||||
" stmia r0!, {r1-r2} \n"
|
||||
|
||||
" mrs r3, primask \n" // primask
|
||||
" strb r3, [r0] \n"
|
||||
" add r0, r0, #3 \n" // skip basepri & faultmask -- not on CM0
|
||||
|
||||
" mrs r1, control \n" // control
|
||||
" strb r1, [r0] \n"
|
||||
|
||||
: [regs] "+r" (regs)
|
||||
:
|
||||
: "r0", "r1"
|
||||
);
|
||||
|
||||
// Fill in the rest of the CoreDumpThreadInfo struct
|
||||
bool in_isr_task = ((s_thread_info.ti.registers[portCANONICAL_REG_INDEX_XPSR] & 0x1FF) != 0);
|
||||
|
||||
if (in_isr_task) {
|
||||
strncpy((char *)s_thread_info.ti.name, "ISR", sizeof(s_thread_info.ti.name));
|
||||
s_thread_info.ti.id = (uint32_t)1;
|
||||
s_thread_info.ti.running = false;
|
||||
} else {
|
||||
TaskHandle_t current_task_id = xTaskGetCurrentTaskHandle();
|
||||
char *task_name = pcTaskGetTaskName(current_task_id);
|
||||
s_thread_info.ti.running = true;
|
||||
s_thread_info.ti.id = (uint32_t)current_task_id;
|
||||
if (task_name) {
|
||||
strncpy((char *)s_thread_info.ti.name, task_name, sizeof(s_thread_info.ti.name));
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that our stack pointer is somewhere rational
|
||||
register uint32_t *sp __asm("sp");
|
||||
extern uint32_t __StackLimit, __StackTop;
|
||||
uint32_t *stack_limit = &__StackLimit;
|
||||
uint32_t *stack_top = &__StackTop;
|
||||
printf("Stack: %p - %p\n", stack_limit, stack_top);
|
||||
printf("Our SP = %p\n", sp);
|
||||
if (!((stack_limit <= sp) || (sp <= stack_top))) {
|
||||
printf("Stack is not sane (%p)! Best of luck.\n", sp);
|
||||
}
|
||||
|
||||
// Calculate the Application CRC
|
||||
uint32_t *text_start = (uint32_t *)&__text_start;
|
||||
uint32_t *text_end = (uint32_t *)&__text_end;
|
||||
size_t text_length = (uint32_t)__text_length;
|
||||
uint32_t text_crc_calculated, text_crc_from_image;
|
||||
printf(".text: %p - %p, 0x%zX\n", text_start, text_end, text_length);
|
||||
text_crc_calculated = crc32(CRC32_INIT, text_start, text_length);
|
||||
text_crc_from_image = *text_end; // The CRC is at __exidx_end.
|
||||
printf(".text calculated CRC32 = 0x%08" PRIX32 "\n", text_crc_calculated);
|
||||
printf(".text from image CRC32 = 0x%08" PRIX32 "\n", text_crc_from_image);
|
||||
if (text_crc_calculated != text_crc_from_image) {
|
||||
printf(".text CRC32 does not match!\n");
|
||||
}
|
||||
|
||||
// Reset the DMA controller
|
||||
for (HW_DMA_CHANNEL channel = HW_DMA_CHANNEL_0; channel < HW_DMA_CHANNEL_INVALID; channel++) {
|
||||
hw_dma_channel_enable(channel, HW_DMA_STATE_DISABLED);
|
||||
}
|
||||
|
||||
// Configure the SPI module.
|
||||
hw_spi_reset(s_spi->bus_id);
|
||||
hw_spi_init(s_spi->bus_id, &s_spi->hw_init);
|
||||
|
||||
// Configure the hardware/GPIO interface. Use the host_transport code for now.
|
||||
host_transport_init_periph();
|
||||
host_transport_configure_spi_scs_pin(SCSPinFunction_SPI_CS);
|
||||
host_transport_set_mcu_int(false);
|
||||
|
||||
prv_timer_enable();
|
||||
|
||||
// Loop until we get a connection from the host or 10 seconds elapses.
|
||||
if (prv_initial_handshake()) {
|
||||
prv_cmd_handler(text_crc_calculated == text_crc_from_image);
|
||||
}
|
||||
|
||||
printf("Done. Going to sleep");
|
||||
|
||||
// Go to low power mode? Reset doesn't actually get back to the ROM bootrom.
|
||||
prv_low_power_mode();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Since at least FreeRTOS V7.5.3 uxTopUsedPriority is no longer
|
||||
* present in the kernel, so it has to be supplied by other means for
|
||||
* OpenOCD's threads awareness.
|
||||
*
|
||||
* Add this file to your project, and, if you're using --gc-sections,
|
||||
* ``--undefined=uxTopUsedPriority'' (or
|
||||
* ``-Wl,--undefined=uxTopUsedPriority'' when using gcc for final
|
||||
* linking) to your LDFLAGS; same with all the other symbols you need.
|
||||
*/
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include <util/attributes.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
// CC: libutil's attributes.h also defines this:
|
||||
// #define USED __attribute__((used))
|
||||
#else
|
||||
#define USED
|
||||
#endif
|
||||
|
||||
const int USED uxTopUsedPriority = configMAX_PRIORITIES;
|
60
src/bluetooth-fw/da1468x/controller/main/src/debug_gpio.c
Normal file
60
src/bluetooth-fw/da1468x/controller/main/src/debug_gpio.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "board.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
// Dialog SDK
|
||||
#include <hw_gpio.h>
|
||||
|
||||
static void prv_config_gpio_as_output_and_set_state(DebugGPIOInfo *gpio, bool active) {
|
||||
// While GPIO pin itself can retain state when the system enters deep sleep the PXX_MODE_REG
|
||||
// holding the configuration gets reset so reprogram the gpio pin function
|
||||
hw_gpio_set_pin_function(gpio->port, gpio->pin, HW_GPIO_MODE_OUTPUT, HW_GPIO_FUNC_GPIO);
|
||||
if (active) {
|
||||
hw_gpio_set_active(gpio->port, gpio->pin);
|
||||
} else {
|
||||
hw_gpio_set_inactive(gpio->port, gpio->pin);
|
||||
}
|
||||
|
||||
gpio->is_active = active;
|
||||
}
|
||||
|
||||
void debug_gpio_init(void) {
|
||||
for (int i = 0; i < DEBUG_GPIOS->num_debug_gpios; i++) {
|
||||
prv_config_gpio_as_output_and_set_state(&DEBUG_GPIOS->debug_gpio[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
void debug_gpio_toggle(int debug_gpio_num) {
|
||||
if (debug_gpio_num >= DEBUG_GPIOS->num_debug_gpios) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Invalid debug gpio id");
|
||||
return;
|
||||
}
|
||||
|
||||
DebugGPIOInfo *gpio = &DEBUG_GPIOS->debug_gpio[debug_gpio_num];
|
||||
prv_config_gpio_as_output_and_set_state(gpio, !gpio->is_active);
|
||||
}
|
||||
|
||||
void debug_gpio_set_active(int debug_gpio_num, bool is_active) {
|
||||
if (debug_gpio_num >= DEBUG_GPIOS->num_debug_gpios) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Invalid debug gpio id");
|
||||
return;
|
||||
}
|
||||
|
||||
DebugGPIOInfo *gpio = &DEBUG_GPIOS->debug_gpio[debug_gpio_num];
|
||||
prv_config_gpio_as_output_and_set_state(gpio, is_active);
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "reboot_reason.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
void debug_reboot_reason_print(void) {
|
||||
RebootReason reason;
|
||||
if (!reboot_reason_get(&reason)) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Dialog: Invalid reboot reason");
|
||||
goto clear;
|
||||
};
|
||||
|
||||
switch (reason.code) {
|
||||
// Normal stuff
|
||||
case RebootReasonCode_Unknown:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to Unknown.");
|
||||
break;
|
||||
case RebootReasonCode_Shutdown:
|
||||
// FIXME PBL-38181: For some reason, the reboot reason shutdown never gets persisted/recovered
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to Shutdown.");
|
||||
break;
|
||||
// Error occurred
|
||||
case RebootReasonCode_Watchdog:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to watchdog, stuck task mask: 0x%"PRIx32,
|
||||
reason.extra);
|
||||
break;
|
||||
case RebootReasonCode_Assert:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to Assert: LR 0x%"PRIx32, reason.extra);
|
||||
break;
|
||||
case RebootReasonCode_HardFault:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to HardFault: LR 0x%"PRIx32, reason.extra);
|
||||
break;
|
||||
case RebootReasonCode_StackOverflow:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to StackOverflow");
|
||||
break;
|
||||
case RebootReasonCode_DialogPlatformReset:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS,
|
||||
"Dialog: Rebooted due to DialogPlatformReset: error 0x%"PRIx32, reason.extra);
|
||||
break;
|
||||
case RebootReasonCode_CoreDumpReentered:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Re-entered core dump. Possibly valid -> LR 0x%"PRIx32,
|
||||
reason.extra);
|
||||
break;
|
||||
case RebootReasonCode_CoreDumpRequested:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: User requested core dump");
|
||||
break;
|
||||
case RebootReasonCode_RomError:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to RomError: ERRORTYPESTAT 0x%"PRIx32,
|
||||
reason.extra);
|
||||
break;
|
||||
case RebootReasonCode_NMI:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to NMI");
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Dialog: Rebooted due to Unrecognized Reason");
|
||||
break;
|
||||
}
|
||||
|
||||
clear:
|
||||
reboot_reason_clear();
|
||||
}
|
44
src/bluetooth-fw/da1468x/controller/main/src/debugger.c
Normal file
44
src/bluetooth-fw/da1468x/controller/main/src/debugger.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "debugger.h"
|
||||
|
||||
#include "hw_cpm.h"
|
||||
#include "hw_watchdog.h"
|
||||
#include "sdk_defs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if NO_WATCHDOG
|
||||
void debugger_await(void) {
|
||||
hw_watchdog_freeze();
|
||||
hw_cpm_enable_debugger();
|
||||
|
||||
printf("\nAttach gdb and enter:\n"
|
||||
"(gdb) set $r0 = 0\n"
|
||||
"(gdb) si\n"
|
||||
"(gdb) <hit return a few times to return to the point where the fault happened>\n");
|
||||
|
||||
NVIC_ClearPendingIRQ(HardFault_IRQn);
|
||||
__asm("mov r0, #1");
|
||||
while (true) {
|
||||
register uintptr_t r0 __asm("r0");
|
||||
if (r0 == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "dialog_analytics/analytics.h"
|
||||
|
||||
#include "hc_protocol/hc_endpoint_analytics.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "kernel/util/freertos_utils.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <util/list.h>
|
||||
#include <util/size.h>
|
||||
#include <os/mutex.h>
|
||||
|
||||
// FreeRTOS
|
||||
#include "FreeRTOSConfig.h"
|
||||
|
||||
// Dialog SDK
|
||||
#include "sys_rtc.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MS_PER_SECOND (1000)
|
||||
|
||||
// Stopwatch
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
DialogAnalyticsMetric metric;
|
||||
uint32_t starting_ticks;
|
||||
uint32_t count_per_sec;
|
||||
} AnalyticsStopwatchNode;
|
||||
|
||||
// Analytic Containers
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
DialogAnalyticsMetric metric;
|
||||
uint32_t value;
|
||||
} AnalyticsNode;
|
||||
|
||||
static ListNode *s_analytics_list = NULL;
|
||||
static ListNode *s_stopwatch_list = NULL;
|
||||
static PebbleRecursiveMutex *s_analytics_lock = NULL;
|
||||
|
||||
// Locking Functions
|
||||
|
||||
static void prv_lock(void) {
|
||||
mutex_lock_recursive(s_analytics_lock);
|
||||
}
|
||||
|
||||
static void prv_unlock(void) {
|
||||
mutex_unlock_recursive(s_analytics_lock);
|
||||
}
|
||||
|
||||
// Analytic Node Functions
|
||||
|
||||
static bool prv_compare_analytic(ListNode *found_node, void *data) {
|
||||
AnalyticsNode *node = (AnalyticsNode *)found_node;
|
||||
return (node->metric == (DialogAnalyticsMetric)data);
|
||||
}
|
||||
|
||||
static AnalyticsNode *prv_find_analytic(DialogAnalyticsMetric metric) {
|
||||
return (AnalyticsNode *)list_find(
|
||||
s_analytics_list, prv_compare_analytic, (void *)(uintptr_t)metric);
|
||||
}
|
||||
|
||||
static void prv_add_analytic_node(DialogAnalyticsMetric metric) {
|
||||
AnalyticsNode *node = kernel_malloc_check(sizeof(AnalyticsNode));
|
||||
*node = (AnalyticsNode) {
|
||||
.metric = metric,
|
||||
};
|
||||
s_analytics_list = list_prepend(s_analytics_list, (ListNode *)node);
|
||||
}
|
||||
|
||||
// Stopwatch Node Functions
|
||||
|
||||
static bool prv_compare_stopwatch(ListNode *found_node, void *data) {
|
||||
AnalyticsStopwatchNode *stopwatch = (AnalyticsStopwatchNode *)found_node;
|
||||
return (stopwatch->metric == (DialogAnalyticsMetric)data);
|
||||
}
|
||||
|
||||
static AnalyticsStopwatchNode *prv_find_stopwatch_node(DialogAnalyticsMetric metric) {
|
||||
return (AnalyticsStopwatchNode *)list_find(
|
||||
s_stopwatch_list, prv_compare_stopwatch, (void *)(uintptr_t)metric);
|
||||
}
|
||||
|
||||
static void prv_add_stopwatch(DialogAnalyticsMetric metric, uint32_t count_per_sec) {
|
||||
AnalyticsStopwatchNode *stopwatch = kernel_malloc_check(sizeof(AnalyticsStopwatchNode));
|
||||
*stopwatch = (AnalyticsStopwatchNode) {
|
||||
.metric = metric,
|
||||
.count_per_sec = count_per_sec,
|
||||
.starting_ticks = rtc_get(),
|
||||
};
|
||||
s_stopwatch_list = list_prepend(s_stopwatch_list, (ListNode *)stopwatch);
|
||||
}
|
||||
|
||||
static uint32_t prv_rtc_ticks_to_ms(uint32_t rtc_ticks) {
|
||||
return (uint32_t)(((uint32_t)rtc_ticks) * MS_PER_SECOND / configSYSTICK_CLOCK_HZ);
|
||||
}
|
||||
|
||||
static uint32_t prv_stopwatch_elapsed_ms(AnalyticsStopwatchNode *stopwatch,
|
||||
uint32_t current_ticks) {
|
||||
// Even if current_ticks is less than stopwatch->starting_ticks (because `rtc_get` rv rolled
|
||||
// over), this operation will still work correctly since we only use uint32_t
|
||||
// See unit test `test_dialog_analytics__stopwatch_rtc_rollover` for validation.
|
||||
const uint32_t difference = current_ticks - stopwatch->starting_ticks;
|
||||
const uint32_t dt_ms = prv_rtc_ticks_to_ms(difference);
|
||||
return (((uint32_t) stopwatch->count_per_sec) * dt_ms) / MS_PER_SECOND;
|
||||
}
|
||||
|
||||
// API Functions
|
||||
|
||||
// Used primarily for unit tests
|
||||
void analytics_init_private(uint32_t num_nodes) {
|
||||
s_analytics_lock = mutex_create_recursive();
|
||||
|
||||
// create and populate list of all Analytics
|
||||
for (uint32_t i = 0; i < num_nodes; i++) {
|
||||
prv_add_analytic_node(i);
|
||||
}
|
||||
}
|
||||
|
||||
void analytics_init(void) {
|
||||
analytics_init_private(DialogAnalyticMetric_Count);
|
||||
}
|
||||
|
||||
void analytics_reset_nodes(void) {
|
||||
prv_lock();
|
||||
|
||||
// Zero out all values
|
||||
ListNode *iter = s_analytics_list;
|
||||
while (iter) {
|
||||
AnalyticsNode *node = (AnalyticsNode *)iter;
|
||||
node->value = 0;
|
||||
iter = iter->next;
|
||||
}
|
||||
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void analytics_set(DialogAnalyticsMetric metric, uint32_t value) {
|
||||
prv_lock();
|
||||
|
||||
AnalyticsNode *node = prv_find_analytic(metric);
|
||||
if (node) {
|
||||
node->value = value;
|
||||
}
|
||||
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void analytics_add(DialogAnalyticsMetric metric, uint32_t amount) {
|
||||
prv_lock();
|
||||
|
||||
AnalyticsNode *node = prv_find_analytic(metric);
|
||||
if (node) {
|
||||
node->value += amount;
|
||||
}
|
||||
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void analytics_inc(DialogAnalyticsMetric metric) {
|
||||
analytics_add(metric, 1);
|
||||
}
|
||||
|
||||
void analytics_stopwatch_start_at_rate(DialogAnalyticsMetric metric, uint32_t count_per_second) {
|
||||
prv_lock();
|
||||
prv_add_stopwatch(metric, count_per_second);
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void analytics_stopwatch_start(DialogAnalyticsMetric metric) {
|
||||
analytics_stopwatch_start_at_rate(metric, MS_PER_SECOND);
|
||||
}
|
||||
|
||||
void analytics_stopwatch_stop(DialogAnalyticsMetric metric) {
|
||||
prv_lock();
|
||||
AnalyticsStopwatchNode *stopwatch = prv_find_stopwatch_node(metric);
|
||||
if (stopwatch) {
|
||||
analytics_add(metric, prv_stopwatch_elapsed_ms(stopwatch, rtc_get()));
|
||||
list_remove(&stopwatch->node, &s_stopwatch_list, NULL);
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
void analytics_stopwatches_update(uint32_t current_ticks) {
|
||||
prv_lock();
|
||||
|
||||
ListNode *cur = s_stopwatch_list;
|
||||
while (cur != NULL) {
|
||||
AnalyticsStopwatchNode *node = (AnalyticsStopwatchNode *)cur;
|
||||
analytics_add(node->metric, prv_stopwatch_elapsed_ms(node, current_ticks));
|
||||
node->starting_ticks = current_ticks;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
void analytics_each(AnalyticsEachCallback cb, void *context) {
|
||||
analytics_stopwatches_update(rtc_get());
|
||||
|
||||
prv_lock();
|
||||
ListNode *iter = s_analytics_list;
|
||||
while (iter) {
|
||||
AnalyticsNode *node = (AnalyticsNode *)iter;
|
||||
cb(node->metric, node->value, context);
|
||||
iter = iter->next;
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
|
||||
// Unit Test Helpers
|
||||
|
||||
uint32_t analytics_get_value(DialogAnalyticsMetric metric) {
|
||||
AnalyticsNode *node = prv_find_analytic(metric);
|
||||
if (node) {
|
||||
return node->value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void analytics_deinit(void) {
|
||||
while (s_stopwatch_list) {
|
||||
ListNode *head = s_stopwatch_list;
|
||||
list_remove(head, &s_stopwatch_list, NULL);
|
||||
kernel_free(head);
|
||||
}
|
||||
s_stopwatch_list = NULL;
|
||||
|
||||
while (s_analytics_list) {
|
||||
ListNode *head = s_analytics_list;
|
||||
list_remove(head, &s_analytics_list, NULL);
|
||||
kernel_free(head);
|
||||
}
|
||||
s_analytics_list = NULL;
|
||||
|
||||
mutex_destroy((PebbleMutex *)s_analytics_lock);
|
||||
}
|
34
src/bluetooth-fw/da1468x/controller/main/src/die.c
Normal file
34
src/bluetooth-fw/da1468x/controller/main/src/die.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "die.h"
|
||||
|
||||
#include "debugger.h"
|
||||
#include "core_dump.h"
|
||||
|
||||
NORETURN reset_due_to_software_failure(void) {
|
||||
#if NO_WATCHDOG
|
||||
debugger_await();
|
||||
while (1) {};
|
||||
__builtin_unreachable();
|
||||
#else
|
||||
core_dump(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void core_dump_and_reset_or_reboot(void) {
|
||||
reset_due_to_software_failure();
|
||||
}
|
50
src/bluetooth-fw/da1468x/controller/main/src/dis.c
Normal file
50
src/bluetooth-fw/da1468x/controller/main/src/dis.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <bluetooth/dis.h>
|
||||
|
||||
#include "dis_impl.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
// Dialog headers
|
||||
#include "dis.h"
|
||||
|
||||
static DisInfo s_dis_info;
|
||||
|
||||
void device_information_service_init(const DisInfo *dis_info) {
|
||||
s_dis_info = *dis_info;
|
||||
}
|
||||
|
||||
bool device_information_service_register(uint16_t start_hdl) {
|
||||
dis_device_info_t device_info = {
|
||||
.model_number = s_dis_info.model_number,
|
||||
.manufacturer = s_dis_info.manufacturer,
|
||||
.serial_number = s_dis_info.serial_number,
|
||||
.fw_revision = s_dis_info.fw_revision,
|
||||
.sw_revision = s_dis_info.sw_revision,
|
||||
};
|
||||
|
||||
bool rv = true;
|
||||
ble_service_t *ble_service = dis_init(NULL, &device_info, start_hdl);
|
||||
if (!ble_service) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "DIS failed to init!");
|
||||
rv = false;
|
||||
} else {
|
||||
PBL_ASSERTN(start_hdl == ble_service->start_h);
|
||||
}
|
||||
return rv;
|
||||
}
|
115
src/bluetooth-fw/da1468x/controller/main/src/fault_handling.c
Normal file
115
src/bluetooth-fw/da1468x/controller/main/src/fault_handling.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hw_watchdog.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "core_cm0.h"
|
||||
#include "core_dump.h"
|
||||
#include "die.h"
|
||||
#include "reboot_reason.h"
|
||||
#include "sdk_defs.h"
|
||||
#include "sys_watchdog.h"
|
||||
#include "core_dump.h"
|
||||
|
||||
uint32_t *g_stacked_regs = NULL;
|
||||
|
||||
static void prv_handle_fault(void) {
|
||||
// Feed the watchdog before we continue. NMI will be triggered very soon if we don't.
|
||||
// Don't freeze the watchdog. It's better if the core-dump code goes into low-power mode than
|
||||
// crash and drain the battery.
|
||||
hw_watchdog_set_pos_val(0xFF);
|
||||
|
||||
printf("R0=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_R0]);
|
||||
printf("R1=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_R1]);
|
||||
printf("R2=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_R2]);
|
||||
printf("R3=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_R3]);
|
||||
printf("R12=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_R12]);
|
||||
printf("LR=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_LR]);
|
||||
printf("PC=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_PC]);
|
||||
printf("xPSR=0x%08" PRIx32 "\n", g_stacked_regs[Stacked_Register_xPSR]);
|
||||
printf("SP=0x%08" PRIx32 "\n", (uint32_t) g_stacked_regs); // Stack Pointer
|
||||
|
||||
reset_due_to_software_failure();
|
||||
}
|
||||
|
||||
static bool prv_handle_hardfault_due_to_bkpt(void) {
|
||||
// Note: assumes Thumb2!
|
||||
const uint16_t instr = *(const uint16_t *)g_stacked_regs[Stacked_Register_PC];
|
||||
const uint16_t bkpt_instruction_opcode = 0xbe;
|
||||
if ((instr >> 8) != bkpt_instruction_opcode) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t arg = (instr & 0xff);
|
||||
|
||||
printf("\nbkpt %"PRIu8"! no dbgr?\n\n", arg);
|
||||
return true;
|
||||
}
|
||||
|
||||
void HardFault_HandlerC(uint32_t *fault_args) {
|
||||
// Save the fault_args pointer for later
|
||||
g_stacked_regs = fault_args;
|
||||
|
||||
bool is_bkpt = prv_handle_hardfault_due_to_bkpt();
|
||||
|
||||
uint32_t stacked_lr = g_stacked_regs[Stacked_Register_LR];
|
||||
RebootReason reason = {
|
||||
.code = is_bkpt ? RebootReasonCode_BreakpointButNoDebuggerAttached : RebootReasonCode_HardFault,
|
||||
.extra = stacked_lr,
|
||||
};
|
||||
reboot_reason_set(&reason);
|
||||
|
||||
printf("HardF:\n");
|
||||
prv_handle_fault();
|
||||
}
|
||||
|
||||
void NMI_HandlerC(uint32_t *fault_args) {
|
||||
// Save the fault_args pointer for later
|
||||
g_stacked_regs = fault_args;
|
||||
|
||||
uint32_t extra_reboot_info = 0;
|
||||
RebootReason reason;
|
||||
bool reboot_already_set = reboot_reason_get(&reason);
|
||||
RebootReasonCode code = RebootReasonCode_NMI;
|
||||
|
||||
// Are we here because the hw watchdog tripped?
|
||||
if (hw_watchdog_did_trip()) {
|
||||
if (hw_watchdog_recovered()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if dg_configUSE_WDOG
|
||||
extra_reboot_info = watchdog_get_tasks_waiting_on();
|
||||
code = RebootReasonCode_Watchdog;
|
||||
#endif
|
||||
}
|
||||
|
||||
// chained faults could manifest in an NMI or watchdog. We are more interested in the crash that
|
||||
// happened first
|
||||
if (!reboot_already_set) {
|
||||
reason = (RebootReason){
|
||||
.code = code,
|
||||
.extra = extra_reboot_info
|
||||
};
|
||||
reboot_reason_set(&reason);
|
||||
}
|
||||
|
||||
printf("NMI\n");
|
||||
prv_handle_fault();
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 <stdio.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "osal.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "reboot_reason.h"
|
||||
#include "util/heap.h"
|
||||
|
||||
/**
|
||||
* @brief Malloc fail hook
|
||||
*/
|
||||
void vApplicationMallocFailedHook( void )
|
||||
{
|
||||
/* vApplicationMallocFailedHook() will only be called if
|
||||
configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook
|
||||
function that will get called if a call to OS_MALLOC() fails.
|
||||
OS_MALLOC() is called internally by the kernel whenever a task, queue,
|
||||
timer or semaphore is created. It is also called by various parts of the
|
||||
demo application. If heap_1.c or heap_2.c are used, then the size of the
|
||||
heap available to OS_MALLOC() is defined by configTOTAL_HEAP_SIZE in
|
||||
FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
|
||||
to query the size of free heap space that remains (although it does not
|
||||
provide information on how the remaining heap might be fragmented). */
|
||||
taskDISABLE_INTERRUPTS();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Application idle task hook
|
||||
*/
|
||||
void vApplicationIdleHook( void )
|
||||
{
|
||||
/* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
|
||||
to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle
|
||||
task. It is essential that code added to this hook function never attempts
|
||||
to block in any way (for example, call OS_QUEUE_GET() with a block time
|
||||
specified, or call OS_DELAY()). If the application makes use of the
|
||||
OS_TASK_DELETE() API function (as this demo application does) then it is also
|
||||
important that vApplicationIdleHook() is permitted to return to its calling
|
||||
function, because it is the responsibility of the idle task to clean up
|
||||
memory allocated by the kernel to any task that has since been deleted. */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Application stack overflow hook
|
||||
*/
|
||||
void vApplicationStackOverflowHook( OS_TASK pxTask, char *pcTaskName )
|
||||
{
|
||||
( void ) pcTaskName;
|
||||
( void ) pxTask;
|
||||
|
||||
/* Run time stack overflow checking is performed if
|
||||
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook
|
||||
function is called if a stack overflow is detected. */
|
||||
taskDISABLE_INTERRUPTS();
|
||||
|
||||
printf("SO on %s", pcTaskName);
|
||||
|
||||
// TODO: Register which stack had the overflow.
|
||||
RebootReason reason = {
|
||||
.code = RebootReasonCode_StackOverflow,
|
||||
};
|
||||
reboot_reason_set(&reason);
|
||||
|
||||
while (1) {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Application tick hook
|
||||
*/
|
||||
void vApplicationTickHook( void )
|
||||
{
|
||||
#if mainCHECK_INTERRUPT_STACK == 1
|
||||
extern unsigned long _pvHeapStart[];
|
||||
|
||||
/* This function will be called by each tick interrupt if
|
||||
configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h. User code can be
|
||||
added here, but the tick hook is called from an interrupt context, so
|
||||
code must not attempt to block, and only the interrupt safe FreeRTOS API
|
||||
functions can be used (those that end in FromISR()). */
|
||||
|
||||
/* Manually check the last few bytes of the interrupt stack to check they
|
||||
have not been overwritten. Note - the task stacks are automatically
|
||||
checked for overflow if configCHECK_FOR_STACK_OVERFLOW is set to 1 or 2
|
||||
in FreeRTOSConifg.h, but the interrupt stack is not. */
|
||||
OS_ASSERT( memcmp( ( void * ) _pvHeapStart, ucExpectedInterruptStackValues, sizeof( ucExpectedInterruptStackValues ) ) == 0U );
|
||||
#endif /* mainCHECK_INTERRUPT_STACK */
|
||||
}
|
||||
|
||||
#ifdef JUST_AN_EXAMPLE_ISR
|
||||
|
||||
void Dummy_IRQHandler(void)
|
||||
{
|
||||
/* Clear the interrupt if necessary. */
|
||||
Dummy_ClearITPendingBit();
|
||||
|
||||
OS_EVENT_SIGNAL_FROM_ISR(xTestSemaphore);
|
||||
}
|
||||
|
||||
#endif /* JUST_AN_EXAMPLE_ISR */
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "connection.h"
|
||||
#include "gatt_wrapper.h"
|
||||
#include "system/logging.h"
|
||||
#include "hc_protocol/hc_endpoint_gap_service.h"
|
||||
|
||||
#include "att.h"
|
||||
#include "ble_att.h"
|
||||
#include "ble_common.h"
|
||||
#include "ble_gap.h"
|
||||
#include "ble_gattc.h"
|
||||
#include "ble_uuid.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void gap_le_device_name_handle_set(const char *name) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Setting Local Device Name: %s", name);
|
||||
ble_error_t e = ble_gap_device_name_set(name, ATT_PERM_READ);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Unexpected error setting name: %d", (int)e);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_gap_le_device_name_request(Connection *connection) {
|
||||
att_uuid_t name_id;
|
||||
ble_uuid_create16(ATT_CHAR_DEVICE_NAME, &name_id);
|
||||
|
||||
uint16_t conn_idx = connection_get_idx(connection);
|
||||
|
||||
ble_error_t e = gatt_wrapper_read_by_uuid(conn_idx, ATT_1ST_REQ_START_HDL, ATT_1ST_REQ_END_HDL,
|
||||
&name_id,
|
||||
(uintptr_t)hc_endpoint_gap_service_device_name_read,
|
||||
GattReqSourceController);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Unexpected error requesting remote device name (conn_idx %d): %d",
|
||||
conn_idx, e);
|
||||
}
|
||||
}
|
||||
|
||||
void gap_le_device_name_handle_request(const BTDeviceInternal *addr) {
|
||||
Connection *connection = connection_by_address(addr);
|
||||
if (!connection) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "No connection for dev_name_request: " BT_DEVICE_ADDRESS_FMT,
|
||||
BT_DEVICE_ADDRESS_XPLODE_PTR(&addr->address));
|
||||
return;
|
||||
}
|
||||
prv_gap_le_device_name_request(connection);
|
||||
}
|
||||
|
||||
static void prv_handle_request_all_cb(Connection *connection, void *data) {
|
||||
prv_gap_le_device_name_request(connection);
|
||||
}
|
||||
|
||||
void gap_le_device_name_handle_request_all(void) {
|
||||
connection_for_each(prv_handle_request_all_cb, NULL);
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "gatt_client_discovery.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "gatt_wrapper.h"
|
||||
#include "hc_protocol/hc_endpoint_discovery.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "ppogatt_emulated_server_wa.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/uuid.h"
|
||||
|
||||
// Dialog Headers
|
||||
#include "ble_gatt.h"
|
||||
#include "ble_gattc.h"
|
||||
#include "rwble_hl_error.h"
|
||||
#include "ble_uuid.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <bluetooth/gatt_service_types.h>
|
||||
#include <bluetooth/pebble_bt.h>
|
||||
#include <util/uuid.h>
|
||||
|
||||
#define GATT_SERVICE_UUID ((uint16_t) 0x1801)
|
||||
#define GATT_SERVICE_CHANGED_CHARACTERISTIC_UUID ((uint16_t) 0x2A05)
|
||||
#define GATT_CCCD_UUID ((uint16_t) 0x2902)
|
||||
|
||||
// Converts from a Dialog 'att_uuid_t' to firmware 'uuid' representation
|
||||
static void prv_att_uuid_to_uuid(const att_uuid_t *att_uuid, Uuid *uuid) {
|
||||
if (att_uuid->type == ATT_UUID_16) {
|
||||
uint16_t uuid16 = att_uuid->uuid16;
|
||||
*uuid = (Uuid){ BT_UUID_EXPAND(uuid16) };
|
||||
} else if (att_uuid->type == ATT_UUID_128) {
|
||||
*uuid = UuidMakeFromLEBytes(att_uuid->uuid128);
|
||||
} else { // should never happen
|
||||
PBL_ASSERTN(0);
|
||||
}
|
||||
}
|
||||
|
||||
T_STATIC HcProtocolDiscoveryServiceFoundPayload *prv_gatt_client_discovery_build_gatt_service(
|
||||
const ble_evt_gattc_browse_svc_t *svc, uint32_t *payload_size) {
|
||||
uint8_t type_counts[GATTC_ITEM_NUM_TYPES] = { };
|
||||
for (int i = 0; i < svc->num_items; i++) {
|
||||
const gattc_item_t *item = &svc->items[i];
|
||||
if (item->type >= GATTC_ITEM_NUM_TYPES) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Unexpected gattc type: 0x%x!", (int)item->type);
|
||||
}
|
||||
type_counts[item->type]++;
|
||||
}
|
||||
|
||||
uint32_t blob_size = COMPUTE_GATTSERVICE_SIZE_BYTES(
|
||||
type_counts[GATTC_ITEM_TYPE_CHARACTERISTIC], type_counts[GATTC_ITEM_TYPE_DESCRIPTOR],
|
||||
type_counts[GATTC_ITEM_TYPE_INCLUDE]);
|
||||
|
||||
uint32_t payload_extra_size =
|
||||
sizeof(HcProtocolDiscoveryServiceFoundPayload) - sizeof(GATTService);
|
||||
*payload_size = payload_extra_size + blob_size;
|
||||
HcProtocolDiscoveryServiceFoundPayload *payload = kernel_zalloc_check(*payload_size);
|
||||
|
||||
GATTService *service = &payload->service;
|
||||
|
||||
uint16_t base_handle = svc->start_h;
|
||||
|
||||
*service = (GATTService) {
|
||||
.size_bytes = blob_size,
|
||||
.att_handle = base_handle,
|
||||
.num_characteristics = type_counts[GATTC_ITEM_TYPE_CHARACTERISTIC],
|
||||
.num_descriptors = type_counts[GATTC_ITEM_TYPE_DESCRIPTOR],
|
||||
.num_att_handles_included_services = type_counts[GATTC_ITEM_TYPE_INCLUDE],
|
||||
};
|
||||
|
||||
prv_att_uuid_to_uuid(&svc->uuid, &service->uuid);
|
||||
|
||||
char uuid_str[UUID_STRING_BUFFER_LENGTH];
|
||||
uuid_to_string(&service->uuid, uuid_str);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG,
|
||||
"Found Service %s Handle: 0x%"PRIx16": 0x%"PRIx16" (%"PRIu32" byte blob)",
|
||||
uuid_str, base_handle, svc->end_h, blob_size);
|
||||
|
||||
uint16_t *att_handles_included_services =
|
||||
(uint16_t *)((uint8_t *)service + blob_size -
|
||||
sizeof(uint16_t) * type_counts[GATTC_ITEM_TYPE_INCLUDE]);
|
||||
uint16_t att_idx = 0;
|
||||
|
||||
GATTCharacteristic *char_dest = NULL;
|
||||
uint8_t *end_ptr = (uint8_t *)service->characteristics;
|
||||
|
||||
// A few notes:
|
||||
// + We expect the descriptors to be part of the last discovered characteristic
|
||||
// + We assume Includes can show up anywhere
|
||||
// + If we find descriptors before any characteristic is found, we bail (it shouldn't happen)
|
||||
for (int i = 0; i < svc->num_items; i++) {
|
||||
const gattc_item_t *item = &svc->items[i];
|
||||
if (item->type == GATTC_ITEM_TYPE_CHARACTERISTIC) {
|
||||
char_dest = (GATTCharacteristic *)end_ptr;
|
||||
*char_dest = (GATTCharacteristic) {
|
||||
.att_handle_offset = (item->c.value_handle - base_handle),
|
||||
.properties = item->c.properties,
|
||||
};
|
||||
prv_att_uuid_to_uuid(&item->uuid, &char_dest->uuid);
|
||||
end_ptr += sizeof(GATTCharacteristic);
|
||||
|
||||
uuid_to_string(&char_dest->uuid, uuid_str);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG,
|
||||
" Found Characteristic %s Handle 0x%"PRIx16, uuid_str, item->c.value_handle);
|
||||
} else if (item->type == GATTC_ITEM_TYPE_DESCRIPTOR) {
|
||||
if (char_dest == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No characteristic found for descriptor! handle=0x%x",
|
||||
(int)item->handle);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
GATTDescriptor *desc_dest = (GATTDescriptor *)end_ptr;
|
||||
*desc_dest = (GATTDescriptor) {
|
||||
.att_handle_offset = item->handle - base_handle,
|
||||
};
|
||||
prv_att_uuid_to_uuid(&item->uuid, &desc_dest->uuid);
|
||||
char_dest->num_descriptors++;
|
||||
end_ptr += sizeof(GATTDescriptor);
|
||||
|
||||
uuid_to_string(&desc_dest->uuid, uuid_str);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG,
|
||||
" Found Descriptor %s Handle 0x%x", uuid_str, item->handle);
|
||||
|
||||
} else if (item->type == GATTC_ITEM_TYPE_INCLUDE) {
|
||||
att_handles_included_services[att_idx] = item->i.start_h;
|
||||
att_idx++;
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Found Include, Start Handle 0x%x", item->i.start_h);
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
failure:
|
||||
kernel_free(payload);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void prv_write_cccd_cb(const ble_evt_gattc_write_completed_t *evt) {
|
||||
if (evt->status != ATT_ERROR_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Failed to write CCCD for idx %d Handle 0x%04X",
|
||||
evt->conn_idx, evt->handle);
|
||||
}
|
||||
}
|
||||
|
||||
//! Inspects the GATT service discovery event in search for the GATT Profile Service and its
|
||||
//! Service Changed characteristic. In case it's found, its ATT handle is recorded in the
|
||||
//! GAPLEConnection structure (by passing it up to the module. The characteristic's indications
|
||||
//! are subscribed to as well.
|
||||
static void prv_search_service_changed_handle(Connection *connection,
|
||||
const ble_evt_gattc_browse_svc_t *evt) {
|
||||
|
||||
// Check whether the service is the "GATT Profile Service":
|
||||
att_uuid_t gatt_service_uuid;
|
||||
ble_uuid_create16(GATT_SERVICE_UUID, &gatt_service_uuid);
|
||||
|
||||
att_uuid_t service_changed_characteristic_uuid;
|
||||
ble_uuid_create16(GATT_SERVICE_CHANGED_CHARACTERISTIC_UUID, &service_changed_characteristic_uuid);
|
||||
|
||||
// Attempt to find the "Service Changed" characteristic:
|
||||
for (uint16_t i = 0; i < evt->num_items; ++i) {
|
||||
const gattc_item_t *characteristic_info = &evt->items[i];
|
||||
|
||||
// Sanity: make sure this is a characteristic
|
||||
if (characteristic_info->type != GATTC_ITEM_TYPE_CHARACTERISTIC) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ble_uuid_equal(&characteristic_info->uuid, &service_changed_characteristic_uuid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found the Service Changed characteristic!
|
||||
att_uuid_t cccd_uuid;
|
||||
ble_uuid_create16(GATT_CCCD_UUID, &cccd_uuid);
|
||||
bool cccd_found = false;
|
||||
|
||||
// Attempt to find the CCCD:
|
||||
for (uint16_t j = 0; j < evt->num_items; ++j) {
|
||||
const gattc_item_t *descriptor_info = &evt->items[j];
|
||||
|
||||
// Sanity: make sure this is a descriptor
|
||||
if (descriptor_info->type != GATTC_ITEM_TYPE_DESCRIPTOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ble_uuid_equal(&cccd_uuid, &descriptor_info->uuid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found the CCCD!
|
||||
|
||||
// ... and finally subscribe to indications:
|
||||
const uint16_t cccd_value = GATT_CCC_INDICATIONS;
|
||||
ble_error_t result = gatt_wrapper_write(evt->conn_idx, descriptor_info->handle,
|
||||
sizeof(cccd_value), (const uint8_t *)&cccd_value,
|
||||
(uintptr_t)prv_write_cccd_cb,
|
||||
GattReqSourceController);
|
||||
if (result != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Failed to write CCCD %d", result);
|
||||
}
|
||||
cccd_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cccd_found) {
|
||||
// gah, Android doesn't seem to create the CCCD, but it does seem to auto-subscribe any
|
||||
// bonded device so let's assume this is what happen and notify the main firmware that we are
|
||||
// subscribed.
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "No cccd found for service changed characteristic, assuming we are "
|
||||
"auto-subscribed");
|
||||
}
|
||||
|
||||
// We've got everything we need, record the characteristic value handle, so we can filter
|
||||
// out the Service Changed indications later on:
|
||||
BTDeviceInternal address;
|
||||
connection_get_address(connection, &address);
|
||||
|
||||
hc_endpoint_discovery_service_changed_handle(&address, characteristic_info->c.value_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void gatt_client_discovery_process_service(const ble_evt_gattc_browse_svc_t *service) {
|
||||
uint32_t payload_size;
|
||||
HcProtocolDiscoveryServiceFoundPayload *payload =
|
||||
prv_gatt_client_discovery_build_gatt_service(service, &payload_size);
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
Connection *connection = connection_by_idx_check(service->conn_idx);
|
||||
connection_get_address(connection, &payload->address);
|
||||
hc_endpoint_discovery_send_service_found(payload, payload_size);
|
||||
|
||||
const Uuid ppogatt_uuid =
|
||||
(const Uuid){PEBBLE_BT_UUID_EXPAND(PEBBLE_BT_PPOGATT_SERVICE_UUID_32BIT)};
|
||||
if (uuid_equal(&payload->service.uuid, &ppogatt_uuid)) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "PPoGATT found");
|
||||
ppogatt_emulated_notify_phone_ppogatt_server_found(connection);
|
||||
}
|
||||
|
||||
kernel_free(payload);
|
||||
|
||||
prv_search_service_changed_handle(connection, service);
|
||||
}
|
||||
|
||||
void gatt_client_discovery_handle_complete(const ble_evt_gattc_browse_completed_t *complete_event) {
|
||||
ppogatt_inject_emulated_ppogatt_service_if_needed(complete_event->conn_idx);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Gatt Service Discovery Complete: %d", complete_event->status);
|
||||
BTDeviceInternal addr;
|
||||
Connection *connection = connection_by_idx_check(complete_event->conn_idx);
|
||||
|
||||
connection_get_address(connection, &addr);
|
||||
HciStatusCode status = HciStatusCode_Success;
|
||||
if (complete_event->status != GAP_ERR_NO_ERROR) {
|
||||
if (complete_event->status == GAP_ERR_INSUFF_RESOURCES) {
|
||||
// Due to a bug in the Dialog ROM (PBL-35827) we may get this error. We
|
||||
// assume all the discovery of services up to this point was successful,
|
||||
// so return "Success" to the Host
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "GATT discovery failed due to OOM, "
|
||||
"considering discovery up to last handle a success");
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "GATT discovery failed, %d\n", complete_event->status);
|
||||
status = HciStatusCode_VS_Base + complete_event->status;
|
||||
}
|
||||
}
|
||||
|
||||
hc_endpoint_discovery_complete(&addr, status);
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "gatt_local_services.h"
|
||||
|
||||
#include "dis_impl.h"
|
||||
#include "hrm_impl.h"
|
||||
#include "pebble_pairing_service_impl.h"
|
||||
#include "ppogatt_emulated_server_wa.h"
|
||||
|
||||
#include "system/logging.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "attm_db.h"
|
||||
#include "ble_common.h"
|
||||
#include "ble_gap.h"
|
||||
#include "ble_mgr.h"
|
||||
#include "gapm_task.h"
|
||||
|
||||
#include <os/mutex.h>
|
||||
|
||||
#include <bluetooth/init.h>
|
||||
|
||||
// We'd like to avoid changing the ATT table between FW updates if we can. The implementation of
|
||||
// the "Service Changed" GATT feature has been buggy on iOS in the past and in the Dialog BT driver
|
||||
// lib side we still have to implement PBL-35626.
|
||||
//
|
||||
// The ATT table currently looks like this (R=Read, W=Write, I=Indicatable, N=Notifiable):
|
||||
//
|
||||
// 1 - Service: Generic Access Profile Service (UUID: 0x1800)
|
||||
// 2 - Characteristic (R): Device Name
|
||||
// 3 - Value
|
||||
// 4 - Characteristic (R): Appearance
|
||||
// 5 - Value
|
||||
// 6 - Characteristic (R): Preferred Peripheral Connection Parameters
|
||||
// 7 - Value
|
||||
//
|
||||
// 8 - Service: Generic Attribute Profile Service (UUID: 0x1801)
|
||||
// 9 - Characteristic (RI): Service Changed
|
||||
// 10 - Value
|
||||
// 11 - CCCD
|
||||
//
|
||||
// 12 - Service: Device Information Service (UUID: 0x180A)
|
||||
// 13 - Characteristic (R): Manufacturer Name
|
||||
// 14 - Value
|
||||
// 15 - Characteristic (R): Model Number
|
||||
// 16 - Value
|
||||
// 17 - Characteristic (R): Serial Number
|
||||
// 18 - Value
|
||||
// 19 - Characteristic (R): Firmware Revision
|
||||
// 20 - Value
|
||||
// 21 - Characteristic (R): Software Revision
|
||||
// 22 - Value
|
||||
//
|
||||
// 23 - Service: Pebble Pairing Service (UUID: 0xFED9) -- Documentation here: http://pbl.io/gatt
|
||||
// 24 - Characteristic (RN): Connectivity Status
|
||||
// 25 - Value
|
||||
// 26 - CCCD
|
||||
// 27 - Characteristic (R): Trigger Pairing
|
||||
// 28 - Value
|
||||
// 29 - Characteristic (RWN): GATT MTU
|
||||
// 30 - Value
|
||||
// 31 - CCCD
|
||||
// 32 - Characteristic (RWN): Connection Parameters
|
||||
// 33 - Value
|
||||
// 34 - CCCD
|
||||
//
|
||||
// 35 - Service: Heart Rate Monitor (UUID: 0x180D)
|
||||
// 36 - Characteristic (N): Heart Rate Measurement (UUID: 0x2A37)
|
||||
// 37 - Value
|
||||
// 38 - CCCD
|
||||
// 39 - Characteristic (R): Sensor Location (UUID: 0x2A38)
|
||||
// 40 - Value
|
||||
// 41 - Characteristic (W): Heart Rate Control Point (UUID: 0x2A39)
|
||||
// 42 - Value
|
||||
//
|
||||
// 57344 (0xE000) - Service: PPoGATT Work-around Service (UUID: TBD)
|
||||
|
||||
static bool s_has_registered = false;
|
||||
static PebbleMutex *s_svc_register_mutex;
|
||||
|
||||
void gatt_local_services_init(const BTDriverConfig *config) {
|
||||
s_svc_register_mutex = mutex_create();
|
||||
device_information_service_init(&config->dis_info);
|
||||
pebble_pairing_service_init();
|
||||
hrm_service_init(config->is_hrm_supported_and_enabled);
|
||||
ppogatt_service_init();
|
||||
}
|
||||
|
||||
static void prv_gatt_local_services_register(void) {
|
||||
device_information_service_register(DEVICE_INFORMATION_SERVICE_EXPECTED_ATT_STARTING_HANDLE);
|
||||
pebble_pairing_service_register(PEBBLE_PAIRING_SERVICE_EXPECTED_ATT_STARTING_HANDLE);
|
||||
hrm_service_register(HRM_SERVICE_EXPECTED_ATT_STARTING_HANDLE);
|
||||
ppogatt_service_register(PEBBLE_PPOGATT_SERVICE_EXPECTED_ATT_STARTING_HANDLE);
|
||||
|
||||
s_has_registered = true;
|
||||
}
|
||||
|
||||
//! @note this will be called multiple times!
|
||||
void gatt_local_services_register(void) {
|
||||
mutex_lock(s_svc_register_mutex);
|
||||
prv_gatt_local_services_register();
|
||||
mutex_unlock(s_svc_register_mutex);
|
||||
}
|
||||
|
||||
void gatt_local_services_re_register_if_needed(void) {
|
||||
if (!s_has_registered) {
|
||||
// Avoid registering at this time, gatt_local_services_register() hasn't been called before.
|
||||
return;
|
||||
}
|
||||
mutex_lock(s_svc_register_mutex);
|
||||
|
||||
// Under the hood all the API calls below can invoke the GAPM_SET_DEV_CONFIG_CMD. Within the RW
|
||||
// stack this will often result in an attmdb_destroy() call which nukes the internal attribute
|
||||
// database. It's hard to detect this happening aside from inspecting code paths (even an rv of
|
||||
// success or failure does not tell you whether or not a flush occurred. To work around this we
|
||||
// issue a call into a RW ROM function to see if our handle still exists. There isn't any locking
|
||||
// around this function but AFAICT only API calls made by this module would wind up updating this
|
||||
// internal list so I think its safe to call
|
||||
if (attmdb_get_service(PEBBLE_PAIRING_SERVICE_EXPECTED_ATT_STARTING_HANDLE) == NULL) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Service flush detected, re-registering ...");
|
||||
prv_gatt_local_services_register();
|
||||
}
|
||||
|
||||
mutex_unlock(s_svc_register_mutex);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The functions below are wrappers for API calls that are known to clear out the ROM stack's
|
||||
// ATT table. All the wrappers do is call the original API and then re-registering our services.
|
||||
|
||||
extern ble_error_t __real_ble_gap_address_set(const own_address_t *address, uint16_t renew_dur);
|
||||
ble_error_t __wrap_ble_gap_address_set(const own_address_t *address, uint16_t renew_dur) {
|
||||
ble_error_t rv = __real_ble_gap_address_set(address, renew_dur);
|
||||
gatt_local_services_re_register_if_needed();
|
||||
return rv;
|
||||
}
|
||||
|
||||
extern ble_error_t __real_ble_gap_device_name_set(const char *name, att_perm_t perm);
|
||||
ble_error_t __wrap_ble_gap_device_name_set(const char *name, att_perm_t perm) {
|
||||
// Note: in spite of what the docstring says, the ATT database does not seem to get flushed by
|
||||
// calling ble_gap_device_name_set(), unless the write permissions have changed. We catch this
|
||||
// with our checks in gatt_local_services_re_register_if_needed
|
||||
|
||||
ble_error_t rv = __real_ble_gap_device_name_set(name, perm);
|
||||
gatt_local_services_re_register_if_needed();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
extern ble_error_t __real_ble_gap_appearance_set(gap_appearance_t appearance, att_perm_t perm);
|
||||
ble_error_t __wrap_ble_gap_appearance_set(gap_appearance_t appearance, att_perm_t perm) {
|
||||
ble_error_t rv = __real_ble_gap_appearance_set(appearance, perm);
|
||||
gatt_local_services_re_register_if_needed();
|
||||
return rv;
|
||||
}
|
||||
|
||||
extern ble_error_t __real_ble_gap_per_pref_conn_params_set(const gap_conn_params_t *conn_params);
|
||||
ble_error_t __wrap_ble_gap_per_pref_conn_params_set(const gap_conn_params_t *conn_params) {
|
||||
ble_error_t rv = __real_ble_gap_per_pref_conn_params_set(conn_params);
|
||||
gatt_local_services_re_register_if_needed();
|
||||
return rv;
|
||||
}
|
||||
|
||||
extern ble_error_t __real_ble_gap_role_set(const gap_role_t role);
|
||||
ble_error_t __wrap_ble_gap_role_set(const gap_role_t role) {
|
||||
ble_error_t rv = __real_ble_gap_role_set(role);
|
||||
gatt_local_services_re_register_if_needed();
|
||||
return rv;
|
||||
}
|
||||
|
||||
extern ble_error_t __real_ble_gap_mtu_size_set(uint16_t mtu_size);
|
||||
ble_error_t __wrap_ble_gap_mtu_size_set(uint16_t mtu_size) {
|
||||
ble_error_t rv = __real_ble_gap_mtu_size_set(mtu_size);
|
||||
gatt_local_services_re_register_if_needed();
|
||||
return rv;
|
||||
}
|
210
src/bluetooth-fw/da1468x/controller/main/src/gatt_wrapper.c
Normal file
210
src/bluetooth-fw/da1468x/controller/main/src/gatt_wrapper.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "gatt_wrapper.h"
|
||||
#include "gatt_wrapper_types.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "hc_protocol/hc_endpoint_gatt.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include "ble_gattc.h"
|
||||
|
||||
#include "rwble_hl_error.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static GattRespDest prv_dest_from_source(GattReqSource source) {
|
||||
return (source == GattReqSourceHost) ? GattRespDestHost : GattRespDestController;
|
||||
}
|
||||
|
||||
static bool prv_enqueue(
|
||||
uint16_t conn_idx, uintptr_t context_ref, GattRespDest resp_dest, GattOpType op_type) {
|
||||
Connection *conn = connection_by_idx(conn_idx);
|
||||
if (!conn) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Failed to find connection during enqueue attempt");
|
||||
return false;
|
||||
}
|
||||
connection_enqueue_gatt_op(conn, context_ref, resp_dest, op_type);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prv_pop_errored_op(uint16_t conn_idx) {
|
||||
Connection *conn = connection_by_idx(conn_idx);
|
||||
if (!conn) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Failed to find connection during queue pop attempt");
|
||||
return false;
|
||||
}
|
||||
connection_pop_gatt_op(conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_pop_if_failing_rv(uint16_t conn_idx, ble_error_t rv) {
|
||||
if (rv != BLE_STATUS_OK) {
|
||||
prv_pop_errored_op(conn_idx);
|
||||
}
|
||||
}
|
||||
|
||||
ble_error_t gatt_wrapper_read(uint16_t conn_idx, uint16_t handle, uintptr_t context_ref,
|
||||
GattReqSource source) {
|
||||
GATT_LOG_DEBUG("gatt_wrapper_read: handle: %d, context_ref: %d, source: %d",
|
||||
handle, context_ref, source);
|
||||
|
||||
const GattRespDest resp_dest = prv_dest_from_source(source);
|
||||
if (!prv_enqueue(conn_idx, context_ref, resp_dest, GattOpType_Read)) {
|
||||
return BLE_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
ble_error_t rv = ble_gattc_read(conn_idx, handle, 0);
|
||||
prv_pop_if_failing_rv(conn_idx, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
ble_error_t gatt_wrapper_read_by_uuid(uint16_t conn_idx, uint16_t start_h, uint16_t end_h,
|
||||
const att_uuid_t *uuid, uintptr_t context_ref,
|
||||
GattReqSource source) {
|
||||
GATT_LOG_DEBUG("gatt_wrapper_read_uuid: context_ref: %d, source: %d", context_ref, source);
|
||||
|
||||
const GattRespDest resp_dest = prv_dest_from_source(source);
|
||||
if (!prv_enqueue(conn_idx, context_ref, resp_dest, GattOpType_Read)) {
|
||||
return BLE_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
ble_error_t rv = ble_gattc_read_by_uuid(conn_idx, start_h, end_h, uuid);
|
||||
prv_pop_if_failing_rv(conn_idx, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
ble_error_t gatt_wrapper_write(uint16_t conn_idx, uint16_t handle, uint16_t length,
|
||||
const uint8_t *value, uintptr_t context_ref, GattReqSource source) {
|
||||
GATT_LOG_DEBUG("gatt_wrapper_write: handle: %d, context_ref: %d, source: %d",
|
||||
handle, context_ref, source);
|
||||
|
||||
const GattRespDest resp_dest = prv_dest_from_source(source);
|
||||
if (!prv_enqueue(conn_idx, context_ref, resp_dest, GattOpType_Write)) {
|
||||
return BLE_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
ble_error_t rv = ble_gattc_write(conn_idx, handle, 0, length, value);
|
||||
prv_pop_if_failing_rv(conn_idx, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
ble_error_t gatt_wrapper_write_no_resp(uint16_t conn_idx, uint16_t handle, uint16_t length,
|
||||
const uint8_t *value) {
|
||||
GATT_LOG_DEBUG("gatt_wrapper_write_no_resp: handle: %d", handle);
|
||||
|
||||
const GattRespDest resp_dest = GattRespDestNone;
|
||||
if (!prv_enqueue(conn_idx, 0, resp_dest, GattOpType_Write)) {
|
||||
return BLE_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
const bool signed_write = false;
|
||||
ble_error_t rv = ble_gattc_write_no_resp(conn_idx, handle, signed_write, length, value);
|
||||
prv_pop_if_failing_rv(conn_idx, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static GattRespDest prv_handle_gatt_event(uint16_t conn_idx, uint16_t handle,
|
||||
BTDeviceInternal *addr, uintptr_t *context_ref,
|
||||
BLEGATTError status, GattOpType expected_type) {
|
||||
// This used to be `connection_by_idx_check`, but due to PBL-36813, needed to change it.
|
||||
// The error that comes back in the `evt` is `GAP_ERR_COMMAND_DISALLOWED: 0x43`, but to make
|
||||
// sure we don't assert on any other future errors, we check for the connection.
|
||||
Connection *conn = connection_by_idx(conn_idx);
|
||||
if (!conn) {
|
||||
return GattRespDestNone;
|
||||
}
|
||||
GattRespDest resp_dest;
|
||||
|
||||
if (!connection_dequeue_gatt_op(conn, context_ref, &resp_dest, expected_type)) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "No gatt op to dequeue, status %d, hdl: %d", status, (int)handle);
|
||||
// FIXME: I think this happens if we reconnect too fast. The log above will get flushed to the
|
||||
// main MCU before the crash and should provide some extra context. I believe this state is
|
||||
// captured by GAP_ERR_COMMAND_DISALLOWED but let's assert if we catch another scenario
|
||||
PBL_ASSERTN(status == (int)GAP_ERR_COMMAND_DISALLOWED);
|
||||
return GattRespDestNone;
|
||||
}
|
||||
connection_get_address(conn, addr);
|
||||
return resp_dest;
|
||||
}
|
||||
|
||||
void gatt_wrapper_handle_read_completed(const ble_evt_gattc_read_completed_t *evt) {
|
||||
BTDeviceInternal addr;
|
||||
uintptr_t context_ref;
|
||||
GATT_LOG_DEBUG("gatt_wrapper_handle_read_completed: handle: %d, status: %d",
|
||||
evt->handle, evt->status);
|
||||
const BLEGATTError status = (BLEGATTError)evt->status;
|
||||
GattRespDest resp_dest = prv_handle_gatt_event(evt->conn_idx, evt->handle, &addr, &context_ref,
|
||||
status, GattOpType_Read);
|
||||
|
||||
|
||||
switch (resp_dest) {
|
||||
case GattRespDestHost:
|
||||
hc_endpoint_gatt_send_read_complete(&addr, evt->handle, status, evt->length,
|
||||
&evt->value[0] + evt->offset, context_ref);
|
||||
break;
|
||||
case GattRespDestController:
|
||||
PBL_ASSERTN(context_ref);
|
||||
((gatt_wrapper_read_cb) context_ref)(evt);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gatt_wrapper_handle_write_completed(const ble_evt_gattc_write_completed_t *evt) {
|
||||
BTDeviceInternal addr;
|
||||
uintptr_t context_ref;
|
||||
GATT_LOG_DEBUG("gatt_wrapper_handle_write_completed: handle: %d, status: %d",
|
||||
evt->handle, evt->status);
|
||||
const BLEGATTError status = (BLEGATTError)evt->status;
|
||||
GattRespDest resp_dest = prv_handle_gatt_event(evt->conn_idx, evt->handle, &addr, &context_ref,
|
||||
status, GattOpType_Write);
|
||||
|
||||
switch (resp_dest) {
|
||||
case GattRespDestHost:
|
||||
hc_endpoint_gatt_send_write_complete(&addr, evt->handle, status, context_ref);
|
||||
break;
|
||||
case GattRespDestController:
|
||||
PBL_ASSERTN(context_ref);
|
||||
((gatt_wrapper_write_cb) context_ref)(evt);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gatt_wrapper_handle_notification(const ble_evt_gattc_notification_t *evt) {
|
||||
BTDeviceInternal addr;
|
||||
Connection *conn = connection_by_idx_check(evt->conn_idx);
|
||||
connection_get_address(conn, &addr);
|
||||
hc_endpoint_gatt_send_notification(&addr, evt->handle, evt->length, evt->value);
|
||||
}
|
||||
|
||||
void gatt_wrapper_handle_indication(const ble_evt_gattc_indication_t *evt) {
|
||||
BTDeviceInternal addr;
|
||||
Connection *conn = connection_by_idx_check(evt->conn_idx);
|
||||
connection_get_address(conn, &addr);
|
||||
hc_endpoint_gatt_send_indication(&addr, evt->handle, evt->length, evt->value);
|
||||
|
||||
// GATT Indications are already automatically confirmed by the Dialog SDK.
|
||||
// See comments with ble_gattc_indication_cfm().
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_advert.h"
|
||||
|
||||
#include "advert.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ble_common.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static void prv_send_response(const HcProtocolMessage *msg, ble_error_t err) {
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)&err, sizeof(ble_error_t));
|
||||
}
|
||||
|
||||
static void prv_handle_advert_enable(const HcProtocolMessage *msg) {
|
||||
HcAdvertEnableData *data = (HcAdvertEnableData *)msg->payload;
|
||||
|
||||
// One slot is 625us:
|
||||
const uint16_t min_slots = data->min_interval_ms * 8 / 5;
|
||||
const uint16_t max_slots = data->max_interval_ms * 8 / 5;
|
||||
|
||||
// PBL_LOG(LOG_LEVEL_DEBUG, "Advert; Setting min/max interval (ms) to %"PRIu16" / %"PRIu16,
|
||||
// data->min_interval_ms, data->max_interval_ms);
|
||||
|
||||
HcAdvertEnableResponseData response_data = {};
|
||||
response_data.error = advert_set_interval(min_slots, max_slots);
|
||||
if (response_data.error == BLE_STATUS_OK) {
|
||||
response_data.current_state = advert_enable();
|
||||
}
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)&response_data, sizeof(response_data));
|
||||
}
|
||||
|
||||
static void prv_handle_advert_disable(const HcProtocolMessage *msg) {
|
||||
advert_disable();
|
||||
prv_send_response(msg, BLE_STATUS_OK);
|
||||
}
|
||||
|
||||
static void prv_handle_advert_set_adv_data(const HcProtocolMessage *msg) {
|
||||
advert_set_data((const BLEAdData *)&msg->payload[0]);
|
||||
prv_send_response(msg, BLE_STATUS_OK);
|
||||
}
|
||||
|
||||
void hc_endpoint_advert_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Advert_Enable:
|
||||
prv_handle_advert_enable(msg);
|
||||
break;
|
||||
case HcMessageID_Advert_Disable:
|
||||
prv_handle_advert_disable(msg);
|
||||
break;
|
||||
case HcMessageID_Advert_SetAdvData:
|
||||
prv_handle_advert_set_adv_data(msg);
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcAdvert: unhandled message id: %d", msg->command_id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_analytics.h"
|
||||
#include "host_transport_protocol.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "dialog_analytics/analytics.h"
|
||||
#include "host_transport.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "reboot_reason.h"
|
||||
#include "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ble_common.h"
|
||||
#include "ble_gap.h"
|
||||
|
||||
#include <bluetooth/analytics.h>
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void slave_window_stats_collect(SlaveConnEventStats *stats);
|
||||
|
||||
static void prv_handle_collect_ble_parameters(const HcProtocolMessage *msg) {
|
||||
uint64_t channel_map_uint;
|
||||
|
||||
const ble_error_t e = ble_gap_channel_map_get(&channel_map_uint);
|
||||
const bool success = (e == BLE_STATUS_OK);
|
||||
|
||||
LEChannelMap channel_map = {};
|
||||
if (success) {
|
||||
memcpy(&channel_map, &channel_map_uint, sizeof(channel_map));
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_channel_map_get err: %x", e);
|
||||
}
|
||||
|
||||
HcAnalyticsCollectBleParameters data = {
|
||||
.success = success,
|
||||
.le_chan_map_res = channel_map,
|
||||
};
|
||||
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)&data, sizeof(data));
|
||||
}
|
||||
|
||||
static void prv_handle_get_connection_quality(const HcProtocolMessage *msg) {
|
||||
HcAnalyticsGetConnectionQuality data = {};
|
||||
|
||||
const BTDeviceInternal *device = (BTDeviceInternal *)&msg->payload[0];
|
||||
Connection *conn = connection_by_address(device);
|
||||
if (!conn) {
|
||||
data.success = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
const uint16_t conn_idx = connection_get_idx(conn);
|
||||
int8_t conn_rssi;
|
||||
const ble_error_t e = ble_gap_conn_rssi_get(conn_idx, &conn_rssi);
|
||||
const bool success = (e == BLE_STATUS_OK);
|
||||
if (!success) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_conn_rssi_get err: %x", e);
|
||||
conn_rssi = 0;
|
||||
}
|
||||
|
||||
data = (HcAnalyticsGetConnectionQuality) {
|
||||
.success = success,
|
||||
.rssi = conn_rssi,
|
||||
};
|
||||
|
||||
done:
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)&data, sizeof(data));
|
||||
}
|
||||
|
||||
#if 0 // FIXME with PBL-38365
|
||||
static void prv_analytic_each_cb(DialogAnalyticsMetric metric, uint32_t value, void *context) {
|
||||
HcAnalyticsHeartbeatData *data = context;
|
||||
data->analytics[metric].metric = cmetric;
|
||||
data->analytics[metric].value = value;
|
||||
}
|
||||
|
||||
#define HEARTBEAT_SIZE (sizeof(HcAnalyticsHeartbeatData) + \
|
||||
(DialogAnalyticMetric_Count * sizeof(SerializedAnalytic)))
|
||||
|
||||
_Static_assert(HEARTBEAT_SIZE < HOST_TRANSPORT_CTLR_TX_BUFFER_SIZE &&
|
||||
HEARTBEAT_SIZE < HOST_TRANSPORT_HOST_RX_BUFFER_SIZE,
|
||||
"Heartbeat Size must be less than HOST_TRANSPORT_TX_BUFFER_SIZE");
|
||||
|
||||
static void prv_handle_get_heartbeat_data(const HcProtocolMessage *msg) {
|
||||
uint32_t data_size = HEARTBEAT_SIZE;
|
||||
HcAnalyticsHeartbeatData *data = kernel_zalloc(data_size);
|
||||
|
||||
if (!data) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Not enough resources to allocate analytics heartbeat");
|
||||
data_size = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
data->count = DialogAnalyticMetric_Count;
|
||||
analytics_each(prv_analytic_each_cb, data);
|
||||
|
||||
analytics_reset_nodes();
|
||||
|
||||
done:
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)data, data_size);
|
||||
kernel_free(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void prv_handle_get_connection_event_stats(const HcProtocolMessage *msg) {
|
||||
SlaveConnEventStats stats;
|
||||
slave_window_stats_collect(&stats);
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)&stats, sizeof(stats));
|
||||
}
|
||||
|
||||
//! This symbol and its contents are provided by the linker script, see the
|
||||
//! .note.gnu.build-id section in src/fw/stm32f2xx_flash_fw.ld
|
||||
extern const ElfExternalNote DIALOG_BUILD_ID;
|
||||
|
||||
void hc_endpoint_analytics_send_reboot_info(void) {
|
||||
const RebootReasonCode reason = reboot_reason_get_last_reboot_reason();
|
||||
if (reason == RebootReasonCode_Shutdown || reason == RebootReasonCode_Unknown) {
|
||||
// Don't send the reboot reason to analytics for insignificant data.
|
||||
return;
|
||||
}
|
||||
|
||||
HcAnalyticsRebootInfo info = {
|
||||
.last_crash_lr = reboot_reason_get_crash_lr(),
|
||||
.reboot_reason_code = reason,
|
||||
};
|
||||
memcpy(info.build_id, &DIALOG_BUILD_ID.data[DIALOG_BUILD_ID.name_length], BUILD_ID_EXPECTED_LEN);
|
||||
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Analytics, HcMessageID_Analytics_LogRebootInfo,
|
||||
(uint8_t *)&info, sizeof(info));
|
||||
}
|
||||
|
||||
void hc_endpoint_analytics_log_mic_error_detected(uint32_t num_subsequent_mic_errors) {
|
||||
HcAnalyticsLogBleMicErrorEvent info = {
|
||||
.num_subsequent_mic_errors = num_subsequent_mic_errors,
|
||||
};
|
||||
|
||||
hc_protocol_enqueue_with_payload(
|
||||
HcEndpointID_Analytics, HcMessageID_Analytics_LogBleMicErrorEvent, (uint8_t *)&info,
|
||||
sizeof(info));
|
||||
}
|
||||
|
||||
void hc_endpoint_analytics_ctlr_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Analytics_CollectBLEParameters:
|
||||
prv_handle_collect_ble_parameters(msg);
|
||||
break;
|
||||
case HcMessageID_Analytics_GetConnectionQuality:
|
||||
prv_handle_get_connection_quality(msg);
|
||||
break;
|
||||
#if 0 // Enable with PBL-38365
|
||||
case HcMessageID_Analytics_GetHeartbeatData:
|
||||
prv_handle_get_heartbeat_data(msg);
|
||||
break;
|
||||
#endif
|
||||
case HcMessageID_Analytics_GetConnEventStats:
|
||||
prv_handle_get_connection_event_stats(msg);
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcAnalytics: unhandled message id: %d", msg->command_id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_chip_id.h"
|
||||
|
||||
#include "chip_id.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static void prv_handle_chip_info_request(const HcProtocolMessage *request) {
|
||||
const uint8_t *response_payload = NULL;
|
||||
DialogChipID chip_id;
|
||||
if (dialog_chip_id_copy(&chip_id)) {
|
||||
response_payload = (const uint8_t *)&chip_id;
|
||||
}
|
||||
// Send back empty response in case of failure.
|
||||
hc_protocol_enqueue_response(request, response_payload, response_payload ? sizeof(chip_id) : 0);
|
||||
}
|
||||
|
||||
void hc_endpoint_chip_id_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Id_ChipInfo:
|
||||
prv_handle_chip_info_request(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Unknown cmd ID: 0x%"PRIx8, msg->command_id);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_ctl.h"
|
||||
|
||||
#include "ble_task.h"
|
||||
#include "power.h"
|
||||
#include "reboot_reason.h"
|
||||
#include "system/logging.h"
|
||||
#include "tasks.h"
|
||||
|
||||
#include <bluetooth/init.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static void prv_handle_init(const HcProtocolMessage *request) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Got init message!");
|
||||
|
||||
const BTDriverConfig *config = (const BTDriverConfig *)request->payload;
|
||||
ble_task_init(config);
|
||||
|
||||
hc_protocol_enqueue_response(request, NULL, 0);
|
||||
}
|
||||
|
||||
static void prv_handle_shutdown(const HcProtocolMessage *request) {
|
||||
#if 0 // Fixme once some space is really freed up
|
||||
// Dump some statistics on stack usage before we shutdown
|
||||
tasks_dump_free_space();
|
||||
#endif
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Got shutdown message! Going to fall into a deep sleep...");
|
||||
|
||||
// Set the reboot reason to signify we shut down gracefully
|
||||
RebootReason reason = {
|
||||
.code = RebootReasonCode_Shutdown,
|
||||
};
|
||||
// FIXME PBL-38181: For some reason, the reboot reason shutdown never gets persisted/recovered
|
||||
reboot_reason_set(&reason);
|
||||
|
||||
// Send empty response as acknowledgement:
|
||||
hc_protocol_enqueue_response(request, NULL, 0);
|
||||
|
||||
power_enter_hibernation();
|
||||
}
|
||||
|
||||
void hc_endpoint_ctl_handler(const HcProtocolMessage *request) {
|
||||
switch (request->command_id) {
|
||||
case HcMessageID_Ctl_Init:
|
||||
prv_handle_init(request);
|
||||
break;
|
||||
|
||||
case HcMessageID_Ctl_Shutdown:
|
||||
prv_handle_shutdown(request);
|
||||
break;
|
||||
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Unknown command 0x%"PRIx8, request->command_id);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_discovery.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <bluetooth/hci_types.h>
|
||||
|
||||
// Dialog APIs
|
||||
#include "ble_common.h"
|
||||
#include "ble_gattc.h"
|
||||
|
||||
void hc_endpoint_discovery_complete(const BTDeviceInternal *address, HciStatusCode status) {
|
||||
HcProtocolDiscoveryCompletePayload payload = {
|
||||
.address = *address,
|
||||
.status = status
|
||||
};
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Discovery, HcMessageID_Discovery_Complete,
|
||||
(uint8_t *)&payload, sizeof(payload));
|
||||
}
|
||||
|
||||
static void prv_handle_discovery_start(const HcProtocolDiscoveryStartPayload *req) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "->" BT_DEVICE_ADDRESS_FMT,
|
||||
BT_DEVICE_ADDRESS_XPLODE(req->address.address));
|
||||
Connection *conn = connection_by_address(&req->address);
|
||||
HciStatusCode status = HciStatusCode_Success;
|
||||
if (conn == NULL) {
|
||||
status = HciStatusCode_UnknownConnectionIdentifier;
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "No connection to addr!");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Received Discovery Start for 0x%x to 0x%x",
|
||||
(int)req->range.start, (int)req->range.end);
|
||||
|
||||
uint16_t conn_idx = connection_get_idx(conn);
|
||||
ble_error_t e = ble_gattc_browse(conn_idx, NULL, req->range.start, req->range.end);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
status = HciStatusCode_VS_Base + e;
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "ble_gattc_browse: %u", e);
|
||||
goto failure;
|
||||
}
|
||||
return;
|
||||
failure:
|
||||
// Notify Host that discovery terminated unexpectedly
|
||||
hc_endpoint_discovery_complete(&req->address, status);
|
||||
}
|
||||
|
||||
void hc_endpoint_discovery_service_changed_handle(const BTDeviceInternal *address,
|
||||
uint16_t handle) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Gatt Service Discovery Service Changed Handle: %d", handle);
|
||||
|
||||
HcProtocolDiscoveryServiceChangedHandlePayload payload = {
|
||||
.address = *address,
|
||||
.handle = handle
|
||||
};
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Discovery,
|
||||
HcMessageID_Discovery_Service_Changed_Handle,
|
||||
(uint8_t *)&payload, sizeof(payload));
|
||||
}
|
||||
|
||||
static void prv_handle_discovery_stop(const BTDeviceInternal *address) {
|
||||
Connection *conn = connection_by_address(address);
|
||||
|
||||
if (conn == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Received Discovery Stop Request");
|
||||
|
||||
uint16_t conn_idx = connection_get_idx(conn);
|
||||
ble_error_t e = ble_gattc_discover_cancel(conn_idx);
|
||||
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "ble_gattc_discover_cancel: %u", e);
|
||||
}
|
||||
}
|
||||
|
||||
void hc_endpoint_discovery_send_service_found(
|
||||
const HcProtocolDiscoveryServiceFoundPayload *payload, uint32_t payload_size) {
|
||||
// Note: kind of wasteful double copy here, a service node could get sort of
|
||||
// big (several hundred bytes)
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Discovery, HcMessageID_Discovery_Service_Found,
|
||||
(uint8_t *)payload, payload_size);
|
||||
}
|
||||
|
||||
void hc_endpoint_discovery_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Discovery_Start:
|
||||
prv_handle_discovery_start((HcProtocolDiscoveryStartPayload *)msg->payload);
|
||||
break;
|
||||
case HcMessageID_Discovery_Stop:
|
||||
prv_handle_discovery_stop((BTDeviceInternal *)msg->payload);
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Unexpected command: 0x%x", (int)msg->command_id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_gap_le_connect.h"
|
||||
|
||||
#include "ble_common.h"
|
||||
#include "ble_gap.h"
|
||||
|
||||
#include <bluetooth/gap_le_connect.h>
|
||||
#include "connection.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
void hc_endpoint_gap_le_connect_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_GapLEConnect_Disconnect: {
|
||||
ble_error_t err = BLE_ERROR_NOT_CONNECTED;
|
||||
Connection *connection = connection_by_address((const BTDeviceInternal *)&msg->payload[0]);
|
||||
if (connection) {
|
||||
err = ble_gap_disconnect(connection_get_idx(connection),
|
||||
BLE_HCI_ERROR_REMOTE_USER_TERM_CON);
|
||||
}
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)&err, sizeof(ble_error_t));
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcGapLeConnect: unhandled message id: %d", msg->command_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_send_msg(HcMessageID_GapLEConnect command_id, void *payload, size_t payload_size) {
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_GapLEConnect, command_id, payload, payload_size);
|
||||
}
|
||||
|
||||
void hc_endpoint_gap_le_connect_send_connection_complete(HcGapLeConnectionData *e) {
|
||||
prv_send_msg(HcMessageID_GapLEConnect_ConnectionComplete, e, sizeof(*e));
|
||||
}
|
||||
|
||||
void hc_endpoint_gap_le_connect_send_disconnection_complete(BleDisconnectionCompleteEvent *e) {
|
||||
prv_send_msg(HcMessageID_GapLEConnect_DisconnectionComplete, e, sizeof(*e));
|
||||
}
|
||||
|
||||
void hc_endpoint_gap_le_connect_send_encryption_changed(BleEncryptionChange *e) {
|
||||
prv_send_msg(HcMessageID_GapLEConnect_EncryptionChange, e, sizeof(*e));
|
||||
}
|
||||
|
||||
void hc_endpoint_gap_le_connect_send_address_and_irk_changed(BleAddressAndIRKChange *e) {
|
||||
prv_send_msg(HcMessageID_GapLEConnect_UpdateAddressAndIRK, e, sizeof(*e));
|
||||
}
|
||||
|
||||
void hc_endpoint_gap_le_connect_send_peer_version_info(BleRemoteVersionInfoReceivedEvent *e) {
|
||||
prv_send_msg(HcMessageID_GapLEConnect_PeerVersionInfo, e, sizeof(*e));
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "gatt_wrapper.h"
|
||||
#include "gap_le_device_name_impl.h"
|
||||
#include "hc_protocol/hc_endpoint_gap_service.h"
|
||||
#include "local_addr_impl.h"
|
||||
#include "pra_generate.h"
|
||||
|
||||
#include "system/logging.h"
|
||||
#include "system/hexdump.h"
|
||||
|
||||
#include <util/attributes.h>
|
||||
|
||||
// Dialog SDK:
|
||||
#include "att.h"
|
||||
#include "ble_gap.h"
|
||||
#include "ble_gattc.h"
|
||||
#include "ble_uuid.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void hc_endpoint_gap_service_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_GapService_SetName:
|
||||
gap_le_device_name_handle_set((const char *)&msg->payload[0]);
|
||||
break;
|
||||
case HcMessageID_GapService_DeviceNameRequest: {
|
||||
BTDeviceInternal addr;
|
||||
memcpy(&addr, &msg->payload[0], sizeof(BTDeviceInternal));
|
||||
gap_le_device_name_handle_request(&addr);
|
||||
}
|
||||
break;
|
||||
case HcMessageID_GapService_DeviceNameRequest_All:
|
||||
gap_le_device_name_handle_request_all();
|
||||
break;
|
||||
case HcMessageID_GapService_SetLocalAddress: {
|
||||
const HcProtocol_GapServiceSetLocalAddress *payload =
|
||||
(const HcProtocol_GapServiceSetLocalAddress *)msg->payload;
|
||||
local_addr_set(payload->allow_cycling,
|
||||
payload->allow_cycling ? NULL : &payload->pinned_addr);
|
||||
break;
|
||||
}
|
||||
case HcMessageID_GapService_GeneratePrivateResolvable_address: {
|
||||
HcProtocol_GapServiceGeneratePrivateResolvableAddressResponse payload;
|
||||
pra_generate(&payload.address);
|
||||
hc_protocol_enqueue_response(msg, (const uint8_t *)&payload, sizeof(payload));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcGap: unhandled message id: %d", msg->command_id);
|
||||
}
|
||||
}
|
||||
|
||||
void hc_endpoint_gap_service_mtu_changed(const Connection *connection, uint16_t mtu) {
|
||||
HcProtocol_GapServiceMtuChanged mtu_resp = {};
|
||||
connection_get_address(connection, &mtu_resp.addr);
|
||||
mtu_resp.mtu = mtu;
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_GapService, HcMessageId_GapService_MtuChanged,
|
||||
(const uint8_t *)&mtu_resp, sizeof(mtu_resp));
|
||||
}
|
||||
|
||||
void hc_endpoint_gap_service_device_name_read(const ble_evt_gattc_read_completed_t *evt) {
|
||||
if (evt->status != ATT_ERROR_OK) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Read_device_name failed: Idx: %d Att 0x%x Status %d",
|
||||
evt->conn_idx, evt->handle, evt->status);
|
||||
return;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Read_device_name: Idx: %d Handle 0x%x Offset %d Length %d Data:",
|
||||
evt->conn_idx, evt->handle, evt->offset, evt->length);
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, evt->value, evt->length);
|
||||
Connection *connection = connection_by_idx(evt->conn_idx);
|
||||
if (!connection) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Read_device_name: Failed to find connection");
|
||||
return;
|
||||
}
|
||||
|
||||
typedef struct PACKED HcProtocol_GapDeviceNameResponse {
|
||||
HcProtocol_GapDeviceNameResponseHeader header;
|
||||
uint8_t name[evt->length];
|
||||
} HcProtocol_GapDeviceNameResponse;
|
||||
HcProtocol_GapDeviceNameResponse response;
|
||||
|
||||
connection_get_address(connection, &response.header.addr);
|
||||
response.header.name_length = evt->length;
|
||||
memcpy(response.name, evt->value, evt->length);
|
||||
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_GapService,
|
||||
HcMessageID_GapService_DeviceNameRequest,
|
||||
(void *)&response, sizeof(HcProtocol_GapDeviceNameResponse));
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_gatt.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "gatt_wrapper.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "ppogatt_emulated_server_wa.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
void hc_endpoint_gatt_handler_controller(const HcProtocolMessage *msg) {
|
||||
BTErrno rv = BTErrnoOK;
|
||||
|
||||
HcGattHdr *hdr = (HcGattHdr *)&msg->payload[0];
|
||||
Connection *conn = connection_by_address(&hdr->addr);
|
||||
if (!conn) {
|
||||
rv = BTErrnoInvalidParameter;
|
||||
goto respond;
|
||||
}
|
||||
|
||||
const uint16_t conn_idx = connection_get_idx(conn);
|
||||
if (ppogatt_emulated_server_handle_msg(conn_idx, conn, msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GattReqSource req_source = GattReqSourceHost;
|
||||
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Gatt_Read: {
|
||||
HcGattReadData *r_data = (HcGattReadData *)hdr;
|
||||
gatt_wrapper_read(conn_idx, r_data->att_handle, r_data->context_ref, req_source);
|
||||
break;
|
||||
}
|
||||
case HcMessageID_Gatt_Write: {
|
||||
HcGattWriteData *w_data = (HcGattWriteData *)hdr;
|
||||
gatt_wrapper_write(conn_idx, w_data->att_handle, w_data->value_length, w_data->value,
|
||||
w_data->context_ref, req_source);
|
||||
break;
|
||||
}
|
||||
case HcMessageID_Gatt_WriteNoResponse: {
|
||||
HcGattWriteData *w_data = (HcGattWriteData *)hdr;
|
||||
gatt_wrapper_write_no_resp(conn_idx, w_data->att_handle, w_data->value_length, w_data->value);
|
||||
return; // Don't respond
|
||||
}
|
||||
}
|
||||
|
||||
rv = BTErrnoOK;
|
||||
|
||||
respond:
|
||||
hc_protocol_enqueue_response(msg, (uint8_t *)&rv, sizeof(rv));
|
||||
}
|
||||
|
||||
void hc_endpoint_gatt_send_read_complete(const BTDeviceInternal *addr, uint16_t handle,
|
||||
BLEGATTError status, uint16_t value_length, const uint8_t *value, uintptr_t context_ref) {
|
||||
|
||||
const uint32_t alloc_size = sizeof(HcGattReadRespData) + value_length;
|
||||
HcGattReadRespData *data = kernel_zalloc_check(alloc_size);
|
||||
*data = (HcGattReadRespData) {
|
||||
.status = status,
|
||||
.hdr.addr = *addr,
|
||||
.att_handle = handle,
|
||||
.value_length = value_length,
|
||||
.context_ref = context_ref,
|
||||
};
|
||||
memcpy(data->value, value, value_length);
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Gatt, HcMessageID_Gatt_ReadCompleted,
|
||||
(uint8_t *)data, alloc_size);
|
||||
kernel_free(data);
|
||||
}
|
||||
|
||||
void hc_endpoint_gatt_send_write_complete(const BTDeviceInternal *addr, uint16_t handle,
|
||||
BLEGATTError status, uintptr_t context_ref) {
|
||||
const uint32_t alloc_size = sizeof(HcGattWriteRespData);
|
||||
HcGattWriteRespData *data = kernel_zalloc_check(alloc_size);
|
||||
*data = (HcGattWriteRespData) {
|
||||
.status = status,
|
||||
.hdr.addr = *addr,
|
||||
.att_handle = handle,
|
||||
.context_ref = context_ref,
|
||||
};
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Gatt, HcMessageID_Gatt_WriteCompleted,
|
||||
(uint8_t *)data, alloc_size);
|
||||
kernel_free(data);
|
||||
}
|
||||
|
||||
static void prv_send_notification_indication(const BTDeviceInternal *addr, uint16_t handle,
|
||||
uint16_t value_length, const uint8_t *value,
|
||||
HcMessageID_Gatt msg_id) {
|
||||
const uint32_t alloc_size = sizeof(HcGattNotifIndicData) + value_length;
|
||||
HcGattNotifIndicData *data = kernel_zalloc_check(alloc_size);
|
||||
*data = (HcGattNotifIndicData) {
|
||||
.hdr.addr = *addr,
|
||||
.att_handle = handle,
|
||||
.value_length = value_length,
|
||||
};
|
||||
memcpy(data->value, value, value_length);
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Gatt, msg_id, (uint8_t *)data, alloc_size);
|
||||
kernel_free(data);
|
||||
}
|
||||
|
||||
void hc_endpoint_gatt_send_notification(const BTDeviceInternal *addr, uint16_t handle,
|
||||
uint16_t value_length, const uint8_t *value) {
|
||||
prv_send_notification_indication(addr, handle, value_length, value,
|
||||
HcMessageID_Gatt_Notification);
|
||||
}
|
||||
|
||||
void hc_endpoint_gatt_send_indication(const BTDeviceInternal *addr, uint16_t handle,
|
||||
uint16_t value_length, const uint8_t *value) {
|
||||
prv_send_notification_indication(addr, handle, value_length, value,
|
||||
HcMessageID_Gatt_Indication);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_hrm.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
|
||||
#include "hrm_impl.h"
|
||||
|
||||
static void prv_handle_measurement_msg(const HcProtocolMessage *msg) {
|
||||
const HcHrmMeasurement *hc_hrm_measurement = (const HcHrmMeasurement *)msg->payload;
|
||||
|
||||
const BleHrmServiceMeasurement hrm_measurement = {
|
||||
.bpm = hc_hrm_measurement->bpm,
|
||||
.is_on_wrist = hc_hrm_measurement->is_on_wrist,
|
||||
};
|
||||
hrm_service_handle_measurement(&hrm_measurement, hc_hrm_measurement->devices,
|
||||
hc_hrm_measurement->num_devices);
|
||||
}
|
||||
|
||||
void hc_endpoint_hrm_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_HRM_Measurement:
|
||||
prv_handle_measurement_msg(msg);
|
||||
break;
|
||||
|
||||
case HcMessageID_HRM_Enable:
|
||||
hrm_service_handle_enable(((HcHrmEnableCmd *)msg)->enable);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hc_endpoint_hrm_update_subscription(const HcHrmSubscription *subscription) {
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_HRM, HcMessageID_HRM_UpdateSubscription,
|
||||
(const uint8_t *)subscription, sizeof(*subscription));
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_logging.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
extern bool host_transport_ready;
|
||||
|
||||
void hc_endpoint_logging_handler(const HcProtocolMessage *msg) {
|
||||
uint8_t level;
|
||||
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Logging_SetLevel:
|
||||
level = msg->payload[0];
|
||||
pbl_log_set_level(level);
|
||||
break;
|
||||
case HcMessageID_Logging_GetLevel:
|
||||
level = pbl_log_get_level();
|
||||
hc_protocol_enqueue_response(msg, &level, sizeof(level));
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcLogging: unhandled message id: %d", msg->command_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The caller must have created the HcProtocolMessage + payload correctly.
|
||||
bool hc_endpoint_logging_send_msg(HcProtocolMessage *msg) {
|
||||
if (!host_transport_ready) {
|
||||
return true; // This is not an "out of space in the ring buffer" situation. Can't alert user.
|
||||
}
|
||||
|
||||
msg->endpoint_id = HcEndpointID_Logging;
|
||||
msg->command_id = HcMessageID_Logging_LogMsg;
|
||||
return hc_protocol_enqueue(msg);
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_pairing.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "ble_common.h"
|
||||
#include "ble_gap.h"
|
||||
|
||||
#include <bluetooth/hci_types.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void pair_reply(uint16_t conn_idx, bool is_confirmed) {
|
||||
ble_error_t e = ble_gap_pair_reply(conn_idx,
|
||||
is_confirmed /* should_accept */,
|
||||
is_confirmed /* should_bond */);
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "ble_gap_pair_reply: %d", e);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_handle_pairing_response(const HcProtocolMessagePairingResponsePayload *response) {
|
||||
Connection *connection = connection_by_address(&response->device);
|
||||
if (!connection) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Got pairing response, but disconnected in the mean time.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t conn_idx = connection_get_idx(connection);
|
||||
pair_reply(conn_idx, response->is_confirmed);
|
||||
}
|
||||
|
||||
void hc_endpoint_pairing_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Pairing_PairingResponse:
|
||||
prv_handle_pairing_response((const HcProtocolMessagePairingResponsePayload *)msg->payload);
|
||||
break;
|
||||
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Unexpected cmd ID: %d", msg->command_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hc_endpoint_pairing_send_pairing_request(const BTDeviceInternal *device) {
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Pairing, HcMessageID_Pairing_PairingRequest,
|
||||
(const uint8_t *)device, sizeof(*device));
|
||||
}
|
||||
|
||||
void hc_endpoint_pairing_send_pairing_complete(const BTDeviceInternal *device,
|
||||
HciStatusCode status) {
|
||||
const HcProtocolMessagePairingCompletePayload payload = {
|
||||
.device = *device,
|
||||
.status = status,
|
||||
};
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_Pairing, HcMessageID_Pairing_PairingComplete,
|
||||
(const uint8_t *)&payload, sizeof(payload));
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_pebble_pairing_service.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
|
||||
#include <bluetooth/pebble_pairing_service.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void hc_endpoint_pebble_pairing_service_send_ios_app_termination_detected(void) {
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_PebblePairingService,
|
||||
HcMessageID_PebblePairingServiceiOSAppTerminationDetected,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
void hc_endpoint_pebble_pairing_service_found_gateway(BTDeviceInternal *device) {
|
||||
hc_protocol_enqueue_with_payload(HcEndpointID_PebblePairingService,
|
||||
HcMessageID_PebblePairingServiceFoundGateway,
|
||||
(uint8_t *)device, sizeof(*device));
|
||||
}
|
||||
|
||||
void hc_endpoint_pebble_pairing_service_send_conn_params(const Connection *connection,
|
||||
const PebblePairingServiceConnParamsWrite *params, size_t params_length) {
|
||||
size_t len = (sizeof(HcProtocolMessage) + offsetof(HcPpsConnParamsPayload, conn_params)
|
||||
+ params_length);
|
||||
HcProtocolMessage *msg = (HcProtocolMessage *)kernel_zalloc_check(len);
|
||||
msg->endpoint_id = HcEndpointID_PebblePairingService;
|
||||
msg->command_id = HcMessageID_PebblePairingServiceConnParams;
|
||||
msg->message_length = len;
|
||||
|
||||
HcPpsConnParamsPayload *payload = (HcPpsConnParamsPayload *)&msg->payload[0];
|
||||
connection_get_address(connection, &payload->device);
|
||||
memcpy(&payload->conn_params, params, params_length);
|
||||
|
||||
hc_protocol_enqueue(msg);
|
||||
kernel_free(msg);
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "ble_gap.h"
|
||||
|
||||
#include "connection.h"
|
||||
|
||||
#include "dialog_utils.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
#include "hc_protocol/hc_endpoint_responsiveness.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <bluetooth/hci_types.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static void prv_handle_update_request(const HcProtocolMessageResponsivenessPayload *req) {
|
||||
const BleConnectionParamsUpdateReq *params = &req->params;
|
||||
gap_conn_params_t conn_params = {
|
||||
.interval_min = params->interval_min_1_25ms,
|
||||
.interval_max = params->interval_max_1_25ms,
|
||||
.slave_latency = params->slave_latency_events,
|
||||
.sup_timeout = params->supervision_timeout_10ms,
|
||||
};
|
||||
|
||||
Connection *conn = connection_by_address(&req->address);
|
||||
ble_error_t e = BLE_ERROR_NOT_CONNECTED;
|
||||
if (conn != NULL) {
|
||||
int conn_idx = connection_get_idx(conn);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Requesting conn param change, Conn Idx: %d - (%d %d %d %d)",
|
||||
conn_idx, (int)conn_params.interval_min, (int)conn_params.interval_max,
|
||||
(int)conn_params.slave_latency, (int)conn_params.sup_timeout);
|
||||
e = ble_gap_conn_param_update(conn_idx, &conn_params);
|
||||
}
|
||||
|
||||
if (e != BLE_STATUS_OK) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error prv_handle_update_request: %d", (int)e);
|
||||
BleConnectionParams params = {};
|
||||
hc_endpoint_responsiveness_notify_update(
|
||||
¶ms, &req->address, ble_error_to_hci_status_error(e));
|
||||
}
|
||||
}
|
||||
|
||||
void hc_endpoint_responsiveness_notify_update(
|
||||
const BleConnectionParams *params, const BTDeviceInternal *addr,
|
||||
HciStatusCode status) {
|
||||
uint8_t response_len = sizeof(HcProtocolMessage) +
|
||||
sizeof(BleConnectionUpdateCompleteEvent);
|
||||
|
||||
uint8_t buf[response_len];
|
||||
HcProtocolMessage *msg = (HcProtocolMessage *)&buf[0];
|
||||
|
||||
*msg = (HcProtocolMessage) {
|
||||
.message_length = response_len,
|
||||
.endpoint_id = HcEndpointID_Responsiveness,
|
||||
.command_id = HcMessageID_Id_ConnParamUpdateResponse,
|
||||
};
|
||||
|
||||
BleConnectionUpdateCompleteEvent *payload =
|
||||
(BleConnectionUpdateCompleteEvent *)msg->payload;
|
||||
*payload = (BleConnectionUpdateCompleteEvent) {
|
||||
.status = status,
|
||||
.dev_address = addr->address,
|
||||
};
|
||||
|
||||
if (params) {
|
||||
payload->conn_params = *params;
|
||||
}
|
||||
|
||||
hc_protocol_enqueue(msg);
|
||||
}
|
||||
|
||||
void hc_endpoint_responsiveness_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
case HcMessageID_Id_ConnParamUpdateReq:
|
||||
prv_handle_update_request((HcProtocolMessageResponsivenessPayload *)msg->payload);
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Unexpected command: 0x%x", (int)msg->command_id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_endpoint_test.h"
|
||||
#include "system/logging.h"
|
||||
#include <bluetooth/bt_test.h>
|
||||
|
||||
#include "core_dump.h"
|
||||
|
||||
// Dialog APIs
|
||||
#include "sdk_defs.h"
|
||||
#include "hw_rf.h"
|
||||
|
||||
void hw_fem_set_pin_config(BtlePaConfig config);
|
||||
void rwip_prevent_sleep(bool enable);
|
||||
void ble_force_wakeup();
|
||||
|
||||
|
||||
static void prv_hard_fault(void) {
|
||||
// Store 0x00000000 at 0x000003. *(int *)0x03 = 0
|
||||
__asm("mov r0, #0\n\t"
|
||||
"mov r1, #3\n\t"
|
||||
"str r0, [r1]\n\t");
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcTest: CoreDump - Unaligned Access succeeded!?!");
|
||||
}
|
||||
|
||||
static void prv_handle_core_dump_request(BtleCoreDump type) {
|
||||
switch (type) {
|
||||
case BtleCoreDump_UserRequest:
|
||||
core_dump(true);
|
||||
break;
|
||||
case BtleCoreDump_ForceHardFault:
|
||||
prv_hard_fault();
|
||||
break;
|
||||
case BtleCoreDump_Watchdog:
|
||||
while (1) {} // wedge the task
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcTest: unhandled core dump id: %d", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_enable_continuous_wave_mode(const HcTestUnmodTxStart *cmd) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "TX'ing unmodulated CW on BT channel %d", cmd->tx_channel);
|
||||
|
||||
// Prevent the RW ROM from powering down the radio in the future
|
||||
rwip_prevent_sleep(true);
|
||||
|
||||
// Power up the radio if the RW ROM already put us to sleep
|
||||
ble_force_wakeup();
|
||||
|
||||
// Wait for the BLE Radio to be up
|
||||
while (!REG_GETF(CRG_TOP, SYS_STAT_REG, BLE_IS_UP)) {}
|
||||
|
||||
// Start the CW pattern
|
||||
hw_rf_start_continuous_wave(0x1, cmd->tx_channel);
|
||||
}
|
||||
|
||||
extern void cm_lp_clk_force_available(bool force_available);
|
||||
|
||||
static void prv_handle_sleep_test_cmd(const HcProtocolMessage *msg) {
|
||||
const HcTestSleep *sleep_test = (HcTestSleep *)&msg->payload[0];
|
||||
|
||||
// Dialog gives the 32K clock time to settle by default. The settling time is 8s. Since we use a
|
||||
// digital clock this isn't really necessary. However, since there are issues around
|
||||
// entering/exiting sleep we may want a delay of some sort before sleeping. Thus, so the sleep
|
||||
// test can fail faster let's just override this check rather than set the timeout to 0
|
||||
cm_lp_clk_force_available(sleep_test->force_sleep);
|
||||
|
||||
|
||||
uint8_t response[20] = { 0 };
|
||||
hc_protocol_enqueue_response(msg, &response[0], sizeof(response));
|
||||
}
|
||||
|
||||
void hc_endpoint_test_handler(const HcProtocolMessage *msg) {
|
||||
switch (msg->command_id) {
|
||||
#if PLATFORM_ROBERT
|
||||
case HcMessageID_Test_Config_PA:
|
||||
PBL_LOG(LOG_LEVEL_INFO, "HcTest: Config PA: %d", msg->payload[0]);
|
||||
hw_fem_set_pin_config(msg->payload[0]);
|
||||
break;
|
||||
#endif
|
||||
case HCMessageID_Test_Sleep:
|
||||
prv_handle_sleep_test_cmd(msg);
|
||||
break;
|
||||
case HcMessageID_Test_UnmodulatedTxStart:
|
||||
prv_enable_continuous_wave_mode((HcTestUnmodTxStart *)&msg->payload[0]);
|
||||
break;
|
||||
case HcMessageID_Test_UnmodulatedTxStop:
|
||||
rwip_prevent_sleep(false);
|
||||
hw_rf_stop_continuous_wave();
|
||||
break;
|
||||
case HcMessageID_Test_Core_Dump:
|
||||
prv_handle_core_dump_request((BtleCoreDump) msg->payload[0]);
|
||||
break;
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "HcTest: unhandled message id: %d", msg->command_id);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "ad_ble.h"
|
||||
#include "co_bt.h"
|
||||
#include "hc_protocol/hc_endpoint_hci.h"
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
#include "hci_rom_passthrough.h"
|
||||
#include "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void hc_endpoint_hci_handler(const HcProtocolMessage *msg) {
|
||||
if (msg->command_id != HcMessageID_Hci_Cmd) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Unhandled HCI command id: 0x%x", (int)msg->command_id);
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "HCI CMD Received:");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)msg, msg->message_length);
|
||||
|
||||
hci_cmd_msg_t *hci_cmd = (hci_cmd_msg_t *)&msg->payload[0];
|
||||
|
||||
uint16_t opcode = hci_cmd->op_code;
|
||||
hci_rom_passthrough_send_cmd(
|
||||
HCI_OP2OGF(opcode), HCI_OP2OCF(opcode), &hci_cmd->param[0],
|
||||
hci_cmd->param_length);
|
||||
}
|
||||
|
||||
void hc_endpoint_enqueue_hci_evt(const uint8_t *hci_evt, uint8_t payload_len) {
|
||||
uint8_t hc_message_len = sizeof(HcProtocolMessage) + 1 /* for HCI_EVT_MSG */ + payload_len;
|
||||
uint8_t hc_protocol_message[hc_message_len];
|
||||
|
||||
HcProtocolMessage *hc_msg = (HcProtocolMessage *)&hc_protocol_message[0];
|
||||
memset(hc_msg, 0x00, hc_message_len);
|
||||
|
||||
*hc_msg = (HcProtocolMessage) {
|
||||
.message_length = sizeof(hc_protocol_message),
|
||||
.endpoint_id = HcEndpointID_Hci,
|
||||
.command_id = HcMessageID_Hci_Evt,
|
||||
};
|
||||
|
||||
uint8_t *hc_msg_data = &hc_msg->payload[0];
|
||||
*hc_msg_data++ = 0x4;
|
||||
|
||||
memcpy(hc_msg_data, hci_evt, payload_len);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Sending:");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)hc_msg, hc_message_len);
|
||||
|
||||
hc_protocol_enqueue(hc_msg);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hc_protocol/hc_protocol.h"
|
||||
|
||||
#include "hc_protocol/hc_endpoint_advert.h"
|
||||
#include "hc_protocol/hc_endpoint_analytics.h"
|
||||
#include "hc_protocol/hc_endpoint_bonding_sync.h"
|
||||
#include "hc_protocol/hc_endpoint_chip_id.h"
|
||||
#include "hc_protocol/hc_endpoint_ctl.h"
|
||||
#include "hc_protocol/hc_endpoint_discovery.h"
|
||||
#include "hc_protocol/hc_endpoint_gap_le_connect.h"
|
||||
#include "hc_protocol/hc_endpoint_gap_service.h"
|
||||
#include "hc_protocol/hc_endpoint_gatt.h"
|
||||
#include "hc_protocol/hc_endpoint_logging.h"
|
||||
#include "hc_protocol/hc_endpoint_hci.h"
|
||||
#include "hc_protocol/hc_endpoint_hrm.h"
|
||||
#include "hc_protocol/hc_endpoint_pairing.h"
|
||||
#include "hc_protocol/hc_endpoint_responsiveness.h"
|
||||
#include "hc_protocol/hc_endpoint_test.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
const HcProtocolMessageHandler g_hc_protocol_endpoints_table[HcEndpointIDCount] = {
|
||||
[HcEndpointID_Invalid] = NULL,
|
||||
[HcEndpointID_Ctl] = hc_endpoint_ctl_handler,
|
||||
[HcEndpointID_Hci] = hc_endpoint_hci_handler,
|
||||
[HcEndpointID_GapService] = hc_endpoint_gap_service_handler,
|
||||
[HcEndpointID_Id] = hc_endpoint_chip_id_handler,
|
||||
[HcEndpointID_Advert] = hc_endpoint_advert_handler,
|
||||
[HcEndpointID_Responsiveness] = hc_endpoint_responsiveness_handler,
|
||||
[HcEndpointID_Gatt] = hc_endpoint_gatt_handler_controller,
|
||||
[HcEndpointID_Discovery] = hc_endpoint_discovery_handler,
|
||||
[HcEndpointID_BondingSync] = hc_endpoint_bonding_sync_handler,
|
||||
[HcEndpointID_Pairing] = hc_endpoint_pairing_handler,
|
||||
[HcEndpointID_GapLEConnect] = hc_endpoint_gap_le_connect_handler,
|
||||
[HcEndpointID_Logging] = hc_endpoint_logging_handler,
|
||||
[HcEndpointID_Analytics] = hc_endpoint_analytics_ctlr_handler,
|
||||
[HcEndpointID_Test] = hc_endpoint_test_handler,
|
||||
[HcEndpointID_HRM] = hc_endpoint_hrm_handler,
|
||||
};
|
||||
|
||||
void hc_protocol_cb_dispatch_handler(
|
||||
const HcProtocolMessageHandler handler, HcProtocolMessage *message, bool *should_free) {
|
||||
handler(message);
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "ad_ble.h"
|
||||
#include "ble_common.h"
|
||||
#include "ble_mgr.h"
|
||||
#include "ble_mgr_irb_common.h"
|
||||
#include "co_bt.h"
|
||||
#include "hc_protocol/hc_endpoint_hci.h"
|
||||
#include "hci_rom_passthrough.h"
|
||||
#include "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include "llc.h"
|
||||
#include "llm.h"
|
||||
#include "gapc.h"
|
||||
|
||||
#include <util/attributes.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
bool hci_rom_passthrough_send_cmd(
|
||||
uint16_t ogf, uint16_t ocf, const uint8_t *param_buf, uint8_t param_length) {
|
||||
|
||||
uint8_t msg_size = sizeof( irb_ble_stack_msg_t ) + sizeof(hci_cmd_msg_t) + param_length;
|
||||
irb_ble_stack_msg_t *msg_buf = OS_MALLOC(msg_size); // memory free'd in ad_ble.c:ble_task()
|
||||
|
||||
*msg_buf = (irb_ble_stack_msg_t) {
|
||||
.op_code = IRB_BLE_STACK_MSG,
|
||||
.msg_type = HCI_CMD_MSG,
|
||||
.msg_size = HCI_CMD_HEADER_LENGTH + param_length,
|
||||
};
|
||||
|
||||
hci_cmd_msg_t *hci_cmd = (hci_cmd_msg_t *)&msg_buf->msg;
|
||||
*hci_cmd = (hci_cmd_msg_t) {
|
||||
.op_code = HCI_OPCODE(ocf, ogf),
|
||||
.param_length = param_length,
|
||||
};
|
||||
|
||||
if (param_length != 0) {
|
||||
memcpy(&hci_cmd->param, param_buf, param_length);
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Sending HCI CMD to ROM stack:");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)hci_cmd, msg_buf->msg_size);
|
||||
|
||||
bool result = (ad_ble_command_queue_send(&msg_buf, OS_QUEUE_FOREVER) == pdPASS);
|
||||
return result;
|
||||
}
|
||||
|
||||
void hci_rom_passthrough_handle_evt(hci_evt_msg_t *hci_evt) {
|
||||
uint16_t payload_len = sizeof(hci_evt_msg_t) + hci_evt->param_length - sizeof(hci_evt->param);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "HCI Event Response:");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)hci_evt, payload_len);
|
||||
|
||||
hc_endpoint_enqueue_hci_evt((uint8_t *)hci_evt, payload_len);
|
||||
}
|
||||
|
||||
#if SUPPORTS_PACKET_LENGTH_EXTENSION
|
||||
void hci_initiate_length_change(uint16_t conn_idx) {
|
||||
uint16_t connhdl = gapc_get_conhdl(conn_idx);
|
||||
|
||||
struct llc_env_tag *llc_env_ptr = llc_env[connhdl];
|
||||
// The ROM HCI handler will only send a length request if it thinks we have requested a parameter
|
||||
// change. Change the setting here to force the negotiation to be sent.
|
||||
// Check out ROM function hci_le_set_data_length_cmd_handler()
|
||||
llc_env_ptr->connMaxTxOctets = LE_LENGTH_EXT_OCTETS_MIN + 1;
|
||||
|
||||
// In llc_le_length_conn_init_func_wa() we set the RX window sizes to be the minimum packet size
|
||||
// so that in case the other side sends an LL_LENGTH_REQ and there are interopobility issues the
|
||||
// connection will start without changing the size. If this routine gets called, it's been
|
||||
// determined that the device connected to supports extended packets so bump our supported RX
|
||||
// window sizes to the max allowed so these values are sent as part of the LL_LENGTH_REQ
|
||||
llc_env_ptr->connMaxRxOctets = LE_LENGTH_EXT_OCTETS_MAX;
|
||||
llc_env_ptr->connMaxRxTime = LE_LENGTH_EXT_TIME_MAX;
|
||||
|
||||
struct PACKED {
|
||||
uint16_t connhdl;
|
||||
uint16_t tx_octets;
|
||||
uint16_t tx_time;
|
||||
} params = {
|
||||
.connhdl = connhdl,
|
||||
.tx_octets = LE_LENGTH_EXT_OCTETS_MIN,
|
||||
.tx_time = LE_LENGTH_EXT_TIME_MIN,
|
||||
};
|
||||
|
||||
uint16_t ocf = HCI_OP2OCF(HCI_LE_SET_DATA_LENGTH_CMD_OPCODE); // 0x22
|
||||
uint16_t ogf = HCI_OP2OGF(HCI_LE_SET_DATA_LENGTH_CMD_OPCODE); // 0x08
|
||||
|
||||
hci_rom_passthrough_send_cmd(ogf, ocf, (uint8_t *)¶ms, sizeof(params));
|
||||
}
|
||||
#endif
|
||||
|
||||
void test_hci_passthrough(void) {
|
||||
static int i = 0;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "===Reset===");
|
||||
hci_rom_passthrough_send_cmd(0x3, 0x3, NULL, 0);
|
||||
break;
|
||||
case 1: {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "===LE Transmit Test===");
|
||||
uint8_t params[] = {0x00, 0x20, 0x01};
|
||||
hci_rom_passthrough_send_cmd(0x8, 0x1e, ¶ms[0], sizeof(params));
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "===LE Stop Tx Test===");
|
||||
hci_rom_passthrough_send_cmd(0x8, 0x1f, NULL, 0);
|
||||
break;
|
||||
case 3: {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "===LE Receiver Test===");
|
||||
uint8_t params[] = { 0x00 };
|
||||
hci_rom_passthrough_send_cmd(0x8, 0x1d, ¶ms[0], sizeof(params));
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "===LE Stop Rx Test===");
|
||||
hci_rom_passthrough_send_cmd(0x8, 0x1f, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
32
src/bluetooth-fw/da1468x/controller/main/src/hexdump.c
Normal file
32
src/bluetooth-fw/da1468x/controller/main/src/hexdump.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/hexdump.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern int _write(int file, char *ptr, int len);
|
||||
|
||||
static void prv_retarget_write_line_cb(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "%s", line_buffer);
|
||||
}
|
||||
|
||||
void hexdump_log_src(int level, const uint8_t *data, size_t length) {
|
||||
hexdump(NULL, 0, level, data, length, prv_retarget_write_line_cb);
|
||||
}
|
430
src/bluetooth-fw/da1468x/controller/main/src/host_transport.c
Normal file
430
src/bluetooth-fw/da1468x/controller/main/src/host_transport.c
Normal file
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "host_transport.h"
|
||||
#include "host_transport_protocol.h"
|
||||
#include "host_transport_impl.h"
|
||||
|
||||
#include "hc_protocol/hc_protocol.h"
|
||||
|
||||
#include "board.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/hexdump.h"
|
||||
#include "tasks.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/circular_buffer.h"
|
||||
#include "util/crc32.h"
|
||||
|
||||
#include "mcu/interrupts.h"
|
||||
|
||||
#include <hw_wkup.h>
|
||||
#include <osal.h>
|
||||
#include <platform_devices.h>
|
||||
#include <sdk_defs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys_watchdog.h>
|
||||
|
||||
// Design doc:
|
||||
// https://docs.google.com/document/d/1or2Ygs3sWt_5XNW_Mpe3Vxmhwuh3DTzdgZr6QlEe7iQ/edit#
|
||||
|
||||
#define HOST_TRANSPORT_DEBUG (0)
|
||||
|
||||
#if HOST_TRANSPORT_DEBUG
|
||||
#define HOST_TRANSPORT_DEBUG_LOG(fmt, ...) PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
#define HOST_TRANSPORT_DEBUG_LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
// Used to Host Logging
|
||||
bool host_transport_ready = false;
|
||||
|
||||
static bool s_is_transacting __RETAINED;
|
||||
static CircularBuffer s_tx_buffer __RETAINED;
|
||||
static CircularBuffer s_rx_buffer __RETAINED;
|
||||
static uint8_t s_tx_storage[HOST_TRANSPORT_CTLR_TX_BUFFER_SIZE] __RETAINED;
|
||||
static uint8_t s_rx_storage[HOST_TRANSPORT_CTLR_RX_BUFFER_SIZE] __RETAINED;
|
||||
|
||||
static void prv_lock(void) {
|
||||
OS_ENTER_CRITICAL_SECTION();
|
||||
}
|
||||
|
||||
static void prv_unlock(void) {
|
||||
OS_LEAVE_CRITICAL_SECTION();
|
||||
}
|
||||
|
||||
static void prv_core_dump(void) {
|
||||
// TODO Implement core dump
|
||||
PBL_ASSERTN(0);
|
||||
}
|
||||
|
||||
void host_transport_set_mcu_int(bool is_ready_to_transact) {
|
||||
if (is_ready_to_transact) {
|
||||
hw_gpio_set_active(HOST_SPI->mcu_int.port, HOST_SPI->mcu_int.pin);
|
||||
} else {
|
||||
hw_gpio_set_inactive(HOST_SPI->mcu_int.port, HOST_SPI->mcu_int.pin);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_disable_spi_cs_wakeup_interrupt_handling_and_unblock_transaction_loop(void) {
|
||||
if (__atomic_test_and_set(&s_is_transacting, __ATOMIC_RELAXED)) {
|
||||
// Already transacting
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable SPI CS wakeup interrupt, otherwise the ISR will keep firing during the SPI transfers.
|
||||
HW_WKUP_REG_SETF(CTRL, WKUP_ENABLE_IRQ, 0);
|
||||
|
||||
// Put a task notification to our task to act upon the interrupt:
|
||||
if (mcu_state_is_isr()) {
|
||||
OS_TASK_NOTIFY_FROM_ISR(DialogTaskList[DialogTask_HostTrans], 0, eNoAction);
|
||||
} else {
|
||||
OS_TASK_NOTIFY(DialogTaskList[DialogTask_HostTrans], 0, eNoAction);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_reenable_spi_cs_wakeup_interrupt_handling(void) {
|
||||
hw_wkup_reset_interrupt();
|
||||
HW_WKUP_REG_SETF(CTRL, WKUP_ENABLE_IRQ, 1);
|
||||
}
|
||||
|
||||
static void prv_sample_spi_cs_and_unblock_loop_if_needed(void) {
|
||||
// Sample CS, to handle case where the edge was missed:
|
||||
bool is_cs_asserted = !hw_gpio_get_pin_status(HOST_SPI->spi.cs.port,
|
||||
HOST_SPI->spi.cs.pin);
|
||||
if (is_cs_asserted && !s_is_transacting) {
|
||||
// Edge was missed, pretend interrupt to be fired:
|
||||
prv_disable_spi_cs_wakeup_interrupt_handling_and_unblock_transaction_loop();
|
||||
}
|
||||
}
|
||||
|
||||
static bool prv_is_scs_asserted(void) {
|
||||
return (false == hw_gpio_get_pin_status(HOST_SPI->spi.cs.port, HOST_SPI->spi.cs.pin));
|
||||
}
|
||||
|
||||
// TODO: Can we avoid re-configuring every time? We might be missing a trigger if it happens
|
||||
// before this reconfiguring is completed... :(
|
||||
// Dialog Aart: "Unfortunately it is not possible to program an I/O pin with both functions as
|
||||
// Wake-up and SPI (or other). Either toggle between the 2 functions or define a dedicated GPIO pin
|
||||
// for Wake-up."
|
||||
//
|
||||
// NB: used by core_dump.c
|
||||
void host_transport_configure_spi_scs_pin(SCSPinFunction function) {
|
||||
if (function == SCSPinFunction_Wakeup_GPIO) {
|
||||
// Configure SCS to generate a wake up interrupt when the line is pulled down:
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.cs.port, HOST_SPI->spi.cs.pin,
|
||||
HW_GPIO_MODE_INPUT, HW_GPIO_FUNC_GPIO);
|
||||
} else {
|
||||
// Configure the SCS pin as "SPI Enable" alternate function:
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.cs.port, HOST_SPI->spi.cs.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.cs.function);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_spi_chip_select_interrupt_handler(void) {
|
||||
// Interrupt handler should always reset interrupt state, otherwise it will be called again.
|
||||
hw_wkup_reset_interrupt();
|
||||
|
||||
prv_disable_spi_cs_wakeup_interrupt_handling_and_unblock_transaction_loop();
|
||||
}
|
||||
|
||||
// NB: used by core_dump.c
|
||||
void init_spi_pins(void) {
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.clk.port, HOST_SPI->spi.clk.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.clk.function);
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.mosi_di.port, HOST_SPI->spi.mosi_di.pin,
|
||||
HW_GPIO_MODE_INPUT, HOST_SPI->spi.mosi_di.function);
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.miso_do.port, HOST_SPI->spi.miso_do.pin,
|
||||
HW_GPIO_MODE_OUTPUT, HOST_SPI->spi.miso_do.function);
|
||||
hw_gpio_set_pin_function(HOST_SPI->spi.cs_2.port, HOST_SPI->spi.cs_2.pin,
|
||||
HW_GPIO_MODE_INPUT, HW_GPIO_FUNC_GPIO);
|
||||
|
||||
hw_gpio_configure_pin(HOST_SPI->mcu_int.port, HOST_SPI->mcu_int.pin,
|
||||
HW_GPIO_MODE_OUTPUT, HOST_SPI->mcu_int.function, false /* is_high */);
|
||||
}
|
||||
|
||||
static void prv_do_blocking_spi_transfer(spi_device dev, spi_transfer_data *transfer) {
|
||||
ad_spi_complex_transact(dev, transfer, 1, host_transport_set_mcu_int);
|
||||
}
|
||||
|
||||
static bool prv_transact(spi_device dev) {
|
||||
const uint8_t *tx_bytes = NULL;
|
||||
|
||||
prv_lock();
|
||||
uint16_t tx_bytes_available = circular_buffer_get_read_space_remaining(&s_tx_buffer);
|
||||
if (tx_bytes_available) {
|
||||
circular_buffer_read(&s_tx_buffer, tx_bytes_available, &tx_bytes, &tx_bytes_available);
|
||||
}
|
||||
uint8_t *rx_buffer_ptr = NULL;
|
||||
uint16_t rx_bytes_available = circular_buffer_write_prepare(&s_rx_buffer, &rx_buffer_ptr);
|
||||
prv_unlock();
|
||||
|
||||
// OK to use the stack for these variables,
|
||||
// because ad_spi_complex_transact blocks until transfer is completed:
|
||||
SPITransportMsgStatus remote_status_in = {};
|
||||
SPITransportMsgStatus local_status_out = {
|
||||
.msg_id = SPITransportMsgID_Status,
|
||||
.bytes_sendable_count = tx_bytes_available,
|
||||
.bytes_receivable_count = rx_bytes_available,
|
||||
};
|
||||
size_t crc_len = sizeof(local_status_out) - sizeof(local_status_out.crc);
|
||||
local_status_out.crc = crc32(CRC32_INIT, &local_status_out, crc_len);
|
||||
|
||||
// Full duplex transaction to exchange Status:
|
||||
{
|
||||
spi_transfer_data transfer = {
|
||||
.wbuf = &local_status_out,
|
||||
.rbuf = &remote_status_in,
|
||||
.length = sizeof(SPITransportMsgStatus),
|
||||
};
|
||||
prv_do_blocking_spi_transfer(dev, &transfer);
|
||||
|
||||
// Check the incoming CRC
|
||||
uint32_t crc = crc32(CRC32_INIT, &remote_status_in, sizeof(remote_status_in));
|
||||
if (crc != CRC32_RESIDUE) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "CRC32 failed on Status Exchange: 0x%"PRIu32 " vs 0x%"PRIu32,
|
||||
crc, (uint32_t)CRC32_RESIDUE);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "->OUT");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)&local_status_out, sizeof(local_status_out));
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "->IN");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)&remote_status_in, sizeof(remote_status_in));
|
||||
prv_core_dump();
|
||||
}
|
||||
|
||||
HOST_TRANSPORT_DEBUG_LOG("Local Status: %u bytes sendable, %u bytes receivable",
|
||||
local_status_out.bytes_sendable_count,
|
||||
local_status_out.bytes_receivable_count);
|
||||
HOST_TRANSPORT_DEBUG_LOG("Remote Status: %u bytes sendable, %u bytes receivable",
|
||||
remote_status_in.bytes_sendable_count,
|
||||
remote_status_in.bytes_receivable_count);
|
||||
}
|
||||
|
||||
// Single duplex write:
|
||||
size_t tx_len = MIN(tx_bytes_available, remote_status_in.bytes_receivable_count);
|
||||
if (tx_len) {
|
||||
// Calculate the CRC before Transmitting
|
||||
SPITransportMsgFooter tx_footer;
|
||||
tx_footer.crc = crc32(CRC32_INIT, tx_bytes, tx_len);
|
||||
|
||||
HOST_TRANSPORT_DEBUG_LOG("Expecting to write %u bytes:", tx_len);
|
||||
spi_transfer_data transfer = {
|
||||
.wbuf = tx_bytes,
|
||||
.length = tx_len,
|
||||
};
|
||||
prv_do_blocking_spi_transfer(dev, &transfer);
|
||||
|
||||
prv_lock();
|
||||
circular_buffer_consume(&s_tx_buffer, tx_len);
|
||||
prv_unlock();
|
||||
|
||||
// Send the footer
|
||||
spi_transfer_data tx_footer_transfer = {
|
||||
.wbuf = &tx_footer,
|
||||
.length = sizeof(tx_footer),
|
||||
};
|
||||
|
||||
prv_do_blocking_spi_transfer(dev, &tx_footer_transfer);
|
||||
|
||||
HOST_TRANSPORT_DEBUG_LOG("Sent %u bytes.", tx_len);
|
||||
} else {
|
||||
HOST_TRANSPORT_DEBUG_LOG("Nothing to send.");
|
||||
}
|
||||
|
||||
// Single duplex read:
|
||||
const size_t rx_len = MIN(remote_status_in.bytes_sendable_count, rx_bytes_available);
|
||||
if (rx_len) {
|
||||
HOST_TRANSPORT_DEBUG_LOG("Expecting to read %u bytes:", rx_len);
|
||||
spi_transfer_data transfer = {
|
||||
.rbuf = rx_buffer_ptr,
|
||||
.length = rx_len,
|
||||
};
|
||||
prv_do_blocking_spi_transfer(dev, &transfer);
|
||||
|
||||
// Read CRC32 & confirm
|
||||
SPITransportMsgFooter rx_footer;
|
||||
spi_transfer_data rx_footer_transfer = {
|
||||
.rbuf = &rx_footer,
|
||||
.length = sizeof(rx_footer),
|
||||
};
|
||||
prv_do_blocking_spi_transfer(dev, &rx_footer_transfer);
|
||||
uint32_t crc = crc32(CRC32_INIT, rx_buffer_ptr, rx_len);
|
||||
if (crc != rx_footer.crc) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "CRC32 failed on Data Read: 0x%"PRIu32 " vs 0x%"PRIu32,
|
||||
crc, rx_footer.crc);
|
||||
prv_core_dump();
|
||||
}
|
||||
#if HOST_TRANSPORT_DEBUG
|
||||
HOST_TRANSPORT_DEBUG_LOG("Received %u bytes:", rx_len);
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, rx_buffer_ptr, rx_len);
|
||||
#endif
|
||||
} else {
|
||||
HOST_TRANSPORT_DEBUG_LOG("Nothing to receive.");
|
||||
}
|
||||
|
||||
bool has_more_rx_data = (remote_status_in.bytes_sendable_count > rx_bytes_available);
|
||||
bool should_continue_to_rx_data =
|
||||
(has_more_rx_data && (local_status_out.bytes_receivable_count != 0));
|
||||
if (has_more_rx_data && !should_continue_to_rx_data) {
|
||||
HOST_TRANSPORT_DEBUG_LOG("Host Transport Receive Buffer Full, exiting from rx'ing to process");
|
||||
}
|
||||
|
||||
prv_lock();
|
||||
circular_buffer_write_finish(&s_rx_buffer, rx_len);
|
||||
|
||||
// Check if more data is available in the circular buffer.
|
||||
// If not, flip s_is_transacting back while the lock is taken. Otherwise, a concurrent call to
|
||||
// host_transport_tx_enqueue() would be prevented to unblock the transaction loop again.
|
||||
bool has_more_tx_data = circular_buffer_get_read_space_remaining(&s_tx_buffer);
|
||||
bool should_continue = (has_more_tx_data || should_continue_to_rx_data);
|
||||
s_is_transacting = should_continue;
|
||||
prv_unlock();
|
||||
|
||||
return should_continue;
|
||||
}
|
||||
|
||||
static void prv_host_transport_main(void *ctx) {
|
||||
static int8_t s_ble_host_transport_wdog_id;
|
||||
s_ble_host_transport_wdog_id = sys_watchdog_register(false);
|
||||
while (true) {
|
||||
sys_watchdog_notify(s_ble_host_transport_wdog_id);
|
||||
sys_watchdog_suspend(s_ble_host_transport_wdog_id);
|
||||
|
||||
// Handle missed SPI CS edge:
|
||||
prv_sample_spi_cs_and_unblock_loop_if_needed();
|
||||
|
||||
// Block the transaction loop until there's either data to transmit, or until the master
|
||||
// asserts the SCS line:
|
||||
xTaskNotifyWait(0, ~0, NULL, portMAX_DELAY);
|
||||
|
||||
sys_watchdog_resume(s_ble_host_transport_wdog_id);
|
||||
|
||||
HOST_TRANSPORT_DEBUG_LOG("prv_host_transport_main loop unblocked, about to read..");
|
||||
|
||||
spi_device dev = ad_spi_open(PEBBLE_HOST);
|
||||
ad_spi_device_acquire(dev);
|
||||
|
||||
host_transport_configure_spi_scs_pin(SCSPinFunction_SPI_CS);
|
||||
|
||||
while (prv_transact(dev)) {};
|
||||
|
||||
// Re-enable interrupt handling before processing,
|
||||
// so that endpoint handlers can cause the loop to get unblocked immediately:
|
||||
prv_reenable_spi_cs_wakeup_interrupt_handling();
|
||||
|
||||
if (DialogTaskList[DialogTask_Ble] == 0) {
|
||||
// We don't bring up the ble task until we have received an init cmd
|
||||
hc_protocol_process_receive_buffer();
|
||||
} else {
|
||||
OS_TASK_NOTIFY(DialogTaskList[DialogTask_Ble], 0x0, eSetBits);
|
||||
}
|
||||
|
||||
ad_spi_device_release(dev);
|
||||
ad_spi_close(dev);
|
||||
|
||||
host_transport_configure_spi_scs_pin(SCSPinFunction_Wakeup_GPIO);
|
||||
}
|
||||
}
|
||||
|
||||
HostTransportEnqueueStatus host_transport_tx_enqueue(const uint8_t *buffer, size_t length) {
|
||||
PBL_ASSERTN(length < HOST_TRANSPORT_CTLR_TX_BUFFER_SIZE &&
|
||||
length < HOST_TRANSPORT_HOST_RX_BUFFER_SIZE);
|
||||
|
||||
prv_lock();
|
||||
bool success = circular_buffer_write(&s_tx_buffer, buffer, length);
|
||||
prv_unlock();
|
||||
|
||||
if (success) {
|
||||
prv_disable_spi_cs_wakeup_interrupt_handling_and_unblock_transaction_loop();
|
||||
return HostTransportEnqueueStatus_Success;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Failed to enqueue %u bytes", length);
|
||||
return HostTransportEnqueueStatus_RetryLater;
|
||||
}
|
||||
|
||||
size_t host_transport_rx_get_length(void) {
|
||||
prv_lock();
|
||||
size_t rx_length = circular_buffer_get_read_space_remaining(&s_rx_buffer);
|
||||
prv_unlock();
|
||||
return rx_length;
|
||||
}
|
||||
|
||||
bool host_transport_rx_read(uint8_t **data_ptr_out, size_t length) {
|
||||
prv_lock();
|
||||
bool caller_should_free = false;
|
||||
PBL_ASSERTN(circular_buffer_read_or_copy(&s_rx_buffer, data_ptr_out, length,
|
||||
kernel_malloc, &caller_should_free));
|
||||
prv_unlock();
|
||||
return caller_should_free;
|
||||
}
|
||||
|
||||
void host_transport_rx_consume(size_t length) {
|
||||
prv_lock();
|
||||
circular_buffer_consume(&s_rx_buffer, length);
|
||||
prv_unlock();
|
||||
|
||||
hc_protocol_buffer_gained_space();
|
||||
}
|
||||
|
||||
bool host_transport_is_current_task_host_transport_task(void) {
|
||||
return (DialogTaskList[DialogTask_HostTrans] == xTaskGetCurrentTaskHandle());
|
||||
}
|
||||
|
||||
// NB: used by core_dump.c
|
||||
void host_transport_init_periph(void) {
|
||||
init_spi_pins();
|
||||
}
|
||||
|
||||
static void prv_wakeup_init(void) {
|
||||
hw_wkup_init(NULL);
|
||||
hw_wkup_set_counter_threshold(1);
|
||||
hw_wkup_set_debounce_time(0);
|
||||
hw_wkup_configure_pin(HOST_SPI->spi.cs.port, HOST_SPI->spi.cs.pin,
|
||||
true /* enabled */, HW_WKUP_PIN_STATE_LOW);
|
||||
hw_wkup_reset_interrupt();
|
||||
hw_wkup_reset_counter();
|
||||
hw_wkup_register_interrupt(prv_spi_chip_select_interrupt_handler, 1);
|
||||
}
|
||||
|
||||
void host_transport_init(void) {
|
||||
SPI_BUS_INIT(SPI1);
|
||||
SPI_DEVICE_INIT(PEBBLE_HOST);
|
||||
|
||||
circular_buffer_init(&s_tx_buffer, s_tx_storage, HOST_TRANSPORT_CTLR_TX_BUFFER_SIZE);
|
||||
circular_buffer_init(&s_rx_buffer, s_rx_storage, HOST_TRANSPORT_CTLR_RX_BUFFER_SIZE);
|
||||
|
||||
// Start the task that runs the transaction loop:
|
||||
OS_BASE_TYPE status = OS_TASK_CREATE("HT", prv_host_transport_main,
|
||||
NULL /* ctx */, 1280 /* stack_size */,
|
||||
(tskIDLE_PRIORITY + 1) /* same as BLE task */,
|
||||
DialogTaskList[DialogTask_HostTrans]);
|
||||
PBL_ASSERTN(status == OS_TASK_CREATE_SUCCESS);
|
||||
|
||||
prv_wakeup_init();
|
||||
|
||||
host_transport_configure_spi_scs_pin(SCSPinFunction_Wakeup_GPIO);
|
||||
|
||||
// Read SCS to see if it's already asserted by the MCU and we need to run the loop already:
|
||||
if (prv_is_scs_asserted()) {
|
||||
prv_disable_spi_cs_wakeup_interrupt_handling_and_unblock_transaction_loop();
|
||||
}
|
||||
|
||||
hc_protocol_boot();
|
||||
hc_protocol_init();
|
||||
host_transport_ready = true;
|
||||
}
|
118
src/bluetooth-fw/da1468x/controller/main/src/hrm.c
Normal file
118
src/bluetooth-fw/da1468x/controller/main/src/hrm.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "hrm_impl.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "dialog_utils.h"
|
||||
#include "gatt_local_services.h"
|
||||
#include "service_changed.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "hc_protocol/hc_endpoint_hrm.h"
|
||||
|
||||
// Dialog SDK:
|
||||
#include "hrs.h"
|
||||
#include "osal.h"
|
||||
|
||||
#include <btutil/bt_device.h>
|
||||
#include <util/size.h>
|
||||
|
||||
static bool s_is_hrm_enabled = false;
|
||||
static hr_service_t s_hrs;
|
||||
|
||||
void hrm_service_init(bool is_hrm_supported_and_enabled) {
|
||||
s_is_hrm_enabled = is_hrm_supported_and_enabled;
|
||||
ble_service_add(&s_hrs.svc);
|
||||
}
|
||||
|
||||
static void prv_handle_subscribe(uint16_t conn_idx, bool enabled) {
|
||||
Connection *connection = connection_by_idx(conn_idx);
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
HcHrmSubscription subscription;
|
||||
subscription.is_subscribed = enabled;
|
||||
connection_get_address(connection, &subscription.device);
|
||||
hc_endpoint_hrm_update_subscription(&subscription);
|
||||
}
|
||||
|
||||
void hrm_service_register(uint16_t start_hdl) {
|
||||
if (!s_is_hrm_enabled) {
|
||||
// Set all handlers to NULL, so the ble_service dispatcher won't ever try to call us:
|
||||
s_hrs.svc = (const ble_service_t) {};
|
||||
return;
|
||||
}
|
||||
|
||||
const hrs_body_sensor_location_t sensor_location = HRS_SENSOR_LOC_WRIST;
|
||||
static const hrs_callbacks_t s_callbacks = {
|
||||
.ee_reset = NULL, // Beat-to-beat interval data is not supported at the moment.
|
||||
.notif_changed = prv_handle_subscribe,
|
||||
};
|
||||
hrs_init(&s_hrs, sensor_location, &s_callbacks, start_hdl);
|
||||
PBL_ASSERTN(start_hdl == s_hrs.svc.start_h);
|
||||
}
|
||||
|
||||
void hrm_service_handle_measurement(const BleHrmServiceMeasurement *measurement,
|
||||
const BTDeviceInternal *permitted_devices,
|
||||
size_t num_permitted_devices) {
|
||||
const hrs_measurement_t hrs_measurement = {
|
||||
.bpm = measurement->bpm,
|
||||
.contact_supported = true,
|
||||
.contact_detected = measurement->is_on_wrist,
|
||||
.has_energy_expended = false,
|
||||
// NTH: Use calories burnt calculation from the activity algo to set energy_expended.
|
||||
// https://pebbletechnology.atlassian.net/browse/PBL-42867
|
||||
.energy_expended = 0,
|
||||
.rr_num = 0,
|
||||
};
|
||||
|
||||
// Only notify permitted devices:
|
||||
gap_device_t devices[8];
|
||||
size_t length = ARRAY_LENGTH(devices);
|
||||
ble_gap_get_devices(GAP_DEVICE_FILTER_CONNECTED, NULL, &length, devices);
|
||||
for (int i = 0; i < (int)length; ++i) {
|
||||
const gap_device_t *const gap_device = &devices[i];
|
||||
BTDeviceInternal device;
|
||||
dialog_utils_bd_address_to_bt_device(&gap_device->address, &device);
|
||||
for (int j = 0; j < (int)num_permitted_devices; ++j) {
|
||||
if (bt_device_internal_equal(&device, &permitted_devices[j])) {
|
||||
hrs_notify_measurement(&s_hrs.svc, gap_device->conn_idx, &hrs_measurement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hrm_service_handle_enable(bool enable) {
|
||||
if (s_is_hrm_enabled == enable) {
|
||||
return;
|
||||
}
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "hrm_service_handle_enable %u", enable);
|
||||
s_is_hrm_enabled = enable;
|
||||
|
||||
if (enable) {
|
||||
hrm_service_register(HRM_SERVICE_EXPECTED_ATT_STARTING_HANDLE);
|
||||
service_changed_send_indication_to_all(HRM_SERVICE_EXPECTED_ATT_STARTING_HANDLE,
|
||||
HRM_SERVICE_EXPECTED_ATT_ENDING_HANDLE);
|
||||
} else {
|
||||
// Unfortunately, there is no clean way to remove a service from RivieraWaves once it has been
|
||||
// added. The various ble_... APIs do nuke the GATT DB entirely, but can only be called when
|
||||
// there is no connection / on-going air operations.
|
||||
// The next time the BT stack is restarted (granted the user pref hasn't changed to "enabled"),
|
||||
// the service will be gone.
|
||||
}
|
||||
}
|
107
src/bluetooth-fw/da1468x/controller/main/src/kernel_heap.c
Normal file
107
src/bluetooth-fw/da1468x/controller/main/src/kernel_heap.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "kernel/pbl_malloc.h"
|
||||
|
||||
#include "system/passert.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/heap.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "sdk_defs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static Heap s_kernel_heap;
|
||||
static bool s_interrupts_disabled_by_heap;
|
||||
|
||||
// FIXME: Cortex-M0 does not have the CMSIS, therefore can't disable interrupts for lower priority
|
||||
// tasks. It's all or nothing. We should come up with a better way for this.
|
||||
static void prv_heap_lock(void *ctx) {
|
||||
if ((__get_PRIMASK() & 0x1) == 0) {
|
||||
__disable_irq();
|
||||
s_interrupts_disabled_by_heap = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_heap_unlock(void *ctx) {
|
||||
if (s_interrupts_disabled_by_heap) {
|
||||
__enable_irq();
|
||||
s_interrupts_disabled_by_heap = false;
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_heap_init(void) {
|
||||
const bool fuzz_on_free = true;
|
||||
|
||||
extern uint8_t __heap_start;
|
||||
uint8_t *heap_start = &__heap_start;
|
||||
uint8_t *heap_end = heap_start + configTOTAL_HEAP_SIZE;
|
||||
heap_init(&s_kernel_heap, heap_start, heap_end, fuzz_on_free);
|
||||
heap_set_lock_impl(&s_kernel_heap, (HeapLockImpl) {
|
||||
.lock_function = prv_heap_lock,
|
||||
.unlock_function = prv_heap_unlock
|
||||
});
|
||||
}
|
||||
|
||||
// kernel_* functions that allocate on the kernel heap
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
static ALWAYS_INLINE void *prv_heap_malloc(size_t bytes) {
|
||||
const uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
|
||||
return heap_malloc(&s_kernel_heap, bytes, saved_lr);
|
||||
}
|
||||
|
||||
void *kernel_malloc(size_t bytes) {
|
||||
return prv_heap_malloc(bytes);
|
||||
}
|
||||
|
||||
void *kernel_zalloc(size_t bytes) {
|
||||
void *ptr = prv_heap_malloc(bytes);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, bytes);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *kernel_malloc_check(size_t bytes) {
|
||||
void *ptr = prv_heap_malloc(bytes);
|
||||
PBL_ASSERTN(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *kernel_zalloc_check(size_t bytes) {
|
||||
void *ptr = prv_heap_malloc(bytes);
|
||||
PBL_ASSERTN(ptr);
|
||||
memset(ptr, 0, bytes);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void kernel_free(void *ptr) {
|
||||
register uintptr_t lr __asm("lr");
|
||||
uintptr_t saved_lr = lr;
|
||||
|
||||
heap_free(&s_kernel_heap, ptr, saved_lr);
|
||||
}
|
||||
|
||||
// Fun fact: The unconditional branch range for cortex M0 is very small (See:
|
||||
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/BABEFHAE.html)
|
||||
// This seems to result in the compiler always emitting "bl" instructions even
|
||||
// if a function is just calling one other function. Thus, just alias the
|
||||
// following routines to the function they call so that we get the lr from the
|
||||
// original call site saved.
|
||||
ALIAS("kernel_malloc") void* pvPortMalloc(size_t xSize);
|
||||
ALIAS("kernel_free") void vPortFree(void* pv);
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed 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 "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void os_log(const char *filename, int line, const char *string) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "%s:%d> %s", filename, line, string);
|
||||
}
|
||||
|
||||
NORETURN os_assertion_failed(const char *filename, int line) {
|
||||
const uintptr_t lr = (uintptr_t) __builtin_return_address(0);
|
||||
PBL_ASSERTN_LR(0, lr);
|
||||
}
|
||||
|
||||
NORETURN os_assertion_failed_lr(const char *filename, int line, uint32_t lr) {
|
||||
PBL_ASSERTN_LR(0, lr);
|
||||
}
|
||||
|
||||
void *os_malloc(size_t size) {
|
||||
return kernel_malloc(size);
|
||||
}
|
||||
|
||||
void *os_malloc_check(size_t size) {
|
||||
return kernel_malloc_check(size);
|
||||
}
|
||||
|
||||
void os_free(void *ptr) {
|
||||
kernel_free(ptr);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue