summaryrefslogtreecommitdiffstatshomepage
path: root/extmod
diff options
context:
space:
mode:
Diffstat (limited to 'extmod')
-rw-r--r--extmod/btstack/modbluetooth_btstack.c7
-rw-r--r--extmod/modbluetooth.c7
-rw-r--r--extmod/modbluetooth.h2
-rw-r--r--extmod/modframebuf.c57
-rw-r--r--extmod/modlwip.c140
-rw-r--r--extmod/nimble/modbluetooth_nimble.c21
-rw-r--r--extmod/nimble/modbluetooth_nimble.h2
7 files changed, 128 insertions, 108 deletions
diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c
index b29970842c..7694a1874f 100644
--- a/extmod/btstack/modbluetooth_btstack.c
+++ b/extmod/btstack/modbluetooth_btstack.c
@@ -705,12 +705,12 @@ int mp_bluetooth_init(void) {
return 0;
}
-void mp_bluetooth_deinit(void) {
+int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit\n");
// Nothing to do if not initialised.
if (!MP_STATE_PORT(bluetooth_btstack_root_pointers)) {
- return;
+ return 0;
}
mp_bluetooth_gap_advertise_stop();
@@ -737,6 +737,9 @@ void mp_bluetooth_deinit(void) {
deinit_stack();
DEBUG_printf("mp_bluetooth_deinit: complete\n");
+
+ bool timeout = mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT;
+ return timeout ? MP_ETIMEDOUT : 0;
}
bool mp_bluetooth_is_active(void) {
diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c
index b95c42a4ee..ffa407809a 100644
--- a/extmod/modbluetooth.c
+++ b/extmod/modbluetooth.c
@@ -290,12 +290,13 @@ static mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args,
static mp_obj_t bluetooth_ble_active(size_t n_args, const mp_obj_t *args) {
if (n_args == 2) {
// Boolean enable/disable argument supplied, set current state.
+ int err;
if (mp_obj_is_true(args[1])) {
- int err = mp_bluetooth_init();
- bluetooth_handle_errno(err);
+ err = mp_bluetooth_init();
} else {
- mp_bluetooth_deinit();
+ err = mp_bluetooth_deinit();
}
+ bluetooth_handle_errno(err);
}
// Return current state.
return mp_obj_new_bool(mp_bluetooth_is_active());
diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h
index 6a087c8e25..24f063fa5d 100644
--- a/extmod/modbluetooth.h
+++ b/extmod/modbluetooth.h
@@ -295,7 +295,7 @@ extern const mp_obj_type_t mp_type_bluetooth_uuid;
int mp_bluetooth_init(void);
// Disables the Bluetooth stack. Is a no-op when not enabled.
-void mp_bluetooth_deinit(void);
+int mp_bluetooth_deinit(void);
// Returns true when the Bluetooth stack is active.
bool mp_bluetooth_is_active(void);
diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c
index b718a66cc6..5c4b9abf0c 100644
--- a/extmod/modframebuf.c
+++ b/extmod/modframebuf.c
@@ -270,8 +270,7 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u
formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col);
}
-static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
- mp_arg_check_num(n_args, n_kw, 4, 5, false);
+static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) {
mp_int_t width = mp_obj_get_int(args_in[1]);
mp_int_t height = mp_obj_get_int(args_in[2]);
@@ -318,13 +317,15 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
}
mp_buffer_info_t bufinfo;
- mp_get_buffer_raise(args_in[0], &bufinfo, MP_BUFFER_WRITE);
+ mp_get_buffer_raise(args_in[0], &bufinfo, buf_flags);
if ((strides_required * stride + (height_required - strides_required) * width_required) * bpp / 8 > bufinfo.len) {
mp_raise_ValueError(NULL);
}
- mp_obj_framebuf_t *o = mp_obj_malloc(mp_obj_framebuf_t, type);
+ if (o == NULL) {
+ o = mp_obj_malloc(mp_obj_framebuf_t, (const mp_obj_type_t *)&mp_type_framebuf);
+ }
o->buf_obj = args_in[0];
o->buf = bufinfo.buf;
o->width = width;
@@ -335,6 +336,11 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
return MP_OBJ_FROM_PTR(o);
}
+static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
+ mp_arg_check_num(n_args, n_kw, 4, 5, false);
+ return framebuf_make_new_helper(n_args, args_in, MP_BUFFER_WRITE, NULL);
+}
+
static void framebuf_args(const mp_obj_t *args_in, mp_int_t *args_out, int n) {
for (int i = 0; i < n; ++i) {
args_out[i] = mp_obj_get_int(args_in[i + 1]);
@@ -707,13 +713,27 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 6, framebuf_pol
#endif // MICROPY_PY_ARRAY
+static void get_readonly_framebuffer(mp_obj_t arg, mp_obj_framebuf_t *rofb) {
+ mp_obj_t fb = mp_obj_cast_to_native_base(arg, MP_OBJ_FROM_PTR(&mp_type_framebuf));
+ if (fb != MP_OBJ_NULL) {
+ *rofb = *(mp_obj_framebuf_t *)MP_OBJ_TO_PTR(fb);
+ } else {
+ // A tuple/list of the form: (buffer, width, height, format[, stride]).
+ size_t len;
+ mp_obj_t *items;
+ mp_obj_get_array(arg, &len, &items);
+ if (len < 4 || len > 5) {
+ mp_raise_ValueError(NULL);
+ }
+ framebuf_make_new_helper(len, items, MP_BUFFER_READ, rofb);
+ }
+}
+
static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
- mp_obj_t source_in = mp_obj_cast_to_native_base(args_in[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
- if (source_in == MP_OBJ_NULL) {
- mp_raise_TypeError(NULL);
- }
- mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in);
+
+ mp_obj_framebuf_t source;
+ get_readonly_framebuffer(args_in[1], &source);
mp_int_t x = mp_obj_get_int(args_in[2]);
mp_int_t y = mp_obj_get_int(args_in[3]);
@@ -721,16 +741,17 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
if (n_args > 4) {
key = mp_obj_get_int(args_in[4]);
}
- mp_obj_framebuf_t *palette = NULL;
+ mp_obj_framebuf_t palette;
+ palette.buf = NULL;
if (n_args > 5 && args_in[5] != mp_const_none) {
- palette = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(args_in[5], MP_OBJ_FROM_PTR(&mp_type_framebuf)));
+ get_readonly_framebuffer(args_in[5], &palette);
}
if (
(x >= self->width) ||
(y >= self->height) ||
- (-x >= source->width) ||
- (-y >= source->height)
+ (-x >= source.width) ||
+ (-y >= source.height)
) {
// Out of bounds, no-op.
return mp_const_none;
@@ -741,15 +762,15 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
int y0 = MAX(0, y);
int x1 = MAX(0, -x);
int y1 = MAX(0, -y);
- int x0end = MIN(self->width, x + source->width);
- int y0end = MIN(self->height, y + source->height);
+ int x0end = MIN(self->width, x + source.width);
+ int y0end = MIN(self->height, y + source.height);
for (; y0 < y0end; ++y0) {
int cx1 = x1;
for (int cx0 = x0; cx0 < x0end; ++cx0) {
- uint32_t col = getpixel(source, cx1, y1);
- if (palette) {
- col = getpixel(palette, col, 0);
+ uint32_t col = getpixel(&source, cx1, y1);
+ if (palette.buf) {
+ col = getpixel(&palette, col, 0);
}
if (col != (uint32_t)key) {
setpixel(self, cx0, y0, col);
diff --git a/extmod/modlwip.c b/extmod/modlwip.c
index 961803f5e8..65a3412ecb 100644
--- a/extmod/modlwip.c
+++ b/extmod/modlwip.c
@@ -70,6 +70,10 @@
#define TCP_NODELAY TF_NODELAY
+// Socket flags
+#define MSG_PEEK 0x01
+#define MSG_DONTWAIT 0x02
+
// For compatibilily with older lwIP versions.
#ifndef ip_set_option
#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt))
@@ -673,13 +677,13 @@ static mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, m
}
// Helper function for recv/recvfrom to handle raw/UDP packets
-static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, ip_addr_t *ip, mp_uint_t *port, int *_errno) {
+static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, mp_int_t flags, ip_addr_t *ip, mp_uint_t *port, int *_errno) {
lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iget];
if (slot->pbuf == NULL) {
- if (socket->timeout == 0) {
- // Non-blocking socket.
+ // Non-blocking socket or flag
+ if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) {
*_errno = MP_EAGAIN;
return -1;
}
@@ -705,9 +709,11 @@ static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_u
MICROPY_PY_LWIP_ENTER
u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : p->tot_len), 0);
- pbuf_free(p);
- slot->pbuf = NULL;
- socket->incoming.udp_raw.iget = (socket->incoming.udp_raw.iget + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN;
+ if ((flags & MSG_PEEK) == 0) {
+ pbuf_free(p);
+ slot->pbuf = NULL;
+ socket->incoming.udp_raw.iget = (socket->incoming.udp_raw.iget + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN;
+ }
MICROPY_PY_LWIP_EXIT
@@ -815,14 +821,14 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui
}
// Helper function for recv/recvfrom to handle TCP packets
-static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) {
+static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, mp_int_t flags, int *_errno) {
// Check for any pending errors
STREAM_ERROR_CHECK(socket);
if (socket->incoming.tcp.pbuf == NULL) {
- // Non-blocking socket
- if (socket->timeout == 0) {
+ // Non-blocking socket or flag
+ if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) {
if (socket->state == STATE_PEER_CLOSED) {
return 0;
}
@@ -867,19 +873,21 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
memcpy(buf, (byte *)p->payload + socket->recv_offset, len);
- remaining -= len;
- if (remaining == 0) {
- socket->incoming.tcp.pbuf = p->next;
- // If we don't ref here, free() will free the entire chain,
- // if we ref, it does what we need: frees 1st buf, and decrements
- // next buf's refcount back to 1.
- pbuf_ref(p->next);
- pbuf_free(p);
- socket->recv_offset = 0;
- } else {
- socket->recv_offset += len;
+ if ((flags & MSG_PEEK) == 0) {
+ remaining -= len;
+ if (remaining == 0) {
+ socket->incoming.tcp.pbuf = p->next;
+ // If we don't ref here, free() will free the entire chain,
+ // if we ref, it does what we need: frees 1st buf, and decrements
+ // next buf's refcount back to 1.
+ pbuf_ref(p->next);
+ pbuf_free(p);
+ socket->recv_offset = 0;
+ } else {
+ socket->recv_offset += len;
+ }
+ tcp_recved(socket->pcb.tcp, len);
}
- tcp_recved(socket->pcb.tcp, len);
MICROPY_PY_LWIP_EXIT
@@ -1271,40 +1279,58 @@ static mp_obj_t lwip_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
}
static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_send_obj, lwip_socket_send);
-static mp_obj_t lwip_socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
- lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
+// Common implementation for recv & recvfrom
+static mp_obj_t lwip_socket_recv_common(size_t n_args, const mp_obj_t *args, ip_addr_t *ip, mp_uint_t *port) {
+ lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t len = mp_obj_get_int(args[1]);
+ mp_int_t flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0;
int _errno;
+ vstr_t vstr;
+ mp_uint_t ret = 0;
lwip_socket_check_connected(socket);
- mp_int_t len = mp_obj_get_int(len_in);
- vstr_t vstr;
vstr_init_len(&vstr, len);
- mp_uint_t ret = 0;
switch (socket->type) {
- case MOD_NETWORK_SOCK_STREAM: {
- ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno);
+ case MOD_NETWORK_SOCK_STREAM:
+ if (ip != NULL) {
+ *ip = socket->tcp_peer_addr;
+ *port = (mp_uint_t)socket->tcp_peer_port;
+ }
+ ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, flags, &_errno);
break;
- }
case MOD_NETWORK_SOCK_DGRAM:
#if MICROPY_PY_LWIP_SOCK_RAW
case MOD_NETWORK_SOCK_RAW:
#endif
- ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, NULL, NULL, &_errno);
+ ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, flags, ip, port, &_errno);
break;
}
if (ret == -1) {
mp_raise_OSError(_errno);
}
-
if (ret == 0) {
return mp_const_empty_bytes;
}
vstr.len = ret;
return mp_obj_new_bytes_from_vstr(&vstr);
}
-static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recv_obj, lwip_socket_recv);
+
+static mp_obj_t lwip_socket_recv(size_t n_args, const mp_obj_t *args) {
+ return lwip_socket_recv_common(n_args, args, NULL, NULL);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_recv_obj, 2, 3, lwip_socket_recv);
+
+static mp_obj_t lwip_socket_recvfrom(size_t n_args, const mp_obj_t *args) {
+ ip_addr_t ip;
+ mp_uint_t port;
+ mp_obj_t tuple[2];
+ tuple[0] = lwip_socket_recv_common(n_args, args, &ip, &port);
+ tuple[1] = lwip_format_inet_addr(&ip, port);
+ return mp_obj_new_tuple(2, tuple);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_recvfrom_obj, 2, 3, lwip_socket_recvfrom);
static mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) {
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
@@ -1339,50 +1365,6 @@ static mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t
}
static MP_DEFINE_CONST_FUN_OBJ_3(lwip_socket_sendto_obj, lwip_socket_sendto);
-static mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
- lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
- int _errno;
-
- lwip_socket_check_connected(socket);
-
- mp_int_t len = mp_obj_get_int(len_in);
- vstr_t vstr;
- vstr_init_len(&vstr, len);
- ip_addr_t ip;
- mp_uint_t port;
-
- mp_uint_t ret = 0;
- switch (socket->type) {
- case MOD_NETWORK_SOCK_STREAM: {
- ip = socket->tcp_peer_addr;
- port = (mp_uint_t)socket->tcp_peer_port;
- ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno);
- break;
- }
- case MOD_NETWORK_SOCK_DGRAM:
- #if MICROPY_PY_LWIP_SOCK_RAW
- case MOD_NETWORK_SOCK_RAW:
- #endif
- ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, &ip, &port, &_errno);
- break;
- }
- if (ret == -1) {
- mp_raise_OSError(_errno);
- }
-
- mp_obj_t tuple[2];
- if (ret == 0) {
- tuple[0] = mp_const_empty_bytes;
- } else {
- vstr.len = ret;
- tuple[0] = mp_obj_new_bytes_from_vstr(&vstr);
- }
- tuple[1] = lwip_format_inet_addr(&ip, port);
-
- return mp_obj_new_tuple(2, tuple);
-}
-static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recvfrom_obj, lwip_socket_recvfrom);
-
static mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) {
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
lwip_socket_check_connected(socket);
@@ -1542,12 +1524,12 @@ static mp_uint_t lwip_socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, i
switch (socket->type) {
case MOD_NETWORK_SOCK_STREAM:
- return lwip_tcp_receive(socket, buf, size, errcode);
+ return lwip_tcp_receive(socket, buf, size, 0, errcode);
case MOD_NETWORK_SOCK_DGRAM:
#if MICROPY_PY_LWIP_SOCK_RAW
case MOD_NETWORK_SOCK_RAW:
#endif
- return lwip_raw_udp_receive(socket, buf, size, NULL, NULL, errcode);
+ return lwip_raw_udp_receive(socket, buf, size, 0, NULL, NULL, errcode);
}
// Unreachable
return MP_STREAM_ERROR;
@@ -1919,6 +1901,8 @@ static const mp_rom_map_elem_t mp_module_lwip_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_IPPROTO_TCP), MP_ROM_INT(IP_PROTO_TCP) },
{ MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) },
};
static MP_DEFINE_CONST_DICT(mp_module_lwip_globals, mp_module_lwip_globals_table);
diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c
index 6ca0c17267..5e7030e36f 100644
--- a/extmod/nimble/modbluetooth_nimble.c
+++ b/extmod/nimble/modbluetooth_nimble.c
@@ -60,6 +60,7 @@
static uint8_t nimble_address_mode = BLE_OWN_ADDR_RANDOM;
#define NIMBLE_STARTUP_TIMEOUT 2000
+#define NIMBLE_SHUTDOWN_TIMEOUT 500
// Any BLE_HS_xxx code not in this table will default to MP_EIO.
static int8_t ble_hs_err_to_errno_table[] = {
@@ -554,7 +555,7 @@ static void ble_hs_shutdown_stop_cb(int status, void *arg) {
static struct ble_hs_stop_listener ble_hs_shutdown_stop_listener;
-void mp_bluetooth_nimble_port_shutdown(void) {
+int mp_bluetooth_nimble_port_shutdown(void) {
DEBUG_printf("mp_bluetooth_nimble_port_shutdown (nimble default)\n");
// By default, just call ble_hs_stop directly and wait for the stack to stop.
@@ -562,9 +563,17 @@ void mp_bluetooth_nimble_port_shutdown(void) {
ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL);
+ mp_uint_t timeout_start_ticks_ms = mp_hal_ticks_ms();
while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
- mp_event_wait_indefinite();
+ mp_uint_t elapsed = mp_hal_ticks_ms() - timeout_start_ticks_ms;
+ if (elapsed > NIMBLE_SHUTDOWN_TIMEOUT) {
+ // Stack had not responded (via ble_hs_shutdown_stop_cb)
+ return MP_ETIMEDOUT;
+ }
+
+ mp_event_wait_ms(NIMBLE_SHUTDOWN_TIMEOUT - elapsed);
}
+ return 0;
}
#endif // !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY
@@ -659,10 +668,11 @@ int mp_bluetooth_init(void) {
return 0;
}
-void mp_bluetooth_deinit(void) {
+int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_nimble_ble_state);
+ int ret = 0;
if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
- return;
+ return 0;
}
// Must call ble_hs_stop() in a port-specific way to stop the background
@@ -675,7 +685,7 @@ void mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit: starting port shutdown\n");
- mp_bluetooth_nimble_port_shutdown();
+ ret = mp_bluetooth_nimble_port_shutdown();
assert(mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF);
} else {
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
@@ -692,6 +702,7 @@ void mp_bluetooth_deinit(void) {
#endif
DEBUG_printf("mp_bluetooth_deinit: shut down\n");
+ return ret;
}
bool mp_bluetooth_is_active(void) {
diff --git a/extmod/nimble/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h
index d9bef64920..0dd20eb658 100644
--- a/extmod/nimble/modbluetooth_nimble.h
+++ b/extmod/nimble/modbluetooth_nimble.h
@@ -84,7 +84,7 @@ void mp_bluetooth_nimble_port_hci_deinit(void);
void mp_bluetooth_nimble_port_start(void);
// Tell the port to stop its background task.
-void mp_bluetooth_nimble_port_shutdown(void);
+int mp_bluetooth_nimble_port_shutdown(void);
// --- Called by the HCI UART layer to let us know when packets have been sent.
void mp_bluetooth_nimble_sent_hci_packet(void);