diff --git a/ports/unix/Makefile b/ports/unix/Makefile index f02e6c6355ab6..ec64e730cb84b 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -33,6 +33,37 @@ QSTR_GLOBAL_DEPENDENCIES += $(VARIANT_DIR)/mpconfigvariant.h # OS name, for simple autoconfig UNAME_S := $(shell uname -s) +# If the variant enables it, enable modbluetooth. +ifeq ($(MICROPY_PY_BLUETOOTH),1) +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +HAVE_LIBUSB := $(shell (which pkg-config > /dev/null && pkg-config --exists libusb-1.0) 2>/dev/null && echo '1') + +# Figure out which BTstack transport to use. +ifeq ($(HAVE_LIBUSB),1) +# Default to btstack-over-usb. +MICROPY_BLUETOOTH_BTSTACK_USB ?= 1 +MICROPY_BLUETOOTH_BTSTACK_H4 ?= 0 +else +# Fallback to HCI controller via a H4 UART (e.g. Zephyr on nRF) over a /dev/tty serial port. +MICROPY_BLUETOOTH_BTSTACK_USB ?= 0 +MICROPY_BLUETOOTH_BTSTACK_H4 ?= 1 +endif + +# MICROPY_BLUETOOTH_BTSTACK_HCI_DUMP could be set to 1 to activate logging +# to file "/tmp/hci_dump.pklg" for btstack-over-usb. +ifndef MICROPY_BLUETOOTH_BTSTACK_HCI_DUMP +MICROPY_BLUETOOTH_BTSTACK_HCI_DUMP ?= 0 +endif + +# Set the file that will be included by "extmod/btstack/btstack_config.h" +# +# The original file "extmod/btstack/btstack_config_common.h" +# will be included by it. +MICROPY_BLUETOOTH_BTSTACK_CONFIG_FILE ?= '"ports/unix/btstack_config.h"' + +endif # MICROPY_BLUETOOTH_BTSTACK +endif # MICROPY_PY_BLUETOOTH + # include py core make definitions include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk @@ -144,21 +175,23 @@ ifeq ($(MICROPY_SSL_AXTLS),1) endif endif -# If the variant enables it, enable modbluetooth. ifeq ($(MICROPY_PY_BLUETOOTH),1) ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) -HAVE_LIBUSB := $(shell (which pkg-config > /dev/null && pkg-config --exists libusb-1.0) 2>/dev/null && echo '1') -# Figure out which BTstack transport to use. -ifeq ($(HAVE_LIBUSB),1) -# Default to btstack-over-usb. -MICROPY_BLUETOOTH_BTSTACK_USB ?= 1 -else -# Fallback to HCI controller via a H4 UART (e.g. Zephyr on nRF) over a /dev/tty serial port. -MICROPY_BLUETOOTH_BTSTACK_H4 ?= 1 +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),0) +SRC_BTSTACK_C += lib/btstack/platform/embedded/btstack_run_loop_embedded.c +endif + +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) +SRC_BTSTACK_C += lib/btstack/platform/posix/btstack_run_loop_posix.c + +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_HCI_DUMP),1) +CFLAGS += -DMICROPY_BLUETOOTH_BTSTACK_HCI_DUMP=1 +SRC_BTSTACK_C += lib/btstack/platform/posix/hci_dump_posix_fs.c +endif + endif -SRC_BTSTACK_C += lib/btstack/platform/embedded/btstack_run_loop_embedded.c endif endif diff --git a/ports/unix/btstack_config.h b/ports/unix/btstack_config.h new file mode 100644 index 0000000000000..374acc02e95b1 --- /dev/null +++ b/ports/unix/btstack_config.h @@ -0,0 +1,23 @@ +#ifndef MICROPY_INCLUDED_PORTS_UNIX_BTSTACK_CONFIG_H +#define MICROPY_INCLUDED_PORTS_UNIX_BTSTACK_CONFIG_H + + +#include "extmod/btstack/btstack_config_common.h" + +#ifdef MICROPY_BLUETOOTH_BTSTACK_USB +#if MICROPY_BLUETOOTH_BTSTACK_USB + +// Workaround: +// +// If ENABLE_SCO_OVER_HCI is not defined there is a compiler error: +// +// ../../lib/btstack/platform/libusb/hci_transport_h2_libusb.c:1354:13: +// error: ‘signal_sco_can_send_now’ defined but not used +// +// Because the problem is in an external code this workaround is done. +#define ENABLE_SCO_OVER_HCI + +#endif +#endif + +#endif // MICROPY_INCLUDED_PORTS_UNIX_BTSTACK_CONFIG_H diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c index 36412e96f4e0f..6cf9d6562fea5 100644 --- a/ports/unix/mpbtstackport_common.c +++ b/ports/unix/mpbtstackport_common.c @@ -41,7 +41,7 @@ #include "mpbtstackport.h" -// Called by the UART polling thread in mpbthciport.c, or by the USB polling thread in mpbtstackport_usb.c. +// Called by the UART polling thread in mpbthciport.c bool mp_bluetooth_hci_poll(void) { if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_HALTING) { // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). @@ -80,9 +80,11 @@ uint32_t hal_time_ms(void) { } void mp_bluetooth_btstack_port_init(void) { + #if !MICROPY_BLUETOOTH_BTSTACK_USB btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); // hci_dump_init(hci_dump_embedded_stdout_get_instance()); + #endif #if MICROPY_BLUETOOTH_BTSTACK_H4 mp_bluetooth_btstack_port_init_h4(); diff --git a/ports/unix/mpbtstackport_usb.c b/ports/unix/mpbtstackport_usb.c index a924fc3ca912a..0388d68911e26 100644 --- a/ports/unix/mpbtstackport_usb.c +++ b/ports/unix/mpbtstackport_usb.c @@ -34,12 +34,12 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" +#include "py/stackctrl.h" #include "lib/btstack/src/btstack.h" #include "lib/btstack/src/hci_transport_usb.h" -#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" -#include "lib/btstack/platform/embedded/hal_cpu.h" -#include "lib/btstack/platform/embedded/hal_time_ms.h" +#include "lib/btstack/platform/posix/btstack_run_loop_posix.h" +#include "lib/btstack/platform/posix/hci_dump_posix_fs.h" #include "extmod/btstack/modbluetooth_btstack.h" @@ -49,9 +49,21 @@ #error Unix btstack requires MICROPY_PY_THREAD #endif -static const useconds_t USB_POLL_INTERVAL_US = 1000; +#define DEBUG_printf(...) // printf("mpbtstackport_usb.c: " __VA_ARGS__) void mp_bluetooth_btstack_port_init_usb(void) { + + #if MICROPY_BLUETOOTH_BTSTACK_HCI_DUMP + // log into file using HCI_DUMP_PACKETLOGGER format + const char *pklg_path = "/tmp/hci_dump.pklg"; + hci_dump_posix_fs_open(pklg_path, HCI_DUMP_PACKETLOGGER); + const hci_dump_t *hci_dump_impl = hci_dump_posix_fs_get_instance(); + hci_dump_init(hci_dump_impl); + printf("Packet Log: %s\n", pklg_path); + #endif + + btstack_run_loop_init(btstack_run_loop_posix_get_instance()); + // MICROPYBTUSB can be a ':'' or '-' separated port list. char *path = getenv("MICROPYBTUSB"); if (path != NULL) { @@ -76,35 +88,77 @@ void mp_bluetooth_btstack_port_init_usb(void) { static pthread_t bstack_thread_id; void mp_bluetooth_btstack_port_deinit(void) { - hci_power_control(HCI_POWER_OFF); + DEBUG_printf("mp_bluetooth_btstack_port_deinit: begin: mp_bluetooth_btstack_state=%d\n", mp_bluetooth_btstack_state); + + // Workaround: + // + // Possibly hci_power_control(HCI_POWER_OFF); + // crashes with memory fault if state is already MP_BLUETOOTH_BTSTACK_STATE_OFF. + if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_OFF && + mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT) { - // Wait for the poll loop to terminate when the state is set to OFF. + hci_power_control(HCI_POWER_OFF); + + DEBUG_printf("mp_bluetooth_btstack_port_deinit: after HCI_POWER_OFF: mp_bluetooth_btstack_state=%d\n", mp_bluetooth_btstack_state); + } + + // btstack_run_loop_trigger_exit() will call btstack_run_loop_t::trigger_exit() + // i.e. btstack_run_loop_posix_trigger_exit(). + // And that will trigger the exit of btstack_run_loop_execute() + // i.e. btstack_run_loop_posix_execute(). + btstack_run_loop_trigger_exit(); + + // A call of btstack_run_loop_poll_data_sources_from_irq() is needed + // additional to btstack_run_loop_trigger_exit() to exit the posix run loop. + // + // Otherwise the posix run loop will wait forever for ready File Descriptors. + // + // Hint: + // btstack_run_loop_poll_data_sources_from_irq() + // calls btstack_run_loop_posix_poll_data_sources_from_irq() + btstack_run_loop_poll_data_sources_from_irq(); + + // MicroPython threads are created with PTHREAD_CREATE_DETACHED. + // + // Nonetheless the thread is created with PTHREAD_CREATE_JOINABLE and + // pthread_join() is used. + + DEBUG_printf("mp_bluetooth_btstack_port_deinit: pthread_join()\n"); pthread_join(bstack_thread_id, NULL); -} + #if MICROPY_BLUETOOTH_BTSTACK_HCI_DUMP + DEBUG_printf("mp_bluetooth_btstack_port_deinit: hci_dump_posix_fs_close()\n"); + hci_dump_posix_fs_close(); + #endif -// Provided by mpbstackport_common.c. -extern bool mp_bluetooth_hci_poll(void); + DEBUG_printf("mp_bluetooth_btstack_port_deinit: end\n"); +} static void *btstack_thread(void *arg) { (void)arg; + + // This code runs on an non-MicroPython thread. + // But some of the code e.g. in "extmod/btstack/modbluetooth_btstack.c" + // make calls that needs the state of a MicroPython thread e. g. "m_del()". + // + // So set up relevant MicroPython state and obtain the GIL, + // to synchronised with the rest of the runtime. + + mp_state_thread_t ts; + mp_thread_init_state(&ts, 40000 * (sizeof(void *) / 4) - 1024, NULL, NULL); + MP_THREAD_GIL_ENTER(); + + log_info("btstack_thread: HCI_POWER_ON"); hci_power_control(HCI_POWER_ON); - // modbluetooth_btstack.c will have set the state to STARTING before - // calling mp_bluetooth_btstack_port_start. - // This loop will terminate when the HCI_POWER_OFF above results - // in modbluetooth_btstack.c setting the state back to OFF. - // Or, if a timeout results in it being set to TIMEOUT. + btstack_run_loop_execute(); - while (true) { - if (!mp_bluetooth_hci_poll()) { - break; - } + log_info("btstack_thread: end"); + + // Give back the GIL and reset the MicroPython state. + MP_THREAD_GIL_EXIT(); + mp_thread_set_state(NULL); - // The USB transport schedules events to the run loop at 1ms intervals, - // and the implementation currently polls rather than selects. - usleep(USB_POLL_INTERVAL_US); - } return NULL; }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: