Skip to content

Commit c8ccefb

Browse files
Add Bluetooth support
Co-authored-by: Peter Harper <[email protected]>
1 parent 0d207f4 commit c8ccefb

34 files changed

+2533
-44
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010
[submodule "lib/mbedtls"]
1111
path = lib/mbedtls
1212
url = https://github.com/Mbed-TLS/mbedtls.git
13+
[submodule "lib/btstack"]
14+
path = lib/btstack
15+
url = https://github.com/bluekitchen/btstack.git

docs/index.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@
6262
* \defgroup networking Networking Libraries
6363
* Functions for implementing networking
6464
* @{
65-
* \defgroup pico_cyw43_driver pico_cyw43_driver
65+
* \defgroup pico_btstack pico_btstack
6666
* \defgroup pico_lwip pico_lwip
67+
* \defgroup pico_cyw43_driver pico_cyw43_driver
6768
* \defgroup pico_cyw43_arch pico_cyw43_arch
6869
* @}
6970
*

lib/btstack

Submodule btstack added at 0d21232

src/rp2_common/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ if (NOT PICO_BARE_METAL)
5858
pico_add_subdirectory(cmsis)
5959
pico_add_subdirectory(tinyusb)
6060
pico_add_subdirectory(pico_stdio_usb)
61-
6261
pico_add_subdirectory(pico_i2c_slave)
6362

63+
# networking libraries - note dependency order is important
6464
pico_add_subdirectory(pico_async_context)
65+
pico_add_subdirectory(pico_btstack)
6566
pico_add_subdirectory(pico_cyw43_driver)
6667
pico_add_subdirectory(pico_lwip)
6768
pico_add_subdirectory(pico_cyw43_arch)

src/rp2_common/pico_async_context/async_context_freertos.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,12 @@ void async_context_freertos_acquire_lock_blocking(async_context_t *self_base) {
168168
self->nesting++;
169169
}
170170

171-
void async_context_freertos_lock_check(async_context_t *self_base) {
171+
void async_context_freertos_lock_check(__unused async_context_t *self_base) {
172+
#ifndef NDEBUG
172173
async_context_freertos_t *self = (async_context_freertos_t *)self_base;
173174
// Lock the other core and stop low_prio_irq running
174175
assert(xSemaphoreGetMutexHolder(self->lock_mutex) == xTaskGetCurrentTaskHandle());
176+
#endif
175177
}
176178

177179
typedef struct sync_func_call{

src/rp2_common/pico_btstack/CMakeLists.txt

+353
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include "pico/btstack_flash_bank.h"
8+
#include "hardware/flash.h"
9+
#include "hardware/sync.h"
10+
#include <string.h>
11+
12+
// Check sizes
13+
static_assert(PICO_FLASH_BANK_TOTAL_SIZE % (FLASH_SECTOR_SIZE * 2) == 0, "PICO_FLASH_BANK_TOTAL_SIZE invalid");
14+
static_assert(PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
15+
static_assert(PICO_FLASH_BANK_STORAGE_OFFSET + PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
16+
17+
// Size of one bank
18+
#define PICO_FLASH_BANK_SIZE (PICO_FLASH_BANK_TOTAL_SIZE / 2)
19+
20+
#if 0
21+
#define DEBUG_PRINT(format,args...) printf(format, ## args)
22+
#else
23+
#define DEBUG_PRINT(...)
24+
#endif
25+
26+
static uint32_t pico_flash_bank_get_size(void * context) {
27+
(void)(context);
28+
return PICO_FLASH_BANK_SIZE;
29+
}
30+
31+
static uint32_t pico_flash_bank_get_alignment(void * context) {
32+
(void)(context);
33+
return 1;
34+
}
35+
36+
static void pico_flash_bank_erase(void * context, int bank) {
37+
(void)(context);
38+
DEBUG_PRINT("erase: bank %d\n", bank);
39+
uint32_t status = save_and_disable_interrupts();
40+
flash_range_erase(PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank), PICO_FLASH_BANK_SIZE);
41+
restore_interrupts(status);
42+
}
43+
44+
static void pico_flash_bank_read(void *context, int bank, uint32_t offset, uint8_t *buffer, uint32_t size) {
45+
(void)(context);
46+
DEBUG_PRINT("read: bank %d offset %u size %u\n", bank, offset, size);
47+
48+
assert(bank <= 1);
49+
if (bank > 1) return;
50+
51+
assert(offset < PICO_FLASH_BANK_SIZE);
52+
if (offset >= PICO_FLASH_BANK_SIZE) return;
53+
54+
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
55+
if ((offset + size) > PICO_FLASH_BANK_SIZE) return;
56+
57+
// Flash is xip
58+
memcpy(buffer, (void *)(XIP_BASE + PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank) + offset), size);
59+
}
60+
61+
static void pico_flash_bank_write(void * context, int bank, uint32_t offset, const uint8_t *data, uint32_t size) {
62+
(void)(context);
63+
DEBUG_PRINT("write: bank %d offset %u size %u\n", bank, offset, size);
64+
65+
assert(bank <= 1);
66+
if (bank > 1) return;
67+
68+
assert(offset < PICO_FLASH_BANK_SIZE);
69+
if (offset >= PICO_FLASH_BANK_SIZE) return;
70+
71+
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
72+
if ((offset + size) > PICO_FLASH_BANK_SIZE) return;
73+
74+
if (size == 0) return;
75+
76+
// calc bank start position
77+
const uint32_t bank_start_pos = PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank);
78+
79+
// Calculate first and last page in the bank
80+
const uint32_t first_page = offset / FLASH_PAGE_SIZE;
81+
const uint32_t last_page = (offset + size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
82+
83+
// Now we only care about the offset in the first page
84+
offset %= FLASH_PAGE_SIZE;
85+
86+
// Amount of data we've copied
87+
uint32_t data_pos = 0;
88+
uint32_t size_left = size;
89+
90+
// Write all the pages required
91+
for(uint32_t page = first_page; page < last_page; page++) {
92+
uint8_t page_data[FLASH_PAGE_SIZE];
93+
94+
assert(data_pos < size && size_left <= size);
95+
96+
// Copy data we're not going to overwrite in the first page
97+
if (page == first_page && offset > 0) {
98+
memcpy(page_data,
99+
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE)),
100+
offset);
101+
}
102+
103+
// Copy the data we're not going to overwrite in the last page
104+
if (page == last_page - 1 && (offset + size_left) < FLASH_PAGE_SIZE) {
105+
memcpy(page_data + offset + size_left,
106+
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE) + offset + size_left),
107+
FLASH_PAGE_SIZE - offset - size_left);
108+
}
109+
110+
// Now copy the new data into the page
111+
const uint32_t size_to_copy = MIN(size_left, FLASH_PAGE_SIZE - offset);
112+
memcpy(page_data + offset, data + data_pos, size_to_copy);
113+
114+
data_pos += size_to_copy;
115+
size_left -= size_to_copy;
116+
117+
// zero offset for the following pages
118+
offset = 0;
119+
120+
// Now program the entire page
121+
uint32_t status = save_and_disable_interrupts();
122+
flash_range_program(bank_start_pos + (page * FLASH_PAGE_SIZE), page_data, FLASH_PAGE_SIZE);
123+
restore_interrupts(status);
124+
}
125+
}
126+
127+
static const hal_flash_bank_t pico_flash_bank_instance_obj = {
128+
/* uint32_t (*get_size)(..) */ &pico_flash_bank_get_size,
129+
/* uint32_t (*get_alignment)(..); */ &pico_flash_bank_get_alignment,
130+
/* void (*erase)(..); */ &pico_flash_bank_erase,
131+
/* void (*read)(..); */ &pico_flash_bank_read,
132+
/* void (*write)(..); */ &pico_flash_bank_write,
133+
};
134+
135+
const hal_flash_bank_t *pico_flash_bank_instance(void) {
136+
137+
#ifndef NDEBUG
138+
// Check we're not overlapping the binary in flash
139+
extern char __flash_binary_end;
140+
assert((uintptr_t)&__flash_binary_end - XIP_BASE <= PICO_FLASH_BANK_STORAGE_OFFSET);
141+
#endif
142+
143+
return &pico_flash_bank_instance_obj;
144+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include "pico/btstack_run_loop_async_context.h"
8+
#include "hardware/sync.h"
9+
10+
static async_context_t *btstack_async_context;
11+
static async_at_time_worker_t btstack_timeout_worker;
12+
static async_when_pending_worker_t btstack_processing_worker;
13+
static void btstack_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
14+
static void btstack_work_pending(async_context_t *context, async_when_pending_worker_t *worker);
15+
static volatile bool run_loop_exit;
16+
17+
static void btstack_run_loop_async_context_init(void) {
18+
btstack_run_loop_base_init();
19+
btstack_timeout_worker.do_work = btstack_timeout_reached;
20+
btstack_processing_worker.do_work = btstack_work_pending;
21+
async_context_add_when_pending_worker(btstack_async_context, &btstack_processing_worker);
22+
}
23+
24+
static void btstack_run_loop_async_context_add_data_source(btstack_data_source_t * data_source) {
25+
async_context_acquire_lock_blocking(btstack_async_context);
26+
btstack_run_loop_base_add_data_source(data_source);
27+
async_context_release_lock(btstack_async_context);
28+
}
29+
30+
static bool btstack_run_loop_async_context_remove_data_source(btstack_data_source_t * data_source) {
31+
async_context_acquire_lock_blocking(btstack_async_context);
32+
bool rc = btstack_run_loop_base_remove_data_source(data_source);
33+
async_context_release_lock(btstack_async_context);
34+
return rc;
35+
}
36+
37+
static void btstack_run_loop_async_context_enable_data_source_callbacks(btstack_data_source_t * data_source, uint16_t callbacks) {
38+
async_context_acquire_lock_blocking(btstack_async_context);
39+
btstack_run_loop_base_enable_data_source_callbacks(data_source, callbacks);
40+
async_context_release_lock(btstack_async_context);
41+
}
42+
43+
static void btstack_run_loop_async_context_disable_data_source_callbacks(btstack_data_source_t * data_source, uint16_t callbacks) {
44+
async_context_acquire_lock_blocking(btstack_async_context);
45+
btstack_run_loop_base_disable_data_source_callbacks(data_source, callbacks);
46+
async_context_release_lock(btstack_async_context);
47+
}
48+
49+
static void btstack_run_loop_async_context_set_timer(btstack_timer_source_t *ts, uint32_t timeout_in_ms){
50+
async_context_acquire_lock_blocking(btstack_async_context);
51+
ts->timeout = to_ms_since_boot(get_absolute_time()) + timeout_in_ms + 1;
52+
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
53+
async_context_release_lock(btstack_async_context);
54+
}
55+
56+
static void btstack_run_loop_async_context_add_timer(btstack_timer_source_t *timer) {
57+
async_context_acquire_lock_blocking(btstack_async_context);
58+
btstack_run_loop_base_add_timer(timer);
59+
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
60+
async_context_release_lock(btstack_async_context);
61+
}
62+
63+
static bool btstack_run_loop_async_context_remove_timer(btstack_timer_source_t *timer) {
64+
async_context_acquire_lock_blocking(btstack_async_context);
65+
bool rc = btstack_run_loop_base_remove_timer(timer);
66+
async_context_release_lock(btstack_async_context);
67+
return rc;
68+
}
69+
70+
static void btstack_run_loop_async_context_dump_timer(void){
71+
async_context_acquire_lock_blocking(btstack_async_context);
72+
btstack_run_loop_base_dump_timer();
73+
async_context_release_lock(btstack_async_context);
74+
}
75+
76+
static uint32_t btstack_run_loop_async_context_get_time_ms(void)
77+
{
78+
return to_ms_since_boot(get_absolute_time());
79+
}
80+
81+
static void btstack_run_loop_async_context_execute(void)
82+
{
83+
run_loop_exit = false;
84+
while (!run_loop_exit) {
85+
async_context_poll(btstack_async_context);
86+
async_context_wait_for_work_until(btstack_async_context, at_the_end_of_time);
87+
}
88+
}
89+
90+
static void btstack_run_loop_async_context_trigger_exit(void)
91+
{
92+
run_loop_exit = true;
93+
}
94+
95+
static void btstack_run_loop_async_context_execute_on_main_thread(btstack_context_callback_registration_t *callback_registration)
96+
{
97+
async_context_acquire_lock_blocking(btstack_async_context);
98+
btstack_run_loop_base_add_callback(callback_registration);
99+
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
100+
async_context_release_lock(btstack_async_context);
101+
}
102+
103+
static void btstack_run_loop_async_context_poll_data_sources_from_irq(void)
104+
{
105+
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
106+
}
107+
108+
static const btstack_run_loop_t btstack_run_loop_async_context = {
109+
&btstack_run_loop_async_context_init,
110+
&btstack_run_loop_async_context_add_data_source,
111+
&btstack_run_loop_async_context_remove_data_source,
112+
&btstack_run_loop_async_context_enable_data_source_callbacks,
113+
&btstack_run_loop_async_context_disable_data_source_callbacks,
114+
&btstack_run_loop_async_context_set_timer,
115+
&btstack_run_loop_async_context_add_timer,
116+
&btstack_run_loop_async_context_remove_timer,
117+
&btstack_run_loop_async_context_execute,
118+
&btstack_run_loop_async_context_dump_timer,
119+
&btstack_run_loop_async_context_get_time_ms,
120+
&btstack_run_loop_async_context_poll_data_sources_from_irq,
121+
&btstack_run_loop_async_context_execute_on_main_thread,
122+
&btstack_run_loop_async_context_trigger_exit,
123+
};
124+
125+
const btstack_run_loop_t *btstack_run_loop_async_context_get_instance(async_context_t *async_context)
126+
{
127+
assert(!btstack_async_context || btstack_async_context == async_context);
128+
btstack_async_context = async_context;
129+
return &btstack_run_loop_async_context;
130+
}
131+
132+
static void btstack_timeout_reached(__unused async_context_t *context, __unused async_at_time_worker_t *worker) {
133+
// simply wakeup worker
134+
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
135+
}
136+
137+
static void btstack_work_pending(__unused async_context_t *context, __unused async_when_pending_worker_t *worker) {
138+
// poll data sources
139+
btstack_run_loop_base_poll_data_sources();
140+
141+
// execute callbacks
142+
btstack_run_loop_base_execute_callbacks();
143+
144+
uint32_t now = to_ms_since_boot(get_absolute_time());
145+
146+
// process timers
147+
btstack_run_loop_base_process_timers(now);
148+
now = to_ms_since_boot(get_absolute_time());
149+
int ms = btstack_run_loop_base_get_time_until_timeout(now);
150+
if (ms == -1) {
151+
async_context_remove_at_time_worker(btstack_async_context, &btstack_timeout_worker);
152+
} else {
153+
async_context_add_at_time_worker_in_ms(btstack_async_context, &btstack_timeout_worker, ms);
154+
}
155+
}

0 commit comments

Comments
 (0)