summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/library/ubluetooth.rst2
-rw-r--r--extmod/btstack/btstack.mk14
-rw-r--r--extmod/btstack/btstack_config.h3
-rw-r--r--ports/unix/Makefile19
-rw-r--r--ports/unix/btstack_usb.c170
-rw-r--r--ports/unix/main.c5
-rw-r--r--ports/unix/mpconfigport.h8
-rw-r--r--ports/unix/mphalport.h8
8 files changed, 225 insertions, 4 deletions
diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst
index 62a823924..2609ac01b 100644
--- a/docs/library/ubluetooth.rst
+++ b/docs/library/ubluetooth.rst
@@ -48,6 +48,8 @@ Configuration
- ``'mac'``: Returns the device MAC address. If a device has a fixed address
(e.g. PYBD) then it will be returned. Otherwise (e.g. ESP32) a random
address will be generated when the BLE interface is made active.
+ Note: on some ports, accessing this value requires that the interface is
+ active (so that the MAC address can be queried from the controller).
- ``'rxbuf'``: Get/set the size in bytes of the internal buffer used to store
incoming events. This buffer is global to the entire BLE driver and so
diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk
index ecb1c84bd..6c883578e 100644
--- a/extmod/btstack/btstack.mk
+++ b/extmod/btstack/btstack.mk
@@ -2,6 +2,8 @@
ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1)
+MICROPY_BLUETOOTH_BTSTACK_USB ?= 0
+
BTSTACK_EXTMOD_DIR = extmod/btstack
EXTMOD_SRC_C += extmod/btstack/modbluetooth_btstack.c
@@ -24,9 +26,17 @@ INC += -I$(BTSTACK_DIR)/3rd-party/yxml
SRC_BTSTACK = \
$(addprefix lib/btstack/src/, $(SRC_FILES)) \
$(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \
- lib/btstack/platform/embedded/btstack_run_loop_embedded.c \
+ lib/btstack/platform/embedded/btstack_run_loop_embedded.c
+
+ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1)
+SRC_BTSTACK += \
+ lib/btstack/platform/libusb/hci_transport_h2_libusb.c
+
+CFLAGS += $(shell pkg-config libusb-1.0 --cflags)
+LDFLAGS += $(shell pkg-config libusb-1.0 --libs)
+endif
-ifeq ($MICROPY_BLUETOOTH_BTSTACK_ENABLE_CLASSIC,1)
+ifeq ($(MICROPY_BLUETOOTH_BTSTACK_ENABLE_CLASSIC),1)
include $(BTSTACK_DIR)/src/classic/Makefile.inc
SRC_BTSTACK += \
$(addprefix lib/btstack/src/classic/, $(SRC_CLASSIC_FILES))
diff --git a/extmod/btstack/btstack_config.h b/extmod/btstack/btstack_config.h
index 0976bbe72..f420f47a5 100644
--- a/extmod/btstack/btstack_config.h
+++ b/extmod/btstack/btstack_config.h
@@ -41,4 +41,7 @@
// BTstack HAL configuration
#define HAVE_EMBEDDED_TIME_MS
+// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
+#define HCI_RESET_RESEND_TIMEOUT_MS 1000
+
#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H
diff --git a/ports/unix/Makefile b/ports/unix/Makefile
index 8cb95d08f..eaaedab15 100644
--- a/ports/unix/Makefile
+++ b/ports/unix/Makefile
@@ -132,6 +132,19 @@ CFLAGS_MOD += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0
LDFLAGS_MOD += $(LIBPTHREAD)
endif
+ifeq ($(MICROPY_PY_BLUETOOTH),1)
+CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1
+CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1
+CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=1
+
+MICROPY_BLUETOOTH_BTSTACK ?= 1
+MICROPY_BLUETOOTH_BTSTACK_USB ?= 1
+endif
+
+ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1)
+include $(TOP)/extmod/btstack/btstack.mk
+endif
+
ifeq ($(MICROPY_PY_FFI),1)
ifeq ($(MICROPY_STANDALONE),1)
@@ -176,10 +189,11 @@ SRC_C = \
alloc.c \
coverage.c \
fatfs_port.c \
+ btstack_usb.c \
$(SRC_MOD) \
$(wildcard $(VARIANT_DIR)/*.c)
-LIB_SRC_C = $(addprefix lib/,\
+LIB_SRC_C += $(addprefix lib/,\
$(LIB_SRC_C_EXTRA) \
timeutils/timeutils.c \
)
@@ -187,9 +201,10 @@ LIB_SRC_C = $(addprefix lib/,\
OBJ = $(PY_O)
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
+OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o))
# List of sources for qstr extraction
-SRC_QSTR += $(SRC_C) $(LIB_SRC_C)
+SRC_QSTR += $(SRC_C) $(LIB_SRC_C) $(EXTMOD_SRC_C)
# Append any auto-generated sources that are needed by sources listed in
# SRC_QSTR
SRC_QSTR_AUTO_DEPS +=
diff --git a/ports/unix/btstack_usb.c b/ports/unix/btstack_usb.c
new file mode 100644
index 000000000..dfd2b91df
--- /dev/null
+++ b/ports/unix/btstack_usb.c
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jim Mussared
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+
+#include "py/runtime.h"
+#include "py/mperrno.h"
+#include "py/mphal.h"
+
+#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
+
+#include "lib/btstack/src/btstack.h"
+#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h"
+
+#include "extmod/btstack/modbluetooth_btstack.h"
+
+#if !MICROPY_PY_THREAD
+#error Unix btstack requires MICROPY_PY_THREAD
+#endif
+
+STATIC const useconds_t USB_POLL_INTERVAL_US = 1000;
+
+STATIC const uint8_t read_static_address_command_complete_prefix[] = { 0x0e, 0x1b, 0x01, 0x09, 0xfc };
+
+STATIC uint8_t local_addr[6] = {0};
+STATIC uint8_t static_address[6] = {0};
+STATIC volatile bool have_addr = false;
+STATIC bool using_static_address = false;
+
+STATIC btstack_packet_callback_registration_t hci_event_callback_registration;
+
+STATIC void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
+ (void)channel;
+ (void)size;
+ if (packet_type != HCI_EVENT_PACKET) {
+ return;
+ }
+ switch (hci_event_packet_get_type(packet)) {
+ case BTSTACK_EVENT_STATE:
+ if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) {
+ return;
+ }
+ gap_local_bd_addr(local_addr);
+ if (using_static_address) {
+ memcpy(local_addr, static_address, sizeof(local_addr));
+ }
+ have_addr = true;
+ break;
+ case HCI_EVENT_COMMAND_COMPLETE:
+ if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) {
+ reverse_48(&packet[7], static_address);
+ gap_random_address_set(static_address);
+ using_static_address = true;
+ have_addr = true;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the
+// following three functions are empty.
+
+void hal_cpu_disable_irqs(void) {
+}
+
+void hal_cpu_enable_irqs(void) {
+}
+
+void hal_cpu_enable_irqs_and_sleep(void) {
+}
+
+uint32_t hal_time_ms(void) {
+ return mp_hal_ticks_ms();
+}
+
+void mp_bluetooth_btstack_port_init(void) {
+ static bool run_loop_init = false;
+ if (!run_loop_init) {
+ run_loop_init = true;
+ btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
+ } else {
+ btstack_run_loop_embedded_get_instance()->init();
+ }
+
+ // TODO: allow setting USB device path via cmdline/env var.
+
+ // hci_dump_open(NULL, HCI_DUMP_STDOUT);
+ hci_init(hci_transport_usb_instance(), NULL);
+
+ hci_event_callback_registration.callback = &packet_handler;
+ hci_add_event_handler(&hci_event_callback_registration);
+}
+
+STATIC pthread_t bstack_thread_id;
+
+void mp_bluetooth_btstack_port_deinit(void) {
+ hci_power_control(HCI_POWER_OFF);
+
+ // Wait for the poll loop to terminate when the state is set to OFF.
+ pthread_join(bstack_thread_id, NULL);
+ have_addr = false;
+}
+
+STATIC void *btstack_thread(void *arg) {
+ (void)arg;
+ 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.
+
+ while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) {
+ btstack_run_loop_embedded_execute_once();
+
+ // 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);
+ }
+
+ hci_close();
+
+ return NULL;
+}
+
+void mp_bluetooth_btstack_port_start(void) {
+ // Create a thread to run the btstack loop.
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_create(&bstack_thread_id, &attr, &btstack_thread, NULL);
+}
+
+void mp_hal_get_mac(int idx, uint8_t buf[6]) {
+ if (idx == MP_HAL_MAC_BDADDR) {
+ if (!have_addr) {
+ mp_raise_OSError(MP_ENODEV);
+ }
+ memcpy(buf, local_addr, sizeof(local_addr));
+ }
+}
+
+#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
diff --git a/ports/unix/main.c b/ports/unix/main.c
index 34847a6ff..5251fe8ae 100644
--- a/ports/unix/main.c
+++ b/ports/unix/main.c
@@ -683,6 +683,11 @@ MP_NOINLINE int main_(int argc, char **argv) {
}
#endif
+ #if MICROPY_PY_BLUETOOTH
+ void mp_bluetooth_deinit(void);
+ mp_bluetooth_deinit();
+ #endif
+
#if MICROPY_PY_THREAD
mp_thread_deinit();
#endif
diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h
index b23b6ce47..5708ceab6 100644
--- a/ports/unix/mpconfigport.h
+++ b/ports/unix/mpconfigport.h
@@ -311,9 +311,17 @@ void mp_unix_mark_exec(void);
#define MP_STATE_PORT MP_STATE_VM
+#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
+struct _mp_bluetooth_btstack_root_pointers_t;
+#define MICROPY_BLUETOOTH_ROOT_POINTERS struct _mp_bluetooth_btstack_root_pointers_t *bluetooth_btstack_root_pointers;
+#else
+#define MICROPY_BLUETOOTH_ROOT_POINTERS
+#endif
+
#define MICROPY_PORT_ROOT_POINTERS \
const char *readline_hist[50]; \
void *mmap_region_head; \
+ MICROPY_BLUETOOTH_ROOT_POINTERS \
// We need to provide a declaration/definition of alloca()
// unless support for it is disabled.
diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h
index 185a2d76f..95ad63221 100644
--- a/ports/unix/mphalport.h
+++ b/ports/unix/mphalport.h
@@ -95,3 +95,11 @@ static inline void mp_hal_delay_us(mp_uint_t us) {
#define RAISE_ERRNO(err_flag, error_val) \
{ if (err_flag == -1) \
{ mp_raise_OSError(error_val); } }
+
+#if MICROPY_PY_BLUETOOTH
+enum {
+ MP_HAL_MAC_BDADDR,
+};
+
+void mp_hal_get_mac(int idx, uint8_t buf[6]);
+#endif