Skip to content

Commit

Permalink
Add Bluetooth support
Browse files Browse the repository at this point in the history
Co-authored-by: Peter Harper <77111776+peterharperuk@users.noreply.github.com>
  • Loading branch information
kilograham and peterharperuk committed Feb 11, 2023
1 parent 0d207f4 commit c8ccefb
Show file tree
Hide file tree
Showing 34 changed files with 2,533 additions and 44 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Expand Up @@ -10,3 +10,6 @@
[submodule "lib/mbedtls"]
path = lib/mbedtls
url = https://github.com/Mbed-TLS/mbedtls.git
[submodule "lib/btstack"]
path = lib/btstack
url = https://github.com/bluekitchen/btstack.git
3 changes: 2 additions & 1 deletion docs/index.h
Expand Up @@ -62,8 +62,9 @@
* \defgroup networking Networking Libraries
* Functions for implementing networking
* @{
* \defgroup pico_cyw43_driver pico_cyw43_driver
* \defgroup pico_btstack pico_btstack
* \defgroup pico_lwip pico_lwip
* \defgroup pico_cyw43_driver pico_cyw43_driver
* \defgroup pico_cyw43_arch pico_cyw43_arch
* @}
*
Expand Down
1 change: 1 addition & 0 deletions lib/btstack
Submodule btstack added at 0d2123
3 changes: 2 additions & 1 deletion src/rp2_common/CMakeLists.txt
Expand Up @@ -58,10 +58,11 @@ if (NOT PICO_BARE_METAL)
pico_add_subdirectory(cmsis)
pico_add_subdirectory(tinyusb)
pico_add_subdirectory(pico_stdio_usb)

pico_add_subdirectory(pico_i2c_slave)

# networking libraries - note dependency order is important
pico_add_subdirectory(pico_async_context)
pico_add_subdirectory(pico_btstack)
pico_add_subdirectory(pico_cyw43_driver)
pico_add_subdirectory(pico_lwip)
pico_add_subdirectory(pico_cyw43_arch)
Expand Down
4 changes: 3 additions & 1 deletion src/rp2_common/pico_async_context/async_context_freertos.c
Expand Up @@ -168,10 +168,12 @@ void async_context_freertos_acquire_lock_blocking(async_context_t *self_base) {
self->nesting++;
}

void async_context_freertos_lock_check(async_context_t *self_base) {
void async_context_freertos_lock_check(__unused async_context_t *self_base) {
#ifndef NDEBUG
async_context_freertos_t *self = (async_context_freertos_t *)self_base;
// Lock the other core and stop low_prio_irq running
assert(xSemaphoreGetMutexHolder(self->lock_mutex) == xTaskGetCurrentTaskHandle());
#endif
}

typedef struct sync_func_call{
Expand Down
353 changes: 353 additions & 0 deletions src/rp2_common/pico_btstack/CMakeLists.txt

Large diffs are not rendered by default.

144 changes: 144 additions & 0 deletions src/rp2_common/pico_btstack/btstack_flash_bank.c
@@ -0,0 +1,144 @@
/*
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "pico/btstack_flash_bank.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include <string.h>

// Check sizes
static_assert(PICO_FLASH_BANK_TOTAL_SIZE % (FLASH_SECTOR_SIZE * 2) == 0, "PICO_FLASH_BANK_TOTAL_SIZE invalid");
static_assert(PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
static_assert(PICO_FLASH_BANK_STORAGE_OFFSET + PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");

// Size of one bank
#define PICO_FLASH_BANK_SIZE (PICO_FLASH_BANK_TOTAL_SIZE / 2)

#if 0
#define DEBUG_PRINT(format,args...) printf(format, ## args)
#else
#define DEBUG_PRINT(...)
#endif

static uint32_t pico_flash_bank_get_size(void * context) {
(void)(context);
return PICO_FLASH_BANK_SIZE;
}

static uint32_t pico_flash_bank_get_alignment(void * context) {
(void)(context);
return 1;
}

static void pico_flash_bank_erase(void * context, int bank) {
(void)(context);
DEBUG_PRINT("erase: bank %d\n", bank);
uint32_t status = save_and_disable_interrupts();
flash_range_erase(PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank), PICO_FLASH_BANK_SIZE);
restore_interrupts(status);
}

static void pico_flash_bank_read(void *context, int bank, uint32_t offset, uint8_t *buffer, uint32_t size) {
(void)(context);
DEBUG_PRINT("read: bank %d offset %u size %u\n", bank, offset, size);

assert(bank <= 1);
if (bank > 1) return;

assert(offset < PICO_FLASH_BANK_SIZE);
if (offset >= PICO_FLASH_BANK_SIZE) return;

assert((offset + size) <= PICO_FLASH_BANK_SIZE);
if ((offset + size) > PICO_FLASH_BANK_SIZE) return;

// Flash is xip
memcpy(buffer, (void *)(XIP_BASE + PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank) + offset), size);
}

static void pico_flash_bank_write(void * context, int bank, uint32_t offset, const uint8_t *data, uint32_t size) {
(void)(context);
DEBUG_PRINT("write: bank %d offset %u size %u\n", bank, offset, size);

assert(bank <= 1);
if (bank > 1) return;

assert(offset < PICO_FLASH_BANK_SIZE);
if (offset >= PICO_FLASH_BANK_SIZE) return;

assert((offset + size) <= PICO_FLASH_BANK_SIZE);
if ((offset + size) > PICO_FLASH_BANK_SIZE) return;

if (size == 0) return;

// calc bank start position
const uint32_t bank_start_pos = PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank);

// Calculate first and last page in the bank
const uint32_t first_page = offset / FLASH_PAGE_SIZE;
const uint32_t last_page = (offset + size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;

// Now we only care about the offset in the first page
offset %= FLASH_PAGE_SIZE;

// Amount of data we've copied
uint32_t data_pos = 0;
uint32_t size_left = size;

// Write all the pages required
for(uint32_t page = first_page; page < last_page; page++) {
uint8_t page_data[FLASH_PAGE_SIZE];

assert(data_pos < size && size_left <= size);

// Copy data we're not going to overwrite in the first page
if (page == first_page && offset > 0) {
memcpy(page_data,
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE)),
offset);
}

// Copy the data we're not going to overwrite in the last page
if (page == last_page - 1 && (offset + size_left) < FLASH_PAGE_SIZE) {
memcpy(page_data + offset + size_left,
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE) + offset + size_left),
FLASH_PAGE_SIZE - offset - size_left);
}

// Now copy the new data into the page
const uint32_t size_to_copy = MIN(size_left, FLASH_PAGE_SIZE - offset);
memcpy(page_data + offset, data + data_pos, size_to_copy);

data_pos += size_to_copy;
size_left -= size_to_copy;

// zero offset for the following pages
offset = 0;

// Now program the entire page
uint32_t status = save_and_disable_interrupts();
flash_range_program(bank_start_pos + (page * FLASH_PAGE_SIZE), page_data, FLASH_PAGE_SIZE);
restore_interrupts(status);
}
}

static const hal_flash_bank_t pico_flash_bank_instance_obj = {
/* uint32_t (*get_size)(..) */ &pico_flash_bank_get_size,
/* uint32_t (*get_alignment)(..); */ &pico_flash_bank_get_alignment,
/* void (*erase)(..); */ &pico_flash_bank_erase,
/* void (*read)(..); */ &pico_flash_bank_read,
/* void (*write)(..); */ &pico_flash_bank_write,
};

const hal_flash_bank_t *pico_flash_bank_instance(void) {

#ifndef NDEBUG
// Check we're not overlapping the binary in flash
extern char __flash_binary_end;
assert((uintptr_t)&__flash_binary_end - XIP_BASE <= PICO_FLASH_BANK_STORAGE_OFFSET);
#endif

return &pico_flash_bank_instance_obj;
}
155 changes: 155 additions & 0 deletions src/rp2_common/pico_btstack/btstack_run_loop_async_context.c
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "pico/btstack_run_loop_async_context.h"
#include "hardware/sync.h"

static async_context_t *btstack_async_context;
static async_at_time_worker_t btstack_timeout_worker;
static async_when_pending_worker_t btstack_processing_worker;
static void btstack_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
static void btstack_work_pending(async_context_t *context, async_when_pending_worker_t *worker);
static volatile bool run_loop_exit;

static void btstack_run_loop_async_context_init(void) {
btstack_run_loop_base_init();
btstack_timeout_worker.do_work = btstack_timeout_reached;
btstack_processing_worker.do_work = btstack_work_pending;
async_context_add_when_pending_worker(btstack_async_context, &btstack_processing_worker);
}

static void btstack_run_loop_async_context_add_data_source(btstack_data_source_t * data_source) {
async_context_acquire_lock_blocking(btstack_async_context);
btstack_run_loop_base_add_data_source(data_source);
async_context_release_lock(btstack_async_context);
}

static bool btstack_run_loop_async_context_remove_data_source(btstack_data_source_t * data_source) {
async_context_acquire_lock_blocking(btstack_async_context);
bool rc = btstack_run_loop_base_remove_data_source(data_source);
async_context_release_lock(btstack_async_context);
return rc;
}

static void btstack_run_loop_async_context_enable_data_source_callbacks(btstack_data_source_t * data_source, uint16_t callbacks) {
async_context_acquire_lock_blocking(btstack_async_context);
btstack_run_loop_base_enable_data_source_callbacks(data_source, callbacks);
async_context_release_lock(btstack_async_context);
}

static void btstack_run_loop_async_context_disable_data_source_callbacks(btstack_data_source_t * data_source, uint16_t callbacks) {
async_context_acquire_lock_blocking(btstack_async_context);
btstack_run_loop_base_disable_data_source_callbacks(data_source, callbacks);
async_context_release_lock(btstack_async_context);
}

static void btstack_run_loop_async_context_set_timer(btstack_timer_source_t *ts, uint32_t timeout_in_ms){
async_context_acquire_lock_blocking(btstack_async_context);
ts->timeout = to_ms_since_boot(get_absolute_time()) + timeout_in_ms + 1;
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
async_context_release_lock(btstack_async_context);
}

static void btstack_run_loop_async_context_add_timer(btstack_timer_source_t *timer) {
async_context_acquire_lock_blocking(btstack_async_context);
btstack_run_loop_base_add_timer(timer);
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
async_context_release_lock(btstack_async_context);
}

static bool btstack_run_loop_async_context_remove_timer(btstack_timer_source_t *timer) {
async_context_acquire_lock_blocking(btstack_async_context);
bool rc = btstack_run_loop_base_remove_timer(timer);
async_context_release_lock(btstack_async_context);
return rc;
}

static void btstack_run_loop_async_context_dump_timer(void){
async_context_acquire_lock_blocking(btstack_async_context);
btstack_run_loop_base_dump_timer();
async_context_release_lock(btstack_async_context);
}

static uint32_t btstack_run_loop_async_context_get_time_ms(void)
{
return to_ms_since_boot(get_absolute_time());
}

static void btstack_run_loop_async_context_execute(void)
{
run_loop_exit = false;
while (!run_loop_exit) {
async_context_poll(btstack_async_context);
async_context_wait_for_work_until(btstack_async_context, at_the_end_of_time);
}
}

static void btstack_run_loop_async_context_trigger_exit(void)
{
run_loop_exit = true;
}

static void btstack_run_loop_async_context_execute_on_main_thread(btstack_context_callback_registration_t *callback_registration)
{
async_context_acquire_lock_blocking(btstack_async_context);
btstack_run_loop_base_add_callback(callback_registration);
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
async_context_release_lock(btstack_async_context);
}

static void btstack_run_loop_async_context_poll_data_sources_from_irq(void)
{
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
}

static const btstack_run_loop_t btstack_run_loop_async_context = {
&btstack_run_loop_async_context_init,
&btstack_run_loop_async_context_add_data_source,
&btstack_run_loop_async_context_remove_data_source,
&btstack_run_loop_async_context_enable_data_source_callbacks,
&btstack_run_loop_async_context_disable_data_source_callbacks,
&btstack_run_loop_async_context_set_timer,
&btstack_run_loop_async_context_add_timer,
&btstack_run_loop_async_context_remove_timer,
&btstack_run_loop_async_context_execute,
&btstack_run_loop_async_context_dump_timer,
&btstack_run_loop_async_context_get_time_ms,
&btstack_run_loop_async_context_poll_data_sources_from_irq,
&btstack_run_loop_async_context_execute_on_main_thread,
&btstack_run_loop_async_context_trigger_exit,
};

const btstack_run_loop_t *btstack_run_loop_async_context_get_instance(async_context_t *async_context)
{
assert(!btstack_async_context || btstack_async_context == async_context);
btstack_async_context = async_context;
return &btstack_run_loop_async_context;
}

static void btstack_timeout_reached(__unused async_context_t *context, __unused async_at_time_worker_t *worker) {
// simply wakeup worker
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
}

static void btstack_work_pending(__unused async_context_t *context, __unused async_when_pending_worker_t *worker) {
// poll data sources
btstack_run_loop_base_poll_data_sources();

// execute callbacks
btstack_run_loop_base_execute_callbacks();

uint32_t now = to_ms_since_boot(get_absolute_time());

// process timers
btstack_run_loop_base_process_timers(now);
now = to_ms_since_boot(get_absolute_time());
int ms = btstack_run_loop_base_get_time_until_timeout(now);
if (ms == -1) {
async_context_remove_at_time_worker(btstack_async_context, &btstack_timeout_worker);
} else {
async_context_add_at_time_worker_in_ms(btstack_async_context, &btstack_timeout_worker, ms);
}
}

1 comment on commit c8ccefb

@mhbkamaei
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking forward to stable release ❤️

Please sign in to comment.