From 3c32d046bf1fee4d9c84e62a057dae2890f91a94 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 4 Oct 2018 21:22:09 +1000 Subject: Copy wiznet module across from MicroPython --- shared-module/wiznet/__init__.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 shared-module/wiznet/__init__.c (limited to 'shared-module') diff --git a/shared-module/wiznet/__init__.c b/shared-module/wiznet/__init__.c new file mode 100644 index 000000000..e69de29bb -- cgit v1.2.3 From d33f6214f15d1983c2fa7f54f42d2145e656fa22 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 4 Oct 2018 21:30:26 +1000 Subject: modify modnetwork and modusocket for circuitpython --- shared-bindings/network/__init__.c | 32 +++++++++------- shared-bindings/network/__init__.h | 7 ++-- shared-bindings/socket/__init__.c | 75 ++++++++++++++++++++------------------ shared-module/network/__init__.c | 0 shared-module/socket/__init__.c | 0 5 files changed, 62 insertions(+), 52 deletions(-) create mode 100644 shared-module/network/__init__.c create mode 100644 shared-module/socket/__init__.c (limited to 'shared-module') diff --git a/shared-bindings/network/__init__.c b/shared-bindings/network/__init__.c index 642174532..357aad346 100644 --- a/shared-bindings/network/__init__.c +++ b/shared-bindings/network/__init__.c @@ -30,7 +30,11 @@ #include "py/objlist.h" #include "py/runtime.h" -#include "modnetwork.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "lib/netutils/netutils.h" + +#include "shared-bindings/network/__init__.h" #if MICROPY_PY_NETWORK @@ -42,6 +46,9 @@ void mod_network_init(void) { mp_obj_list_init(&MP_STATE_PORT(mod_network_nic_list), 0); } +void mod_network_deinit(void) { +} + void mod_network_register_nic(mp_obj_t nic) { for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { if (MP_STATE_PORT(mod_network_nic_list).items[i] == nic) { @@ -50,7 +57,7 @@ void mod_network_register_nic(mp_obj_t nic) { } } // nic not registered so add to list - mp_obj_list_append(&MP_STATE_PORT(mod_network_nic_list), nic); + mp_obj_list_append(MP_OBJ_FROM_PTR(&MP_STATE_PORT(mod_network_nic_list)), nic); } mp_obj_t mod_network_find_nic(const uint8_t *ip) { @@ -62,30 +69,29 @@ mp_obj_t mod_network_find_nic(const uint8_t *ip) { return nic; } - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, translate("no available NIC"))); } +STATIC mp_obj_t network_initialize(void) { + mod_network_init(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(network_initialize_obj, network_initialize); + STATIC mp_obj_t network_route(void) { - return &MP_STATE_PORT(mod_network_nic_list); + return MP_OBJ_FROM_PTR(&MP_STATE_PORT(mod_network_nic_list)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(network_route_obj, network_route); STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) }, - - #if MICROPY_PY_WIZNET5K - { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, - #endif - #if MICROPY_PY_CC3K - { MP_ROM_QSTR(MP_QSTR_CC3K), MP_ROM_PTR(&mod_network_nic_type_cc3k) }, - #endif - + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&network_initialize_obj) }, { MP_ROM_QSTR(MP_QSTR_route), MP_ROM_PTR(&network_route_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_network_globals, mp_module_network_globals_table); -const mp_obj_module_t mp_module_network = { +const mp_obj_module_t network_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_network_globals, }; diff --git a/shared-bindings/network/__init__.h b/shared-bindings/network/__init__.h index 6796b087a..1036c47be 100644 --- a/shared-bindings/network/__init__.h +++ b/shared-bindings/network/__init__.h @@ -23,8 +23,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_STM32_MODNETWORK_H -#define MICROPY_INCLUDED_STM32_MODNETWORK_H +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_NETWORK___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_NETWORK___INIT___H #define MOD_NETWORK_IPADDR_BUF_SIZE (4) @@ -77,7 +77,8 @@ extern const mod_network_nic_type_t mod_network_nic_type_wiznet5k; extern const mod_network_nic_type_t mod_network_nic_type_cc3k; void mod_network_init(void); +void mod_network_deinit(void); void mod_network_register_nic(mp_obj_t nic); mp_obj_t mod_network_find_nic(const uint8_t *ip); -#endif // MICROPY_INCLUDED_STM32_MODNETWORK_H +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_NETWORK___INIT___H diff --git a/shared-bindings/socket/__init__.c b/shared-bindings/socket/__init__.c index 0c663437e..b378b2940 100644 --- a/shared-bindings/socket/__init__.c +++ b/shared-bindings/socket/__init__.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2014 Damien P. George + * 2018 Nick Moore for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,12 +34,17 @@ #include "py/stream.h" #include "py/mperrno.h" #include "lib/netutils/netutils.h" -#include "modnetwork.h" -#if MICROPY_PY_USOCKET +#include "shared-bindings/network/__init__.h" -/******************************************************************************/ -// socket class +//| :mod:`socket` --- TCP, UDP and RAW socket support +//| ================================================= +//| +//| .. module:: socket +//| :synopsis: TCP, UDP and RAW sockets +//| :platform: SAMD21, SAMD51 +//| +//| XXX TODO Write Docs. STATIC const mp_obj_type_t socket_type; @@ -48,7 +54,7 @@ STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type, size_t n_args, size_t // create socket object (not bound to any NIC yet) mod_network_socket_obj_t *s = m_new_obj_with_finaliser(mod_network_socket_obj_t); - s->base.type = (mp_obj_t)&socket_type; + s->base.type = &socket_type; s->nic = MP_OBJ_NULL; s->nic_type = NULL; s->u_param.domain = MOD_NETWORK_AF_INET; @@ -64,7 +70,7 @@ STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type, size_t n_args, size_t } } - return s; + return MP_OBJ_FROM_PTR(s); } STATIC void socket_select_nic(mod_network_socket_obj_t *self, const byte *ip) { @@ -83,7 +89,7 @@ STATIC void socket_select_nic(mod_network_socket_obj_t *self, const byte *ip) { // method socket.bind(address) STATIC mp_obj_t socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); // get address uint8_t ip[MOD_NETWORK_IPADDR_BUF_SIZE]; @@ -104,7 +110,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); // method socket.listen(backlog) STATIC mp_obj_t socket_listen(mp_obj_t self_in, mp_obj_t backlog) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->nic == MP_OBJ_NULL) { // not connected @@ -123,12 +129,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen); // method socket.accept() STATIC mp_obj_t socket_accept(mp_obj_t self_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); // create new socket object // starts with empty NIC so that finaliser doesn't run close() method if accept() fails mod_network_socket_obj_t *socket2 = m_new_obj_with_finaliser(mod_network_socket_obj_t); - socket2->base.type = (mp_obj_t)&socket_type; + socket2->base.type = &socket_type; socket2->nic = MP_OBJ_NULL; socket2->nic_type = NULL; @@ -145,17 +151,17 @@ STATIC mp_obj_t socket_accept(mp_obj_t self_in) { socket2->nic_type = self->nic_type; // make the return value - mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL); - client->items[0] = socket2; + mp_obj_tuple_t *client = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + client->items[0] = MP_OBJ_FROM_PTR(socket2); client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); - return client; + return MP_OBJ_FROM_PTR(client); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); // method socket.connect(address) STATIC mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); // get address uint8_t ip[MOD_NETWORK_IPADDR_BUF_SIZE]; @@ -176,7 +182,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); // method socket.send(bytes) STATIC mp_obj_t socket_send(mp_obj_t self_in, mp_obj_t buf_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->nic == MP_OBJ_NULL) { // not connected mp_raise_OSError(MP_EPIPE); @@ -184,7 +190,7 @@ STATIC mp_obj_t socket_send(mp_obj_t self_in, mp_obj_t buf_in) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); int _errno; - mp_uint_t ret = self->nic_type->send(self, bufinfo.buf, bufinfo.len, &_errno); + mp_int_t ret = self->nic_type->send(self, bufinfo.buf, bufinfo.len, &_errno); if (ret == -1) { mp_raise_OSError(_errno); } @@ -194,7 +200,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send); // method socket.recv(bufsize) STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->nic == MP_OBJ_NULL) { // not connected mp_raise_OSError(MP_ENOTCONN); @@ -203,7 +209,7 @@ STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { vstr_t vstr; vstr_init_len(&vstr, len); int _errno; - mp_uint_t ret = self->nic_type->recv(self, (byte*)vstr.buf, len, &_errno); + mp_int_t ret = self->nic_type->recv(self, (byte*)vstr.buf, len, &_errno); if (ret == -1) { mp_raise_OSError(_errno); } @@ -217,7 +223,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); // method socket.sendto(bytes, address) STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); // get the data mp_buffer_info_t bufinfo; @@ -243,7 +249,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(socket_sendto_obj, socket_sendto); // method socket.recvfrom(bufsize) STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->nic == MP_OBJ_NULL) { // not connected mp_raise_OSError(MP_ENOTCONN); @@ -271,7 +277,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); // method socket.setsockopt(level, optname, value) STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { - mod_network_socket_obj_t *self = args[0]; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); mp_int_t level = mp_obj_get_int(args[1]); mp_int_t opt = mp_obj_get_int(args[2]); @@ -304,7 +310,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_s // timeout=None means blocking // otherwise, timeout is in seconds STATIC mp_obj_t socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) { - mod_network_socket_obj_t *self = self_in; + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->nic == MP_OBJ_NULL) { // not connected mp_raise_OSError(MP_ENOTCONN); @@ -355,8 +361,8 @@ STATIC const mp_rom_map_elem_t socket_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); -mp_uint_t socket_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { - mod_network_socket_obj_t *self = self_in; +mp_uint_t socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); if (request == MP_STREAM_CLOSE) { if (self->nic != MP_OBJ_NULL) { self->nic_type->close(self); @@ -383,8 +389,7 @@ STATIC const mp_obj_type_t socket_type = { /******************************************************************************/ // usocket module -// function usocket.getaddrinfo(host, port) -STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { +STATIC mp_obj_t socket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { size_t hlen; const char *host = mp_obj_str_get_data(host_in, &hlen); mp_int_t port = mp_obj_get_int(port_in); @@ -420,10 +425,10 @@ STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { } if (!have_ip) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, translate("no available NIC"))); } - mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL); + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET); tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM); tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); @@ -431,13 +436,13 @@ STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { tuple->items[4] = netutils_format_inet_addr(out_ip, port, NETUTILS_BIG); return mp_obj_new_list(1, (mp_obj_t*)&tuple); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_usocket_getaddrinfo_obj, mod_usocket_getaddrinfo); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_getaddrinfo_obj, socket_getaddrinfo); -STATIC const mp_rom_map_elem_t mp_module_usocket_globals_table[] = { +STATIC const mp_rom_map_elem_t socket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usocket) }, { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&socket_type) }, - { MP_ROM_QSTR(MP_QSTR_getaddrinfo), MP_ROM_PTR(&mod_usocket_getaddrinfo_obj) }, + { MP_ROM_QSTR(MP_QSTR_getaddrinfo), MP_ROM_PTR(&socket_getaddrinfo_obj) }, // class constants { MP_ROM_QSTR(MP_QSTR_AF_INET), MP_ROM_INT(MOD_NETWORK_AF_INET) }, @@ -458,11 +463,9 @@ STATIC const mp_rom_map_elem_t mp_module_usocket_globals_table[] = { */ }; -STATIC MP_DEFINE_CONST_DICT(mp_module_usocket_globals, mp_module_usocket_globals_table); +STATIC MP_DEFINE_CONST_DICT(socket_globals, socket_globals_table); -const mp_obj_module_t mp_module_usocket = { +const mp_obj_module_t socket_module = { .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_usocket_globals, + .globals = (mp_obj_dict_t*)&socket_globals, }; - -#endif // MICROPY_PY_USOCKET diff --git a/shared-module/network/__init__.c b/shared-module/network/__init__.c new file mode 100644 index 000000000..e69de29bb diff --git a/shared-module/socket/__init__.c b/shared-module/socket/__init__.c new file mode 100644 index 000000000..e69de29bb -- cgit v1.2.3 From 76008ce304e09394733a10d56b944218b41630ca Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 2 Oct 2018 18:06:52 -0700 Subject: Introduce audioio.Mixer which can mix multiple audio samples to produce a single sample. Only works with 16 bit samples on the M4. Fixes #987 --- ports/atmel-samd/Makefile | 2 + ports/atmel-samd/audio_dma.c | 76 --------- shared-bindings/audioio/Mixer.c | 249 +++++++++++++++++++++++++++ shared-bindings/audioio/Mixer.h | 52 ++++++ shared-bindings/audioio/__init__.c | 4 + shared-module/audioio/Mixer.c | 338 +++++++++++++++++++++++++++++++++++++ shared-module/audioio/Mixer.h | 75 ++++++++ shared-module/audioio/WaveFile.c | 18 +- shared-module/audioio/__init__.c | 125 ++++++++++++++ shared-module/audioio/__init__.h | 17 ++ shared-module/displayio/Sprite.c | 2 +- 11 files changed, 880 insertions(+), 78 deletions(-) create mode 100644 shared-bindings/audioio/Mixer.c create mode 100644 shared-bindings/audioio/Mixer.h create mode 100644 shared-module/audioio/Mixer.c create mode 100644 shared-module/audioio/Mixer.h create mode 100644 shared-module/audioio/__init__.c (limited to 'shared-module') diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index f8e5051c2..c8ade987d 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -407,6 +407,8 @@ ifneq ($(CHIP_VARIANT),SAMR21G18A) audioio/__init__.c \ audioio/AudioOut.c SRC_SHARED_MODULE += \ + audioio/__init__.c \ + audioio/Mixer.c \ audioio/RawSample.c \ audioio/WaveFile.c endif diff --git a/ports/atmel-samd/audio_dma.c b/ports/atmel-samd/audio_dma.c index 45d698c17..68d6f182d 100644 --- a/ports/atmel-samd/audio_dma.c +++ b/ports/atmel-samd/audio_dma.c @@ -40,82 +40,6 @@ static audio_dma_t* audio_dma_state[AUDIO_DMA_CHANNEL_COUNT]; // This cannot be in audio_dma_state because it's volatile. static volatile bool audio_dma_pending[AUDIO_DMA_CHANNEL_COUNT]; -uint32_t audiosample_sample_rate(mp_obj_t sample_obj) { - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { - audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); - return sample->sample_rate; - } - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { - audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - return file->sample_rate; - } - return 16000; -} - -uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj) { - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { - audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); - return sample->bits_per_sample; - } - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { - audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - return file->bits_per_sample; - } - return 8; -} - -uint8_t audiosample_channel_count(mp_obj_t sample_obj) { - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { - audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); - return sample->channel_count; - } - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { - audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - return file->channel_count; - } - return 1; -} - -static void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel) { - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { - audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); - audioio_rawsample_reset_buffer(sample, single_channel, audio_channel); - } - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { - audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - audioio_wavefile_reset_buffer(file, single_channel, audio_channel); - } -} - -static audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj, - bool single_channel, - uint8_t channel, - uint8_t** buffer, uint32_t* buffer_length) { - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { - audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); - return audioio_rawsample_get_buffer(sample, single_channel, channel, buffer, buffer_length); - } - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { - audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - return audioio_wavefile_get_buffer(file, single_channel, channel, buffer, buffer_length); - } - return GET_BUFFER_DONE; -} - -static void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, - bool* single_buffer, bool* samples_signed, - uint32_t* max_buffer_length, uint8_t* spacing) { - if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { - audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); - audioio_rawsample_get_buffer_structure(sample, single_channel, single_buffer, - samples_signed, max_buffer_length, spacing); - } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { - audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - audioio_wavefile_get_buffer_structure(file, single_channel, single_buffer, samples_signed, - max_buffer_length, spacing); - } -} - uint8_t find_free_audio_dma_channel(void) { uint8_t channel; for (channel = 0; channel < AUDIO_DMA_CHANNEL_COUNT; channel++) { diff --git a/shared-bindings/audioio/Mixer.c b/shared-bindings/audioio/Mixer.c new file mode 100644 index 000000000..fece3b000 --- /dev/null +++ b/shared-bindings/audioio/Mixer.c @@ -0,0 +1,249 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "shared-bindings/audioio/Mixer.h" + +#include + +#include "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audioio/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| .. currentmodule:: audioio +//| +//| :class:`Mixer` -- Mixes one or more audio samples together +//| =========================================================== +//| +//| Mixer mixes multiple samples into one sample. +//| +//| .. class:: Mixer(channel_count=2, buffer_size=1024) +//| +//| Create a Mixer object that can mix multiple channels with the same sample rate. +//| +//| :param int channel_count: The maximum number of samples to mix at once +//| :param int buffer_size: The total size in bytes of the buffers to mix into +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audioio +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| music = audioio.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) +//| drum = audioio.WaveFile(open("drum.wav", "rb")) +//| mixer = audioio.Mixer(voice_count=2, sample_rate=16000, channel_count=1, bits_per_sample=16, samples_signed=True) +//| a = audioio.AudioOut(board.A0) +//| +//| print("playing") +//| a.play(mixer) +//| mixer.play(music, voice=0) +//| while mixer.playing: +//| mixer.play(drum, voice=1) +//| time.sleep(1) +//| print("stopped") +//| +STATIC mp_obj_t audioio_mixer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 0, 2, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + enum { ARG_voice_count, ARG_buffer_size, ARG_channel_count, ARG_bits_per_sample, ARG_samples_signed, ARG_sample_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} }, + { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1024} }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} }, + { MP_QSTR_bits_per_sample, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_samples_signed, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t voice_count = args[ARG_voice_count].u_int; + if (voice_count < 1 || voice_count > 255) { + mp_raise_ValueError(translate("Invalid voice count")); + } + + mp_int_t channel_count = args[ARG_channel_count].u_int; + if (channel_count < 1 || channel_count > 2) { + mp_raise_ValueError(translate("Invalid channel count")); + } + mp_int_t sample_rate = args[ARG_sample_rate].u_int; + if (sample_rate < 1) { + mp_raise_ValueError(translate("Sample rate must be positive")); + } + mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int; + if (bits_per_sample != 8 && bits_per_sample != 16) { + mp_raise_ValueError(translate("bits_per_sample must be 8 or 16")); + } + audioio_mixer_obj_t *self = m_new_obj_var(audioio_mixer_obj_t, audioio_mixer_voice_t, voice_count); + self->base.type = &audioio_mixer_type; + common_hal_audioio_mixer_construct(self, voice_count, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the Mixer and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audioio_mixer_deinit(mp_obj_t self_in) { + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_mixer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_deinit_obj, audioio_mixer_deinit); + +//| .. method:: __enter__() +//| +//| No-op used by Context Managers. +//| +// Provided by context manager helper. + +//| .. method:: __exit__() +//| +//| Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +STATIC mp_obj_t audioio_mixer_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audioio_mixer_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer___exit___obj, 4, 4, audioio_mixer_obj___exit__); + + +//| .. method:: play(sample, *, voice=0, loop=False) +//| +//| Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audioio.WaveFile`, `audioio.Mixer` or `audioio.RawSample`. +//| +//| If other samples are already playing, the encodings must match. +//| +STATIC mp_obj_t audioio_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_voice, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audioio_mixer_play(self, sample, args[ARG_voice].u_int, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_play_obj, 1, audioio_mixer_obj_play); + +//| .. method:: stop(voice=0) +//| +//| Stops playback and resets to the start of the sample on the given channel. +//| +STATIC mp_obj_t audioio_mixer_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_voice }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + common_hal_audioio_mixer_stop(self, args[ARG_voice].u_int); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_obj, 1, audioio_mixer_obj_stop); + +//| .. attribute:: playing +//| +//| True when an audio sample is being output even if `paused`. (read-only) +//| +STATIC mp_obj_t audioio_mixer_obj_get_playing(mp_obj_t self_in) { + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + return mp_obj_new_bool(common_hal_audioio_mixer_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_playing_obj, audioio_mixer_obj_get_playing); + +const mp_obj_property_t audioio_mixer_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixer_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: sample_rate +//| +//| 32 bit value that dictates how quickly samples are played in Hertz (cycles per second). +//| +STATIC mp_obj_t audioio_mixer_obj_get_sample_rate(mp_obj_t self_in) { + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_mixer_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_sample_rate_obj, audioio_mixer_obj_get_sample_rate); + + +const mp_obj_property_t audioio_mixer_sample_rate_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixer_get_sample_rate_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audioio_mixer_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixer___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixer_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixer_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixer_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_mixer_sample_rate_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_mixer_locals_dict, audioio_mixer_locals_dict_table); + +const mp_obj_type_t audioio_mixer_type = { + { &mp_type_type }, + .name = MP_QSTR_Mixer, + .make_new = audioio_mixer_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_mixer_locals_dict, +}; diff --git a/shared-bindings/audioio/Mixer.h b/shared-bindings/audioio/Mixer.h new file mode 100644 index 000000000..ad1a9fe05 --- /dev/null +++ b/shared-bindings/audioio/Mixer.h @@ -0,0 +1,52 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audioio/Mixer.h" +#include "shared-bindings/audioio/RawSample.h" + +extern const mp_obj_type_t audioio_mixer_type; + +void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, + uint8_t voice_count, + uint32_t buffer_size, + uint8_t bits_per_sample, + bool samples_signed, + uint8_t channel_count, + uint32_t sample_rate); + +void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self); +bool common_hal_audioio_mixer_deinited(audioio_mixer_obj_t* self); +void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, uint8_t voice, bool loop); +void common_hal_audioio_mixer_stop(audioio_mixer_obj_t* self, uint8_t voice); + +bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self); +uint32_t common_hal_audioio_mixer_get_sample_rate(audioio_mixer_obj_t* self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H diff --git a/shared-bindings/audioio/__init__.c b/shared-bindings/audioio/__init__.c index 8a00b43d8..4786b6425 100644 --- a/shared-bindings/audioio/__init__.c +++ b/shared-bindings/audioio/__init__.c @@ -32,6 +32,8 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/audioio/__init__.h" #include "shared-bindings/audioio/AudioOut.h" +#include "shared-bindings/audioio/Mixer.h" +#include "shared-bindings/audioio/RawSample.h" #include "shared-bindings/audioio/WaveFile.h" //| :mod:`audioio` --- Support for audio input and output @@ -49,6 +51,7 @@ //| :maxdepth: 3 //| //| AudioOut +//| Mixer //| RawSample //| WaveFile //| @@ -61,6 +64,7 @@ STATIC const mp_rom_map_elem_t audioio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audioio) }, { MP_ROM_QSTR(MP_QSTR_AudioOut), MP_ROM_PTR(&audioio_audioout_type) }, + { MP_ROM_QSTR(MP_QSTR_Mixer), MP_ROM_PTR(&audioio_mixer_type) }, { MP_ROM_QSTR(MP_QSTR_RawSample), MP_ROM_PTR(&audioio_rawsample_type) }, { MP_ROM_QSTR(MP_QSTR_WaveFile), MP_ROM_PTR(&audioio_wavefile_type) }, }; diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c new file mode 100644 index 000000000..85acbf0ab --- /dev/null +++ b/shared-module/audioio/Mixer.c @@ -0,0 +1,338 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "shared-bindings/audioio/Mixer.h" + +#include + +#include "py/runtime.h" +#include "shared-module/audioio/__init__.h" +#include "shared-module/audioio/RawSample.h" + +void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, + uint8_t voice_count, + uint32_t buffer_size, + uint8_t bits_per_sample, + bool samples_signed, + uint8_t channel_count, + uint32_t sample_rate) { + self->len = buffer_size / 2 / sizeof(uint32_t) * sizeof(uint32_t); + + self->first_buffer = m_malloc(self->len, false); + if (self->first_buffer == NULL) { + common_hal_audioio_mixer_deinit(self); + mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate first buffer")); + } + + self->second_buffer = m_malloc(self->len, false); + if (self->second_buffer == NULL) { + common_hal_audioio_mixer_deinit(self); + mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate second buffer")); + } + + self->bits_per_sample = bits_per_sample; + self->samples_signed = samples_signed; + self->channel_count = channel_count; + self->sample_rate = sample_rate; + self->voice_count = voice_count; + + for (uint8_t i = 0; i < self->voice_count; i++) { + self->voice[i].sample = NULL; + } +} + +void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self) { + self->first_buffer = NULL; + self->second_buffer = NULL; +} + +bool common_hal_audioio_mixer_deinited(audioio_mixer_obj_t* self) { + return self->first_buffer == NULL; +} + +uint32_t common_hal_audioio_mixer_get_sample_rate(audioio_mixer_obj_t* self) { + return self->sample_rate; +} + +void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, uint8_t v, bool loop) { + if (v >= self->voice_count) { + mp_raise_ValueError(translate("Voice index too high")); + } + if (audiosample_sample_rate(sample) != self->sample_rate) { + mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's")); + } + if (audiosample_channel_count(sample) != self->channel_count) { + mp_raise_ValueError(translate("The sample's channel count does not match the mixer's")); + } + if (audiosample_bits_per_sample(sample) != self->bits_per_sample) { + mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's")); + } + bool single_buffer; + bool samples_signed; + uint32_t max_buffer_length; + uint8_t spacing; + audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, + &max_buffer_length, &spacing); + if (samples_signed != self->samples_signed) { + mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); + } + audioio_mixer_voice_t* voice = &self->voice[v]; + voice->sample = sample; + voice->loop = loop; + + audiosample_reset_buffer(sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); + // Track length in terms of words. + voice->buffer_length /= sizeof(uint32_t); + voice->more_data = result == GET_BUFFER_MORE_DATA; +} + +void common_hal_audioio_mixer_stop(audioio_mixer_obj_t* self, uint8_t voice) { + self->voice[voice].sample = NULL; +} + +bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self) { + for (int32_t v = 0; v < self->voice_count; v++) { + if (self->voice[v].sample != NULL) { + return true; + } + } + return false; +} + +void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, + bool single_channel, + uint8_t channel) { + for (int32_t i = 0; i < self->voice_count; i++) { + self->voice[i].sample = NULL; + } +} + +uint32_t add8signed(uint32_t a, uint32_t b) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return __QADD8(a, b); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 4; i++) { + int8_t ai = a >> (sizeof(int8_t) * i); + int8_t bi = b >> (sizeof(int8_t) * i); + int32_t intermediate = (int32_t) ai + bi; + if (intermediate > CHAR_MAX) { + intermediate = CHAR_MAX; + } else if (intermediate < CHAR_MIN) { + intermediate = CHAR_MIN; + } + result |= ((int8_t) intermediate) >> (sizeof(int8_t) * i); + } + return result; + #endif +} + +uint32_t add8unsigned(uint32_t a, uint32_t b) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + // Subtract out the DC offset, add and then shift back. + a = __USUB8(a, 0x80808080); + b = __USUB8(b, 0x80808080); + uint32_t sum = __QADD8(a, b); + return __UADD8(sum, 0x80808080); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 4; i++) { + uint8_t ai = a >> (sizeof(uint8_t) * i); + uint8_t bi = b >> (sizeof(uint8_t) * i); + int32_t intermediate = (int32_t) ai + bi; + if (intermediate > UCHAR_MAX) { + intermediate = UCHAR_MAX; + } + result |= ((uint8_t) intermediate) >> (sizeof(uint8_t) * i); + } + return result; + #endif +} + +uint32_t add16signed(uint32_t a, uint32_t b) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return __QADD16(a, b); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 2; i++) { + int16_t ai = a >> (sizeof(int16_t) * i); + int16_t bi = b >> (sizeof(int16_t) * i); + int32_t intermediate = (int32_t) ai + bi; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } else if (intermediate < SHRT_MIN) { + intermediate = SHRT_MIN; + } + result |= ((int16_t) intermediate) >> (sizeof(int16_t) * i); + } + return result; + #endif +} + +uint32_t add16unsigned(uint32_t a, uint32_t b) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + // Subtract out the DC offset, add and then shift back. + a = __USUB16(a, 0x80008000); + b = __USUB16(b, 0x80008000); + uint32_t sum = __QADD16(a, b); + return __UADD16(sum, 0x80008000); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 2; i++) { + uint16_t ai = a >> (sizeof(uint16_t) * i); + uint16_t bi = b >> (sizeof(uint16_t) * i); + uint32_t intermediate = (uint32_t) ai + bi; + if (intermediate > USHRT_MAX) { + intermediate = USHRT_MAX; + } + result |= ((uint16_t) intermediate) >> (sizeof(int16_t) * i); + } + return result; + #endif +} + +audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, + bool single_channel, + uint8_t channel, + uint8_t** buffer, + uint32_t* buffer_length) { + if (!single_channel) { + channel = 0; + } + + uint32_t channel_read_count = self->left_read_count; + if (channel == 1) { + channel_read_count = self->right_read_count; + } + *buffer_length = self->len; + + bool need_more_data = self->read_count == channel_read_count; + if (need_more_data) { + uint32_t* word_buffer; + if (self->use_first_buffer) { + *buffer = (uint8_t*) self->first_buffer; + word_buffer = self->first_buffer; + } else { + *buffer = (uint8_t*) self->second_buffer; + word_buffer = self->second_buffer; + } + self->use_first_buffer = !self->use_first_buffer; + bool voices_active = false; + for (int32_t v = 0; v < self->voice_count; v++) { + audioio_mixer_voice_t* voice = &self->voice[v]; + if (voice->sample == NULL) { + continue; + } + + uint32_t j = 0; + for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) { + if (j >= voice->buffer_length) { + if (!voice->more_data) { + if (voice->loop) { + audiosample_reset_buffer(voice->sample, false, 0); + } else { + voice->sample = NULL; + break; + } + } + // Load another buffer + audioio_get_buffer_result_t result = audiosample_get_buffer(voice->sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); + // Track length in terms of words. + voice->buffer_length /= sizeof(uint32_t); + voice->more_data = result == GET_BUFFER_MORE_DATA; + j = 0; + } + // First active voice gets copied over verbatim. + uint32_t sample_value = voice->remaining_buffer[j]; + if (!voices_active) { + word_buffer[i] = sample_value; + } else { + if (self->bits_per_sample == 8) { + if (self->samples_signed) { + word_buffer[i] = add8signed(word_buffer[i], sample_value); + } else { + word_buffer[i] = add8unsigned(word_buffer[i], sample_value); + } + } else { + if (self->samples_signed) { + word_buffer[i] = add16signed(word_buffer[i], sample_value); + } else { + word_buffer[i] = add16unsigned(word_buffer[i], sample_value); + } + } + } + j++; + } + voice->buffer_length -= j; + voice->remaining_buffer += j; + + voices_active = true; + } + + // No voice is active so zero out the signal. + if (!voices_active) { + uint32_t zero = 0; + if (!self->samples_signed) { + if (self->bits_per_sample == 8) { + zero = 0x7f7f7f7f; + } else { + zero = 0x7fff7fff; + } + } + for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) { + word_buffer[i] = zero; + } + } + self->read_count += 1; + } else if (!self->use_first_buffer) { + *buffer = (uint8_t*) self->first_buffer; + } else { + *buffer = (uint8_t*) self->second_buffer; + } + + + if (channel == 0) { + self->left_read_count += 1; + } else if (channel == 1) { + self->right_read_count += 1; + *buffer = *buffer + self->bits_per_sample / 8; + } + return GET_BUFFER_MORE_DATA; +} + +void audioio_mixer_get_buffer_structure(audioio_mixer_obj_t* self, bool single_channel, + bool* single_buffer, bool* samples_signed, + uint32_t* max_buffer_length, uint8_t* spacing) { + *single_buffer = false; + *samples_signed = self->samples_signed; + *max_buffer_length = self->len; + if (single_channel) { + *spacing = self->channel_count; + } else { + *spacing = 1; + } +} diff --git a/shared-module/audioio/Mixer.h b/shared-module/audioio/Mixer.h new file mode 100644 index 000000000..6a88fe0bd --- /dev/null +++ b/shared-module/audioio/Mixer.h @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MIXER_H +#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MIXER_H + +#include "py/obj.h" + +#include "shared-module/audioio/__init__.h" + +typedef struct { + mp_obj_t sample; + bool loop; + bool more_data; + uint32_t* remaining_buffer; + uint32_t buffer_length; +} audioio_mixer_voice_t; + +typedef struct { + mp_obj_base_t base; + uint32_t* first_buffer; + uint32_t* second_buffer; + uint32_t len; // in words + uint8_t bits_per_sample; + bool use_first_buffer; + bool samples_signed; + uint8_t channel_count; + uint32_t sample_rate; + + uint32_t read_count; + uint32_t left_read_count; + uint32_t right_read_count; + + uint8_t voice_count; + audioio_mixer_voice_t voice[]; +} audioio_mixer_obj_t; + + +// These are not available from Python because it may be called in an interrupt. +void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, + bool single_channel, + uint8_t channel); +audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, + bool single_channel, + uint8_t channel, + uint8_t** buffer, + uint32_t* buffer_length); // length in bytes +void audioio_mixer_get_buffer_structure(audioio_mixer_obj_t* self, bool single_channel, + bool* single_buffer, bool* samples_signed, + uint32_t* max_buffer_length, uint8_t* spacing); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MIXER_H diff --git a/shared-module/audioio/WaveFile.c b/shared-module/audioio/WaveFile.c index 7efad05e0..e5a7b1a3b 100644 --- a/shared-module/audioio/WaveFile.c +++ b/shared-module/audioio/WaveFile.c @@ -200,13 +200,29 @@ audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t* if (f_read(&self->file->fp, *buffer, num_bytes_to_load, &length_read) != FR_OK) { return GET_BUFFER_ERROR; } + self->bytes_remaining -= length_read; + // Pad the last buffer to word align it. + if (self->bytes_remaining == 0 && length_read % sizeof(uint32_t) != 0) { + uint32_t pad = length_read % sizeof(uint32_t); + length_read += pad; + if (self->bits_per_sample == 8) { + for (uint32_t i = 0; i < pad; i++) { + ((uint8_t*) (*buffer))[length_read / sizeof(uint8_t) - i - 1] = 0x80; + } + } else if (self->bits_per_sample == 16) { + // We know the buffer is aligned because we allocated it onto the heap ourselves. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" + ((int16_t*) (*buffer))[length_read / sizeof(int16_t) - 1] = 0; + #pragma GCC diagnostic pop + } + } *buffer_length = length_read; if (self->buffer_index % 2 == 1) { self->second_buffer_length = length_read; } else { self->buffer_length = length_read; } - self->bytes_remaining -= length_read; self->buffer_index += 1; self->read_count += 1; } diff --git a/shared-module/audioio/__init__.c b/shared-module/audioio/__init__.c new file mode 100644 index 000000000..b87b06a83 --- /dev/null +++ b/shared-module/audioio/__init__.c @@ -0,0 +1,125 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "shared-module/audioio/__init__.h" + +#include "py/obj.h" +#include "shared-bindings/audioio/Mixer.h" +#include "shared-bindings/audioio/RawSample.h" +#include "shared-bindings/audioio/WaveFile.h" +#include "shared-module/audioio/Mixer.h" +#include "shared-module/audioio/RawSample.h" +#include "shared-module/audioio/WaveFile.h" + +uint32_t audiosample_sample_rate(mp_obj_t sample_obj) { + if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { + audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); + return sample->sample_rate; + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { + audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + return file->sample_rate; + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { + audioio_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); + return mixer->sample_rate; + } + return 16000; +} + +uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj) { + if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { + audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); + return sample->bits_per_sample; + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { + audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + return file->bits_per_sample; + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { + audioio_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); + return mixer->bits_per_sample; + } + return 8; +} + +uint8_t audiosample_channel_count(mp_obj_t sample_obj) { + if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { + audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); + return sample->channel_count; + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { + audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + return file->channel_count; + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { + audioio_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); + return mixer->channel_count; + } + return 1; +} + +void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel) { + if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { + audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); + audioio_rawsample_reset_buffer(sample, single_channel, audio_channel); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { + audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + audioio_wavefile_reset_buffer(file, single_channel, audio_channel); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { + audioio_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + audioio_mixer_reset_buffer(file, single_channel, audio_channel); + } +} + +audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj, + bool single_channel, + uint8_t channel, + uint8_t** buffer, uint32_t* buffer_length) { + if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { + audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); + return audioio_rawsample_get_buffer(sample, single_channel, channel, buffer, buffer_length); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { + audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + return audioio_wavefile_get_buffer(file, single_channel, channel, buffer, buffer_length); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { + audioio_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + return audioio_mixer_get_buffer(file, single_channel, channel, buffer, buffer_length); + } + return GET_BUFFER_DONE; +} + +void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, + bool* single_buffer, bool* samples_signed, + uint32_t* max_buffer_length, uint8_t* spacing) { + if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { + audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); + audioio_rawsample_get_buffer_structure(sample, single_channel, single_buffer, + samples_signed, max_buffer_length, spacing); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { + audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + audioio_wavefile_get_buffer_structure(file, single_channel, single_buffer, samples_signed, + max_buffer_length, spacing); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { + audioio_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + audioio_mixer_get_buffer_structure(file, single_channel, single_buffer, samples_signed, + max_buffer_length, spacing); + } +} diff --git a/shared-module/audioio/__init__.h b/shared-module/audioio/__init__.h index 2491beb12..c805f3116 100644 --- a/shared-module/audioio/__init__.h +++ b/shared-module/audioio/__init__.h @@ -27,10 +27,27 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H #define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H +#include +#include + +#include "py/obj.h" + typedef enum { GET_BUFFER_DONE, // No more data to read GET_BUFFER_MORE_DATA, // More data to read. GET_BUFFER_ERROR, // Error while reading data. } audioio_get_buffer_result_t; +uint32_t audiosample_sample_rate(mp_obj_t sample_obj); +uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj); +uint8_t audiosample_channel_count(mp_obj_t sample_obj); +void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel); +audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj, + bool single_channel, + uint8_t channel, + uint8_t** buffer, uint32_t* buffer_length); +void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, + bool* single_buffer, bool* samples_signed, + uint32_t* max_buffer_length, uint8_t* spacing); + #endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H diff --git a/shared-module/displayio/Sprite.c b/shared-module/displayio/Sprite.c index 4fe8d1ff6..87600f721 100644 --- a/shared-module/displayio/Sprite.c +++ b/shared-module/displayio/Sprite.c @@ -68,7 +68,7 @@ bool displayio_sprite_get_pixel(displayio_sprite_t *self, int16_t x, int16_t y, if (y < 0 || y >= self->height || x >= self->width || x < 0) { return false; } - uint32_t value; + uint32_t value = 0; if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_bitmap_type)) { value = common_hal_displayio_bitmap_get_pixel(self->bitmap, x, y); } else if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_ondiskbitmap_type)) { -- cgit v1.2.3 From 8587d8edf0a794644783e26ac243d1ba141a85ca Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 5 Oct 2018 13:00:22 -0700 Subject: Fix voice ending in the middle of a buffer. --- shared-module/audioio/Mixer.c | 55 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'shared-module') diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index 85acbf0ab..c669402bd 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -244,30 +244,47 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, bool voices_active = false; for (int32_t v = 0; v < self->voice_count; v++) { audioio_mixer_voice_t* voice = &self->voice[v]; - if (voice->sample == NULL) { - continue; - } uint32_t j = 0; + bool voice_done = voice->sample == NULL; for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) { - if (j >= voice->buffer_length) { + if (!voice_done && j >= voice->buffer_length) { if (!voice->more_data) { if (voice->loop) { audiosample_reset_buffer(voice->sample, false, 0); } else { voice->sample = NULL; - break; + voice_done = true; } } - // Load another buffer - audioio_get_buffer_result_t result = audiosample_get_buffer(voice->sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); - // Track length in terms of words. - voice->buffer_length /= sizeof(uint32_t); - voice->more_data = result == GET_BUFFER_MORE_DATA; - j = 0; + if (!voice_done) { + // Load another buffer + audioio_get_buffer_result_t result = audiosample_get_buffer(voice->sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); + // Track length in terms of words. + voice->buffer_length /= sizeof(uint32_t); + voice->more_data = result == GET_BUFFER_MORE_DATA; + j = 0; + } } // First active voice gets copied over verbatim. - uint32_t sample_value = voice->remaining_buffer[j]; + uint32_t sample_value; + if (voice_done) { + // Exit early if another voice already set all samples once. + if (voices_active) { + continue; + } + sample_value = 0; + if (!self->samples_signed) { + if (self->bits_per_sample == 8) { + sample_value = 0x7f7f7f7f; + } else { + sample_value = 0x7fff7fff; + } + } + } else { + sample_value = voice->remaining_buffer[j]; + } + if (!voices_active) { word_buffer[i] = sample_value; } else { @@ -293,20 +310,6 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, voices_active = true; } - // No voice is active so zero out the signal. - if (!voices_active) { - uint32_t zero = 0; - if (!self->samples_signed) { - if (self->bits_per_sample == 8) { - zero = 0x7f7f7f7f; - } else { - zero = 0x7fff7fff; - } - } - for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) { - word_buffer[i] = zero; - } - } self->read_count += 1; } else if (!self->use_first_buffer) { *buffer = (uint8_t*) self->first_buffer; -- cgit v1.2.3 From 3c6812f2c1c900b4b6fd52bc9a5ee3b6f483626f Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 5 Oct 2018 14:43:27 -0700 Subject: Fix M0 math --- shared-module/audioio/Mixer.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'shared-module') diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index c669402bd..0709e2e42 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -131,25 +131,28 @@ void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, } } + #pragma GCC push_options + #pragma GCC optimize ("O0") uint32_t add8signed(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) return __QADD8(a, b); #else uint32_t result = 0; for (int8_t i = 0; i < 4; i++) { - int8_t ai = a >> (sizeof(int8_t) * i); - int8_t bi = b >> (sizeof(int8_t) * i); + int8_t ai = a >> (sizeof(int8_t) * 8 * i); + int8_t bi = b >> (sizeof(int8_t) * 8 * i); int32_t intermediate = (int32_t) ai + bi; if (intermediate > CHAR_MAX) { intermediate = CHAR_MAX; } else if (intermediate < CHAR_MIN) { - intermediate = CHAR_MIN; + //intermediate = CHAR_MIN; } - result |= ((int8_t) intermediate) >> (sizeof(int8_t) * i); + result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int8_t) * 8 * i); } return result; #endif } + #pragma GCC pop_options uint32_t add8unsigned(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) @@ -161,13 +164,13 @@ uint32_t add8unsigned(uint32_t a, uint32_t b) { #else uint32_t result = 0; for (int8_t i = 0; i < 4; i++) { - uint8_t ai = a >> (sizeof(uint8_t) * i); - uint8_t bi = b >> (sizeof(uint8_t) * i); + int8_t ai = (a >> (sizeof(uint8_t) * 8 * i)) - 128; + int8_t bi = (b >> (sizeof(uint8_t) * 8 * i)) - 128; int32_t intermediate = (int32_t) ai + bi; if (intermediate > UCHAR_MAX) { intermediate = UCHAR_MAX; } - result |= ((uint8_t) intermediate) >> (sizeof(uint8_t) * i); + result |= ((uint8_t) intermediate + 128) << (sizeof(uint8_t) * 8 * i); } return result; #endif @@ -179,15 +182,15 @@ uint32_t add16signed(uint32_t a, uint32_t b) { #else uint32_t result = 0; for (int8_t i = 0; i < 2; i++) { - int16_t ai = a >> (sizeof(int16_t) * i); - int16_t bi = b >> (sizeof(int16_t) * i); + int16_t ai = a >> (sizeof(int16_t) * 8 * i); + int16_t bi = b >> (sizeof(int16_t) * 8 * i); int32_t intermediate = (int32_t) ai + bi; if (intermediate > SHRT_MAX) { intermediate = SHRT_MAX; } else if (intermediate < SHRT_MIN) { intermediate = SHRT_MIN; } - result |= ((int16_t) intermediate) >> (sizeof(int16_t) * i); + result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); } return result; #endif @@ -203,13 +206,13 @@ uint32_t add16unsigned(uint32_t a, uint32_t b) { #else uint32_t result = 0; for (int8_t i = 0; i < 2; i++) { - uint16_t ai = a >> (sizeof(uint16_t) * i); - uint16_t bi = b >> (sizeof(uint16_t) * i); - uint32_t intermediate = (uint32_t) ai + bi; + int16_t ai = (a >> (sizeof(uint16_t) * 8 * i)) - 0x8000; + int16_t bi = (b >> (sizeof(uint16_t) * 8 * i)) - 0x8000; + int32_t intermediate = (int32_t) ai + bi; if (intermediate > USHRT_MAX) { intermediate = USHRT_MAX; } - result |= ((uint16_t) intermediate) >> (sizeof(int16_t) * i); + result |= ((uint16_t) intermediate + 0x8000) << (sizeof(int16_t) * 8 * i); } return result; #endif -- cgit v1.2.3 From 6e624b9c6a51e8b88eb22e62427c8e489f36072d Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 9 Oct 2018 13:05:58 +1100 Subject: Split wiznet.WIZNET5K off into its own file --- ports/atmel-samd/Makefile | 2 +- shared-bindings/wiznet/__init__.c | 492 +---------------------------------- shared-bindings/wiznet/wiznet5k.c | 527 ++++++++++++++++++++++++++++++++++++++ shared-module/wiznet/wiznet5k.c | 0 4 files changed, 531 insertions(+), 490 deletions(-) create mode 100644 shared-bindings/wiznet/wiznet5k.c create mode 100644 shared-module/wiznet/wiznet5k.c (limited to 'shared-module') diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index f01704a81..1ee0d79f0 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -421,7 +421,7 @@ SRC_SHARED_MODULE = \ ifeq ($(MICROPY_PY_NETWORK),1) SRC_SHARED_MODULE += socket/__init__.c network/__init__.c ifneq ($(MICROPY_PY_WIZNET5K),0) -SRC_SHARED_MODULE += wiznet/__init__.c +SRC_SHARED_MODULE += wiznet/__init__.c wiznet/wiznet5k.c endif endif diff --git a/shared-bindings/wiznet/__init__.c b/shared-bindings/wiznet/__init__.c index 088335290..e448d2ef1 100644 --- a/shared-bindings/wiznet/__init__.c +++ b/shared-bindings/wiznet/__init__.c @@ -31,502 +31,17 @@ #include "py/objlist.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "py/stream.h" -#include "py/mperrno.h" #include "py/mphal.h" -#include "lib/netutils/netutils.h" #include "shared-bindings/network/__init__.h" -#include "shared-bindings/digitalio/DigitalInOut.h" -#include "shared-bindings/digitalio/DriveMode.h" -#include "shared-bindings/busio/SPI.h" -#include "shared-bindings/random/__init__.h" -#if MICROPY_PY_WIZNET5K - -#include "ethernet/wizchip_conf.h" -#include "ethernet/socket.h" -#include "internet/dns/dns.h" - -/// \moduleref network - -typedef struct _wiznet5k_obj_t { - mp_obj_base_t base; - mp_uint_t cris_state; - busio_spi_obj_t *spi; - digitalio_digitalinout_obj_t cs; - digitalio_digitalinout_obj_t rst; - uint8_t socket_used; -} wiznet5k_obj_t; - -STATIC wiznet5k_obj_t wiznet5k_obj; - -STATIC void wiz_cris_enter(void) { - wiznet5k_obj.cris_state = MICROPY_BEGIN_ATOMIC_SECTION(); -} - -STATIC void wiz_cris_exit(void) { - MICROPY_END_ATOMIC_SECTION(wiznet5k_obj.cris_state); -} - -STATIC void wiz_cs_select(void) { - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 0); -} - -STATIC void wiz_cs_deselect(void) { - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 1); -} - -STATIC void wiz_spi_read(uint8_t *buf, uint32_t len) { - (void)common_hal_busio_spi_read(wiznet5k_obj.spi, buf, len, 0); -} - -STATIC void wiz_spi_write(const uint8_t *buf, uint32_t len) { - (void)common_hal_busio_spi_write(wiznet5k_obj.spi, buf, len); -} - -STATIC int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { - uint8_t dns_ip[MOD_NETWORK_IPADDR_BUF_SIZE] = {8, 8, 8, 8}; - uint8_t *buf = m_new(uint8_t, MAX_DNS_BUF_SIZE); - DNS_init(0, buf); - mp_int_t ret = DNS_run(dns_ip, (uint8_t*)name, out_ip); - m_del(uint8_t, buf, MAX_DNS_BUF_SIZE); - if (ret == 1) { - // success - return 0; - } else { - // failure - return -2; - } -} - -STATIC int wiznet5k_socket_socket(mod_network_socket_obj_t *socket, int *_errno) { - if (socket->u_param.domain != MOD_NETWORK_AF_INET) { - *_errno = MP_EAFNOSUPPORT; - return -1; - } - - switch (socket->u_param.type) { - case MOD_NETWORK_SOCK_STREAM: socket->u_param.type = Sn_MR_TCP; break; - case MOD_NETWORK_SOCK_DGRAM: socket->u_param.type = Sn_MR_UDP; break; - default: *_errno = MP_EINVAL; return -1; - } - - if (socket->u_param.fileno == -1) { - // get first unused socket number - for (mp_uint_t sn = 0; sn < _WIZCHIP_SOCK_NUM_; sn++) { - if ((wiznet5k_obj.socket_used & (1 << sn)) == 0) { - wiznet5k_obj.socket_used |= (1 << sn); - socket->u_param.fileno = sn; - break; - } - } - if (socket->u_param.fileno == -1) { - // too many open sockets - *_errno = MP_EMFILE; - return -1; - } - } - - // WIZNET does not have a concept of pure "open socket". You need to know - // if it's a server or client at the time of creation of the socket. - // So, we defer the open until we know what kind of socket we want. - - // use "domain" to indicate that this socket has not yet been opened - socket->u_param.domain = 0; - - return 0; -} - -STATIC void wiznet5k_socket_close(mod_network_socket_obj_t *socket) { - uint8_t sn = (uint8_t)socket->u_param.fileno; - if (sn < _WIZCHIP_SOCK_NUM_) { - wiznet5k_obj.socket_used &= ~(1 << sn); - WIZCHIP_EXPORT(close)(sn); - } -} - -STATIC int wiznet5k_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { - // open the socket in server mode (if port != 0) - mp_int_t ret = WIZCHIP_EXPORT(socket)(socket->u_param.fileno, socket->u_param.type, port, 0); - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - - // indicate that this socket has been opened - socket->u_param.domain = 1; - - // success - return 0; -} - -STATIC int wiznet5k_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) { - mp_int_t ret = WIZCHIP_EXPORT(listen)(socket->u_param.fileno); - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return 0; -} - -STATIC int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) { - for (;;) { - int sr = getSn_SR((uint8_t)socket->u_param.fileno); - if (sr == SOCK_ESTABLISHED) { - socket2->u_param = socket->u_param; - getSn_DIPR((uint8_t)socket2->u_param.fileno, ip); - *port = getSn_PORT(socket2->u_param.fileno); - - // WIZnet turns the listening socket into the client socket, so we - // need to re-bind and re-listen on another socket for the server. - // TODO handle errors, especially no-more-sockets error - socket->u_param.domain = MOD_NETWORK_AF_INET; - socket->u_param.fileno = -1; - int _errno2; - if (wiznet5k_socket_socket(socket, &_errno2) != 0) { - //printf("(bad resocket %d)\n", _errno2); - } else if (wiznet5k_socket_bind(socket, NULL, *port, &_errno2) != 0) { - //printf("(bad rebind %d)\n", _errno2); - } else if (wiznet5k_socket_listen(socket, 0, &_errno2) != 0) { - //printf("(bad relisten %d)\n", _errno2); - } - - return 0; - } - if (sr == SOCK_CLOSED || sr == SOCK_CLOSE_WAIT) { - wiznet5k_socket_close(socket); - *_errno = MP_ENOTCONN; // ?? - return -1; - } - mp_hal_delay_ms(1); - } -} - -STATIC int wiznet5k_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { - // use "bind" function to open the socket in client mode - if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { - return -1; - } - - // now connect - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(connect)(socket->u_param.fileno, ip, port); - MP_THREAD_GIL_ENTER(); - - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - - // success - return 0; -} - -STATIC mp_uint_t wiznet5k_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(send)(socket->u_param.fileno, (byte*)buf, len); - MP_THREAD_GIL_ENTER(); - - // TODO convert Wiz errno's to POSIX ones - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC mp_uint_t wiznet5k_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) { - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(recv)(socket->u_param.fileno, buf, len); - MP_THREAD_GIL_ENTER(); - - // TODO convert Wiz errno's to POSIX ones - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC mp_uint_t wiznet5k_socket_sendto(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { - if (socket->u_param.domain == 0) { - // socket not opened; use "bind" function to open the socket in client mode - if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { - return -1; - } - } - - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(sendto)(socket->u_param.fileno, (byte*)buf, len, ip, port); - MP_THREAD_GIL_ENTER(); - - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC mp_uint_t wiznet5k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { - uint16_t port2; - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(recvfrom)(socket->u_param.fileno, buf, len, ip, &port2); - MP_THREAD_GIL_ENTER(); - *port = port2; - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC int wiznet5k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { - // TODO - *_errno = MP_EINVAL; - return -1; -} - -STATIC int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) { - // TODO - *_errno = MP_EINVAL; - return -1; - - /* - if (timeout_ms == 0) { - // set non-blocking mode - uint8_t arg = SOCK_IO_NONBLOCK; - WIZCHIP_EXPORT(ctlsocket)(socket->u_param.fileno, CS_SET_IOMODE, &arg); - } - */ -} - -STATIC int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { - if (request == MP_STREAM_POLL) { - int ret = 0; - if (arg & MP_STREAM_POLL_RD && getSn_RX_RSR(socket->u_param.fileno) != 0) { - ret |= MP_STREAM_POLL_RD; - } - if (arg & MP_STREAM_POLL_WR && getSn_TX_FSR(socket->u_param.fileno) != 0) { - ret |= MP_STREAM_POLL_WR; - } - return ret; - } else { - *_errno = MP_EINVAL; - return MP_STREAM_ERROR; - } -} - -#if 0 -STATIC void wiznet5k_socket_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { - wiznet5k_socket_obj_t *self = self_in; - print(env, "", self->sn, getSn_MR(self->sn)); -} - -STATIC mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in) { - mp_int_t ret = WIZCHIP_EXPORT(disconnect)(self->sn); - return 0; -} -#endif - -void create_random_mac_address(uint8_t *mac) { - uint32_t rb1 = shared_modules_random_getrandbits(24); - uint32_t rb2 = shared_modules_random_getrandbits(24); - // first octet has multicast bit (0) cleared and local bit (1) set - // everything else is just set randomly - mac[0] = ((uint8_t)(rb1 >> 16) & 0xfe) | 0x02; - mac[1] = (uint8_t)(rb1 >> 8); - mac[2] = (uint8_t)(rb1); - mac[3] = (uint8_t)(rb2 >> 16); - mac[4] = (uint8_t)(rb2 >> 8); - mac[5] = (uint8_t)(rb2); -} - -/******************************************************************************/ -// MicroPython bindings - -/// \classmethod \constructor(spi, pin_cs, pin_rst) -/// Create and return a WIZNET5K object. -STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - // check arguments - mp_arg_check_num(n_args, n_kw, 3, 3, false); - - // init the wiznet5k object - wiznet5k_obj.base.type = (mp_obj_type_t*)&mod_network_nic_type_wiznet5k; - wiznet5k_obj.cris_state = 0; - wiznet5k_obj.spi = MP_OBJ_TO_PTR(args[0]); - common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.cs, args[1]); - common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.rst, args[2]); - wiznet5k_obj.socket_used = 0; - - /*!< SPI configuration */ - // XXX probably should check if the provided SPI is already configured, and - // if so skip configuration? - - common_hal_busio_spi_configure(wiznet5k_obj.spi, - 10000000, // BAUDRATE 10MHz - 1, // HIGH POLARITY - 1, // SECOND PHASE TRANSITION - 8 // 8 BITS - ); - - common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.cs, 1, DRIVE_MODE_PUSH_PULL); - common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.rst, 1, DRIVE_MODE_PUSH_PULL); - - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 0); - mp_hal_delay_us(10); // datasheet says 2us - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 1); - mp_hal_delay_ms(160); // datasheet says 150ms - - reg_wizchip_cris_cbfunc(wiz_cris_enter, wiz_cris_exit); - reg_wizchip_cs_cbfunc(wiz_cs_select, wiz_cs_deselect); - reg_wizchip_spi_cbfunc(wiz_spi_read, wiz_spi_write); - - uint8_t sn_size[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; // 2k buffer for each socket - ctlwizchip(CW_INIT_WIZCHIP, sn_size); - - // set some sensible default values; they are configurable using ifconfig method - wiz_NetInfo netinfo = { - .ip = {192, 168, 0, 18}, - .sn = {255, 255, 255, 0}, - .gw = {192, 168, 0, 1}, - .dns = {8, 8, 8, 8}, // Google public DNS - .dhcp = NETINFO_STATIC, - }; - create_random_mac_address(netinfo.mac); - ctlnetwork(CN_SET_NETINFO, (void*)&netinfo); - - // seems we need a small delay after init - mp_hal_delay_ms(250); - - // register with network module - network_module_register_nic(&wiznet5k_obj); - - // return wiznet5k object - return &wiznet5k_obj; -} - -/// \method regs() -/// Dump WIZNET5K registers. -STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { - //wiznet5k_obj_t *self = self_in; - printf("Wiz CREG:"); - for (int i = 0; i < 0x50; ++i) { - if (i % 16 == 0) { - printf("\n %04x:", i); - } - #if MICROPY_PY_WIZNET5K == 5200 - uint32_t reg = i; - #else - uint32_t reg = _W5500_IO_BASE_ | i << 8; - #endif - printf(" %02x", WIZCHIP_READ(reg)); - } - for (int sn = 0; sn < 4; ++sn) { - printf("\nWiz SREG[%d]:", sn); - for (int i = 0; i < 0x30; ++i) { - if (i % 16 == 0) { - printf("\n %04x:", i); - } - #if MICROPY_PY_WIZNET5K == 5200 - uint32_t reg = WIZCHIP_SREG_ADDR(sn, i); - #else - uint32_t reg = _W5500_IO_BASE_ | i << 8 | WIZCHIP_SREG_BLOCK(sn) << 3; - #endif - printf(" %02x", WIZCHIP_READ(reg)); - } - } - printf("\n"); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_regs_obj, wiznet5k_regs); - -STATIC mp_obj_t wiznet5k_connected_get_value(mp_obj_t self_in) { - (void)self_in; - return mp_obj_new_bool(wizphy_getphylink() == PHY_LINK_ON); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_connected_get_value_obj, wiznet5k_connected_get_value); - -//| attribute:: connected -//| -//| is this device physically connected? - -const mp_obj_property_t wiznet5k_connected_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&wiznet5k_connected_get_value_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -/// \method ifconfig([(ip, subnet, gateway, dns)]) -/// Get/set IP address, subnet mask, gateway and DNS. -STATIC mp_obj_t wiznet5k_ifconfig(size_t n_args, const mp_obj_t *args) { - wiz_NetInfo netinfo; - ctlnetwork(CN_GET_NETINFO, &netinfo); - if (n_args == 1) { - // get - mp_obj_t tuple[4] = { - netutils_format_ipv4_addr(netinfo.ip, NETUTILS_BIG), - netutils_format_ipv4_addr(netinfo.sn, NETUTILS_BIG), - netutils_format_ipv4_addr(netinfo.gw, NETUTILS_BIG), - netutils_format_ipv4_addr(netinfo.dns, NETUTILS_BIG), - }; - return mp_obj_new_tuple(4, tuple); - } else { - // set - mp_obj_t *items; - mp_obj_get_array_fixed_n(args[1], 4, &items); - netutils_parse_ipv4_addr(items[0], netinfo.ip, NETUTILS_BIG); - netutils_parse_ipv4_addr(items[1], netinfo.sn, NETUTILS_BIG); - netutils_parse_ipv4_addr(items[2], netinfo.gw, NETUTILS_BIG); - netutils_parse_ipv4_addr(items[3], netinfo.dns, NETUTILS_BIG); - ctlnetwork(CN_SET_NETINFO, &netinfo); - return mp_const_none; - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k_ifconfig); - -STATIC const mp_rom_map_elem_t wiznet5k_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_regs), MP_ROM_PTR(&wiznet5k_regs_obj) }, - { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wiznet5k_ifconfig_obj) }, - { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&wiznet5k_connected_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(wiznet5k_locals_dict, wiznet5k_locals_dict_table); - -const mod_network_nic_type_t mod_network_nic_type_wiznet5k = { - .base = { - { &mp_type_type }, - .name = MP_QSTR_WIZNET5K, - .make_new = wiznet5k_make_new, - .locals_dict = (mp_obj_dict_t*)&wiznet5k_locals_dict, - }, - .gethostbyname = wiznet5k_gethostbyname, - .socket = wiznet5k_socket_socket, - .close = wiznet5k_socket_close, - .bind = wiznet5k_socket_bind, - .listen = wiznet5k_socket_listen, - .accept = wiznet5k_socket_accept, - .connect = wiznet5k_socket_connect, - .send = wiznet5k_socket_send, - .recv = wiznet5k_socket_recv, - .sendto = wiznet5k_socket_sendto, - .recvfrom = wiznet5k_socket_recvfrom, - .setsockopt = wiznet5k_socket_setsockopt, - .settimeout = wiznet5k_socket_settimeout, - .ioctl = wiznet5k_socket_ioctl, -}; +extern const mod_network_nic_type_t mod_network_nic_type_wiznet5k; STATIC const mp_rom_map_elem_t mp_module_wiznet_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_wiznet) }, +#ifdef MICROPY_PY_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, +#endif // MICROPY_PY_WIZNET5K }; STATIC MP_DEFINE_CONST_DICT(mp_module_wiznet_globals, mp_module_wiznet_globals_table); @@ -535,4 +50,3 @@ const mp_obj_module_t wiznet_module = { .globals = (mp_obj_dict_t*)&mp_module_wiznet_globals, }; -#endif // MICROPY_PY_WIZNET5K diff --git a/shared-bindings/wiznet/wiznet5k.c b/shared-bindings/wiznet/wiznet5k.c new file mode 100644 index 000000000..967e7d0f4 --- /dev/null +++ b/shared-bindings/wiznet/wiznet5k.c @@ -0,0 +1,527 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 +#include +#include + +#include "py/objlist.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "lib/netutils/netutils.h" + +#include "shared-bindings/network/__init__.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/random/__init__.h" + +#if MICROPY_PY_WIZNET5K + +#include "ethernet/wizchip_conf.h" +#include "ethernet/socket.h" +#include "internet/dns/dns.h" + +/// \moduleref network + +typedef struct _wiznet5k_obj_t { + mp_obj_base_t base; + mp_uint_t cris_state; + busio_spi_obj_t *spi; + digitalio_digitalinout_obj_t cs; + digitalio_digitalinout_obj_t rst; + uint8_t socket_used; +} wiznet5k_obj_t; + +STATIC wiznet5k_obj_t wiznet5k_obj; + +STATIC void wiz_cris_enter(void) { + wiznet5k_obj.cris_state = MICROPY_BEGIN_ATOMIC_SECTION(); +} + +STATIC void wiz_cris_exit(void) { + MICROPY_END_ATOMIC_SECTION(wiznet5k_obj.cris_state); +} + +STATIC void wiz_cs_select(void) { + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 0); +} + +STATIC void wiz_cs_deselect(void) { + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 1); +} + +STATIC void wiz_spi_read(uint8_t *buf, uint32_t len) { + (void)common_hal_busio_spi_read(wiznet5k_obj.spi, buf, len, 0); +} + +STATIC void wiz_spi_write(const uint8_t *buf, uint32_t len) { + (void)common_hal_busio_spi_write(wiznet5k_obj.spi, buf, len); +} + +STATIC int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { + uint8_t dns_ip[MOD_NETWORK_IPADDR_BUF_SIZE] = {8, 8, 8, 8}; + uint8_t *buf = m_new(uint8_t, MAX_DNS_BUF_SIZE); + DNS_init(0, buf); + mp_int_t ret = DNS_run(dns_ip, (uint8_t*)name, out_ip); + m_del(uint8_t, buf, MAX_DNS_BUF_SIZE); + if (ret == 1) { + // success + return 0; + } else { + // failure + return -2; + } +} + +STATIC int wiznet5k_socket_socket(mod_network_socket_obj_t *socket, int *_errno) { + if (socket->u_param.domain != MOD_NETWORK_AF_INET) { + *_errno = MP_EAFNOSUPPORT; + return -1; + } + + switch (socket->u_param.type) { + case MOD_NETWORK_SOCK_STREAM: socket->u_param.type = Sn_MR_TCP; break; + case MOD_NETWORK_SOCK_DGRAM: socket->u_param.type = Sn_MR_UDP; break; + default: *_errno = MP_EINVAL; return -1; + } + + if (socket->u_param.fileno == -1) { + // get first unused socket number + for (mp_uint_t sn = 0; sn < _WIZCHIP_SOCK_NUM_; sn++) { + if ((wiznet5k_obj.socket_used & (1 << sn)) == 0) { + wiznet5k_obj.socket_used |= (1 << sn); + socket->u_param.fileno = sn; + break; + } + } + if (socket->u_param.fileno == -1) { + // too many open sockets + *_errno = MP_EMFILE; + return -1; + } + } + + // WIZNET does not have a concept of pure "open socket". You need to know + // if it's a server or client at the time of creation of the socket. + // So, we defer the open until we know what kind of socket we want. + + // use "domain" to indicate that this socket has not yet been opened + socket->u_param.domain = 0; + + return 0; +} + +STATIC void wiznet5k_socket_close(mod_network_socket_obj_t *socket) { + uint8_t sn = (uint8_t)socket->u_param.fileno; + if (sn < _WIZCHIP_SOCK_NUM_) { + wiznet5k_obj.socket_used &= ~(1 << sn); + WIZCHIP_EXPORT(close)(sn); + } +} + +STATIC int wiznet5k_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + // open the socket in server mode (if port != 0) + mp_int_t ret = WIZCHIP_EXPORT(socket)(socket->u_param.fileno, socket->u_param.type, port, 0); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + + // indicate that this socket has been opened + socket->u_param.domain = 1; + + // success + return 0; +} + +STATIC int wiznet5k_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) { + mp_int_t ret = WIZCHIP_EXPORT(listen)(socket->u_param.fileno); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return 0; +} + +STATIC int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) { + for (;;) { + int sr = getSn_SR((uint8_t)socket->u_param.fileno); + if (sr == SOCK_ESTABLISHED) { + socket2->u_param = socket->u_param; + getSn_DIPR((uint8_t)socket2->u_param.fileno, ip); + *port = getSn_PORT(socket2->u_param.fileno); + + // WIZnet turns the listening socket into the client socket, so we + // need to re-bind and re-listen on another socket for the server. + // TODO handle errors, especially no-more-sockets error + socket->u_param.domain = MOD_NETWORK_AF_INET; + socket->u_param.fileno = -1; + int _errno2; + if (wiznet5k_socket_socket(socket, &_errno2) != 0) { + //printf("(bad resocket %d)\n", _errno2); + } else if (wiznet5k_socket_bind(socket, NULL, *port, &_errno2) != 0) { + //printf("(bad rebind %d)\n", _errno2); + } else if (wiznet5k_socket_listen(socket, 0, &_errno2) != 0) { + //printf("(bad relisten %d)\n", _errno2); + } + + return 0; + } + if (sr == SOCK_CLOSED || sr == SOCK_CLOSE_WAIT) { + wiznet5k_socket_close(socket); + *_errno = MP_ENOTCONN; // ?? + return -1; + } + mp_hal_delay_ms(1); + } +} + +STATIC int wiznet5k_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + // use "bind" function to open the socket in client mode + if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { + return -1; + } + + // now connect + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(connect)(socket->u_param.fileno, ip, port); + MP_THREAD_GIL_ENTER(); + + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + + // success + return 0; +} + +STATIC mp_uint_t wiznet5k_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(send)(socket->u_param.fileno, (byte*)buf, len); + MP_THREAD_GIL_ENTER(); + + // TODO convert Wiz errno's to POSIX ones + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC mp_uint_t wiznet5k_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) { + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(recv)(socket->u_param.fileno, buf, len); + MP_THREAD_GIL_ENTER(); + + // TODO convert Wiz errno's to POSIX ones + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC mp_uint_t wiznet5k_socket_sendto(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { + if (socket->u_param.domain == 0) { + // socket not opened; use "bind" function to open the socket in client mode + if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { + return -1; + } + } + + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(sendto)(socket->u_param.fileno, (byte*)buf, len, ip, port); + MP_THREAD_GIL_ENTER(); + + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC mp_uint_t wiznet5k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { + uint16_t port2; + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(recvfrom)(socket->u_param.fileno, buf, len, ip, &port2); + MP_THREAD_GIL_ENTER(); + *port = port2; + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC int wiznet5k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { + // TODO + *_errno = MP_EINVAL; + return -1; +} + +STATIC int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) { + // TODO + *_errno = MP_EINVAL; + return -1; + + /* + if (timeout_ms == 0) { + // set non-blocking mode + uint8_t arg = SOCK_IO_NONBLOCK; + WIZCHIP_EXPORT(ctlsocket)(socket->u_param.fileno, CS_SET_IOMODE, &arg); + } + */ +} + +STATIC int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { + if (request == MP_STREAM_POLL) { + int ret = 0; + if (arg & MP_STREAM_POLL_RD && getSn_RX_RSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_RD; + } + if (arg & MP_STREAM_POLL_WR && getSn_TX_FSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_WR; + } + return ret; + } else { + *_errno = MP_EINVAL; + return MP_STREAM_ERROR; + } +} + +#if 0 +STATIC void wiznet5k_socket_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + wiznet5k_socket_obj_t *self = self_in; + print(env, "", self->sn, getSn_MR(self->sn)); +} + +STATIC mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in) { + mp_int_t ret = WIZCHIP_EXPORT(disconnect)(self->sn); + return 0; +} +#endif + +void create_random_mac_address(uint8_t *mac) { + uint32_t rb1 = shared_modules_random_getrandbits(24); + uint32_t rb2 = shared_modules_random_getrandbits(24); + // first octet has multicast bit (0) cleared and local bit (1) set + // everything else is just set randomly + mac[0] = ((uint8_t)(rb1 >> 16) & 0xfe) | 0x02; + mac[1] = (uint8_t)(rb1 >> 8); + mac[2] = (uint8_t)(rb1); + mac[3] = (uint8_t)(rb2 >> 16); + mac[4] = (uint8_t)(rb2 >> 8); + mac[5] = (uint8_t)(rb2); +} + +/******************************************************************************/ +// MicroPython bindings + +/// \classmethod \constructor(spi, pin_cs, pin_rst) +/// Create and return a WIZNET5K object. +STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 3, 3, false); + + // init the wiznet5k object + wiznet5k_obj.base.type = (mp_obj_type_t*)&mod_network_nic_type_wiznet5k; + wiznet5k_obj.cris_state = 0; + wiznet5k_obj.spi = MP_OBJ_TO_PTR(args[0]); + common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.cs, args[1]); + common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.rst, args[2]); + wiznet5k_obj.socket_used = 0; + + /*!< SPI configuration */ + // XXX probably should check if the provided SPI is already configured, and + // if so skip configuration? + + common_hal_busio_spi_configure(wiznet5k_obj.spi, + 10000000, // BAUDRATE 10MHz + 1, // HIGH POLARITY + 1, // SECOND PHASE TRANSITION + 8 // 8 BITS + ); + + common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.cs, 1, DRIVE_MODE_PUSH_PULL); + common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.rst, 1, DRIVE_MODE_PUSH_PULL); + + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 0); + mp_hal_delay_us(10); // datasheet says 2us + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 1); + mp_hal_delay_ms(160); // datasheet says 150ms + + reg_wizchip_cris_cbfunc(wiz_cris_enter, wiz_cris_exit); + reg_wizchip_cs_cbfunc(wiz_cs_select, wiz_cs_deselect); + reg_wizchip_spi_cbfunc(wiz_spi_read, wiz_spi_write); + + uint8_t sn_size[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; // 2k buffer for each socket + ctlwizchip(CW_INIT_WIZCHIP, sn_size); + + // set some sensible default values; they are configurable using ifconfig method + wiz_NetInfo netinfo = { + .ip = {192, 168, 0, 18}, + .sn = {255, 255, 255, 0}, + .gw = {192, 168, 0, 1}, + .dns = {8, 8, 8, 8}, // Google public DNS + .dhcp = NETINFO_STATIC, + }; + create_random_mac_address(netinfo.mac); + ctlnetwork(CN_SET_NETINFO, (void*)&netinfo); + + // seems we need a small delay after init + mp_hal_delay_ms(250); + + // register with network module + network_module_register_nic(&wiznet5k_obj); + + // return wiznet5k object + return &wiznet5k_obj; +} + +/// \method regs() +/// Dump WIZNET5K registers. +STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { + //wiznet5k_obj_t *self = self_in; + printf("Wiz CREG:"); + for (int i = 0; i < 0x50; ++i) { + if (i % 16 == 0) { + printf("\n %04x:", i); + } + #if MICROPY_PY_WIZNET5K == 5200 + uint32_t reg = i; + #else + uint32_t reg = _W5500_IO_BASE_ | i << 8; + #endif + printf(" %02x", WIZCHIP_READ(reg)); + } + for (int sn = 0; sn < 4; ++sn) { + printf("\nWiz SREG[%d]:", sn); + for (int i = 0; i < 0x30; ++i) { + if (i % 16 == 0) { + printf("\n %04x:", i); + } + #if MICROPY_PY_WIZNET5K == 5200 + uint32_t reg = WIZCHIP_SREG_ADDR(sn, i); + #else + uint32_t reg = _W5500_IO_BASE_ | i << 8 | WIZCHIP_SREG_BLOCK(sn) << 3; + #endif + printf(" %02x", WIZCHIP_READ(reg)); + } + } + printf("\n"); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_regs_obj, wiznet5k_regs); + +STATIC mp_obj_t wiznet5k_connected_get_value(mp_obj_t self_in) { + (void)self_in; + return mp_obj_new_bool(wizphy_getphylink() == PHY_LINK_ON); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_connected_get_value_obj, wiznet5k_connected_get_value); + +//| attribute:: connected +//| +//| is this device physically connected? + +const mp_obj_property_t wiznet5k_connected_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&wiznet5k_connected_get_value_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +/// \method ifconfig([(ip, subnet, gateway, dns)]) +/// Get/set IP address, subnet mask, gateway and DNS. +STATIC mp_obj_t wiznet5k_ifconfig(size_t n_args, const mp_obj_t *args) { + wiz_NetInfo netinfo; + ctlnetwork(CN_GET_NETINFO, &netinfo); + if (n_args == 1) { + // get + mp_obj_t tuple[4] = { + netutils_format_ipv4_addr(netinfo.ip, NETUTILS_BIG), + netutils_format_ipv4_addr(netinfo.sn, NETUTILS_BIG), + netutils_format_ipv4_addr(netinfo.gw, NETUTILS_BIG), + netutils_format_ipv4_addr(netinfo.dns, NETUTILS_BIG), + }; + return mp_obj_new_tuple(4, tuple); + } else { + // set + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 4, &items); + netutils_parse_ipv4_addr(items[0], netinfo.ip, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[1], netinfo.sn, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[2], netinfo.gw, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[3], netinfo.dns, NETUTILS_BIG); + ctlnetwork(CN_SET_NETINFO, &netinfo); + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k_ifconfig); + +STATIC const mp_rom_map_elem_t wiznet5k_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_regs), MP_ROM_PTR(&wiznet5k_regs_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wiznet5k_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&wiznet5k_connected_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(wiznet5k_locals_dict, wiznet5k_locals_dict_table); + +const mod_network_nic_type_t mod_network_nic_type_wiznet5k = { + .base = { + { &mp_type_type }, + .name = MP_QSTR_WIZNET5K, + .make_new = wiznet5k_make_new, + .locals_dict = (mp_obj_dict_t*)&wiznet5k_locals_dict, + }, + .gethostbyname = wiznet5k_gethostbyname, + .socket = wiznet5k_socket_socket, + .close = wiznet5k_socket_close, + .bind = wiznet5k_socket_bind, + .listen = wiznet5k_socket_listen, + .accept = wiznet5k_socket_accept, + .connect = wiznet5k_socket_connect, + .send = wiznet5k_socket_send, + .recv = wiznet5k_socket_recv, + .sendto = wiznet5k_socket_sendto, + .recvfrom = wiznet5k_socket_recvfrom, + .setsockopt = wiznet5k_socket_setsockopt, + .settimeout = wiznet5k_socket_settimeout, + .ioctl = wiznet5k_socket_ioctl, +}; + +#endif // MICROPY_PY_WIZNET5K diff --git a/shared-module/wiznet/wiznet5k.c b/shared-module/wiznet/wiznet5k.c new file mode 100644 index 000000000..e69de29bb -- cgit v1.2.3 From 9b36d33df142d4a3dd2c238799e227c05b537d95 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 9 Oct 2018 13:12:04 +1100 Subject: move random mac address function into network module --- shared-bindings/wiznet/wiznet5k.c | 18 +++------------- shared-module/network/__init__.c | 43 +++++++++++++++++++++++++++++++++++++++ shared-module/network/__init__.h | 28 +++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 shared-module/network/__init__.h (limited to 'shared-module') diff --git a/shared-bindings/wiznet/wiznet5k.c b/shared-bindings/wiznet/wiznet5k.c index 967e7d0f4..558867264 100644 --- a/shared-bindings/wiznet/wiznet5k.c +++ b/shared-bindings/wiznet/wiznet5k.c @@ -40,7 +40,8 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/digitalio/DriveMode.h" #include "shared-bindings/busio/SPI.h" -#include "shared-bindings/random/__init__.h" + +#include "shared-module/network/__init__.h" #if MICROPY_PY_WIZNET5K @@ -336,19 +337,6 @@ STATIC mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in) { } #endif -void create_random_mac_address(uint8_t *mac) { - uint32_t rb1 = shared_modules_random_getrandbits(24); - uint32_t rb2 = shared_modules_random_getrandbits(24); - // first octet has multicast bit (0) cleared and local bit (1) set - // everything else is just set randomly - mac[0] = ((uint8_t)(rb1 >> 16) & 0xfe) | 0x02; - mac[1] = (uint8_t)(rb1 >> 8); - mac[2] = (uint8_t)(rb1); - mac[3] = (uint8_t)(rb2 >> 16); - mac[4] = (uint8_t)(rb2 >> 8); - mac[5] = (uint8_t)(rb2); -} - /******************************************************************************/ // MicroPython bindings @@ -400,7 +388,7 @@ STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size .dns = {8, 8, 8, 8}, // Google public DNS .dhcp = NETINFO_STATIC, }; - create_random_mac_address(netinfo.mac); + network_module_create_random_mac_address(netinfo.mac); ctlnetwork(CN_SET_NETINFO, (void*)&netinfo); // seems we need a small delay after init diff --git a/shared-module/network/__init__.c b/shared-module/network/__init__.c index e69de29bb..7e3b7fd26 100644 --- a/shared-module/network/__init__.c +++ b/shared-module/network/__init__.c @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Nick Moore + * + * 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 "py/mphal.h" + +#include "shared-bindings/random/__init__.h" + +void network_module_create_random_mac_address(uint8_t *mac) { + uint32_t rb1 = shared_modules_random_getrandbits(24); + uint32_t rb2 = shared_modules_random_getrandbits(24); + // first octet has multicast bit (0) cleared and local bit (1) set + // everything else is just set randomly + mac[0] = ((uint8_t)(rb1 >> 16) & 0xfe) | 0x02; + mac[1] = (uint8_t)(rb1 >> 8); + mac[2] = (uint8_t)(rb1); + mac[3] = (uint8_t)(rb2 >> 16); + mac[4] = (uint8_t)(rb2 >> 8); + mac[5] = (uint8_t)(rb2); +} + diff --git a/shared-module/network/__init__.h b/shared-module/network/__init__.h new file mode 100644 index 000000000..13f800fb0 --- /dev/null +++ b/shared-module/network/__init__.h @@ -0,0 +1,28 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Nick Moore + * + * 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. + */ + +void network_module_create_random_mac_address(uint8_t *mac); + -- cgit v1.2.3 From 6da25c8893afedfc195327f597fd3b0b45fc65d8 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 9 Oct 2018 13:28:00 -0700 Subject: Rename stop to stop_voice in case we want stop to stop everything later. --- shared-bindings/audioio/Mixer.c | 16 ++++++++-------- shared-bindings/audioio/Mixer.h | 2 +- shared-module/audioio/Mixer.c | 5 +---- 3 files changed, 10 insertions(+), 13 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/audioio/Mixer.c b/shared-bindings/audioio/Mixer.c index d353daa78..dc7a12ebc 100644 --- a/shared-bindings/audioio/Mixer.c +++ b/shared-bindings/audioio/Mixer.c @@ -150,7 +150,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer___exit___obj, 4, 4, aud //| //| Sample must be an `audioio.WaveFile`, `audioio.Mixer` or `audioio.RawSample`. //| -//| If other samples are already playing, the encodings must match. +//| The sample must match the Mixer's encoding settings given in the constructor. //| STATIC mp_obj_t audioio_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_sample, ARG_voice, ARG_loop }; @@ -171,24 +171,24 @@ STATIC mp_obj_t audioio_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, } MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_play_obj, 1, audioio_mixer_obj_play); -//| .. method:: stop(voice=0) +//| .. method:: stop_voice(voice=0) //| -//| Stops playback and resets to the start of the sample on the given channel. +//| Stops playback of the sample on the given voice. //| -STATIC mp_obj_t audioio_mixer_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +STATIC mp_obj_t audioio_mixer_obj_stop_voice(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_voice }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, }; audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - common_hal_audioio_mixer_stop(self, args[ARG_voice].u_int); + common_hal_audioio_mixer_stop_voice(self, args[ARG_voice].u_int); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_obj, 1, audioio_mixer_obj_stop); +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_voice_obj, 1, audioio_mixer_obj_stop_voice); //| .. attribute:: playing //| @@ -233,7 +233,7 @@ STATIC const mp_rom_map_elem_t audioio_mixer_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixer___exit___obj) }, { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixer_play_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixer_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_voice), MP_ROM_PTR(&audioio_mixer_stop_voice_obj) }, // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixer_playing_obj) }, diff --git a/shared-bindings/audioio/Mixer.h b/shared-bindings/audioio/Mixer.h index ad1a9fe05..832072b8f 100644 --- a/shared-bindings/audioio/Mixer.h +++ b/shared-bindings/audioio/Mixer.h @@ -44,7 +44,7 @@ void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self); bool common_hal_audioio_mixer_deinited(audioio_mixer_obj_t* self); void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, uint8_t voice, bool loop); -void common_hal_audioio_mixer_stop(audioio_mixer_obj_t* self, uint8_t voice); +void common_hal_audioio_mixer_stop_voice(audioio_mixer_obj_t* self, uint8_t voice); bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self); uint32_t common_hal_audioio_mixer_get_sample_rate(audioio_mixer_obj_t* self); diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index 0709e2e42..8020a621e 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -110,7 +110,7 @@ void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, u voice->more_data = result == GET_BUFFER_MORE_DATA; } -void common_hal_audioio_mixer_stop(audioio_mixer_obj_t* self, uint8_t voice) { +void common_hal_audioio_mixer_stop_voice(audioio_mixer_obj_t* self, uint8_t voice) { self->voice[voice].sample = NULL; } @@ -131,8 +131,6 @@ void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, } } - #pragma GCC push_options - #pragma GCC optimize ("O0") uint32_t add8signed(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) return __QADD8(a, b); @@ -152,7 +150,6 @@ uint32_t add8signed(uint32_t a, uint32_t b) { return result; #endif } - #pragma GCC pop_options uint32_t add8unsigned(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) -- cgit v1.2.3 From 823ff779cacb2c940d1e9e742c66dd89223f97ad Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 11 Oct 2018 14:02:18 +1100 Subject: network module c api into shared-module --- main.c | 2 +- shared-bindings/network/__init__.c | 32 +------------------- shared-bindings/network/__init__.h | 55 +--------------------------------- shared-bindings/socket/__init__.c | 2 +- shared-bindings/wiznet/__init__.c | 2 +- shared-bindings/wiznet/wiznet5k.c | 2 +- shared-module/network/__init__.c | 37 ++++++++++++++++++++++- shared-module/network/__init__.h | 60 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 102 insertions(+), 90 deletions(-) (limited to 'shared-module') diff --git a/main.c b/main.c index f1c68d06a..c1379c571 100755 --- a/main.c +++ b/main.c @@ -55,7 +55,7 @@ #include "supervisor/serial.h" #ifdef MICROPY_PY_NETWORK -#include "shared-bindings/network/__init__.h" +#include "shared-module/network/__init__.h" #endif void do_str(const char *src, mp_parse_input_kind_t input_kind) { diff --git a/shared-bindings/network/__init__.c b/shared-bindings/network/__init__.c index f28d5265c..c5639b462 100644 --- a/shared-bindings/network/__init__.c +++ b/shared-bindings/network/__init__.c @@ -47,37 +47,7 @@ /// \module network - network configuration /// -/// This module provides network drivers and routing configuration. - -void network_module_init(void) { - mp_obj_list_init(&MP_STATE_PORT(mod_network_nic_list), 0); -} - -void network_module_deinit(void) { -} - -void network_module_register_nic(mp_obj_t nic) { - for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { - if (MP_STATE_PORT(mod_network_nic_list).items[i] == nic) { - // nic already registered - return; - } - } - // nic not registered so add to list - mp_obj_list_append(MP_OBJ_FROM_PTR(&MP_STATE_PORT(mod_network_nic_list)), nic); -} - -mp_obj_t network_module_find_nic(const uint8_t *ip) { - // find a NIC that is suited to given IP address - for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { - mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; - // TODO check IP suitability here - //mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); - return nic; - } - - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, translate("no available NIC"))); -} +/// This module provides a registry of configured NICs. STATIC mp_obj_t network_route(void) { return MP_OBJ_FROM_PTR(&MP_STATE_PORT(mod_network_nic_list)); diff --git a/shared-bindings/network/__init__.h b/shared-bindings/network/__init__.h index b3fd657b6..4fe5e75a3 100644 --- a/shared-bindings/network/__init__.h +++ b/shared-bindings/network/__init__.h @@ -26,59 +26,6 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_NETWORK___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_NETWORK___INIT___H -#define MOD_NETWORK_IPADDR_BUF_SIZE (4) - -#define MOD_NETWORK_AF_INET (2) -#define MOD_NETWORK_AF_INET6 (10) - -#define MOD_NETWORK_SOCK_STREAM (1) -#define MOD_NETWORK_SOCK_DGRAM (2) -#define MOD_NETWORK_SOCK_RAW (3) - -struct _mod_network_socket_obj_t; - -typedef struct _mod_network_nic_type_t { - mp_obj_type_t base; - - // API for non-socket operations - int (*gethostbyname)(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *ip_out); - - // API for socket operations; return -1 on error - int (*socket)(struct _mod_network_socket_obj_t *socket, int *_errno); - void (*close)(struct _mod_network_socket_obj_t *socket); - int (*bind)(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); - int (*listen)(struct _mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno); - int (*accept)(struct _mod_network_socket_obj_t *socket, struct _mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno); - int (*connect)(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); - mp_uint_t (*send)(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno); - mp_uint_t (*recv)(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno); - mp_uint_t (*sendto)(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno); - mp_uint_t (*recvfrom)(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno); - int (*setsockopt)(struct _mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno); - int (*settimeout)(struct _mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno); - int (*ioctl)(struct _mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno); -} mod_network_nic_type_t; - -typedef struct _mod_network_socket_obj_t { - mp_obj_base_t base; - mp_obj_t nic; - mod_network_nic_type_t *nic_type; - union { - struct { - uint8_t domain; - uint8_t type; - int8_t fileno; - } u_param; - mp_uint_t u_state; - }; -} mod_network_socket_obj_t; - -extern const mod_network_nic_type_t mod_network_nic_type_wiznet5k; -extern const mod_network_nic_type_t mod_network_nic_type_cc3k; - -void network_module_init(void); -void network_module_deinit(void); -void network_module_register_nic(mp_obj_t nic); -mp_obj_t network_module_find_nic(const uint8_t *ip); +// nothing #endif // MICROPY_INCLUDED_SHARED_BINDINGS_NETWORK___INIT___H diff --git a/shared-bindings/socket/__init__.c b/shared-bindings/socket/__init__.c index 85796c5c4..f6949b4bf 100644 --- a/shared-bindings/socket/__init__.c +++ b/shared-bindings/socket/__init__.c @@ -35,7 +35,7 @@ #include "py/mperrno.h" #include "lib/netutils/netutils.h" -#include "shared-bindings/network/__init__.h" +#include "shared-module/network/__init__.h" //| :mod:`socket` --- TCP, UDP and RAW socket support //| ================================================= diff --git a/shared-bindings/wiznet/__init__.c b/shared-bindings/wiznet/__init__.c index fbdac3885..342cb1052 100644 --- a/shared-bindings/wiznet/__init__.c +++ b/shared-bindings/wiznet/__init__.c @@ -33,7 +33,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "shared-bindings/network/__init__.h" +#include "shared-module/network/__init__.h" //| :mod:`wiznet` --- Support for WizNet hardware //| ============================================= diff --git a/shared-bindings/wiznet/wiznet5k.c b/shared-bindings/wiznet/wiznet5k.c index ccbd2b115..26e02fb67 100644 --- a/shared-bindings/wiznet/wiznet5k.c +++ b/shared-bindings/wiznet/wiznet5k.c @@ -36,7 +36,7 @@ #include "py/mphal.h" #include "lib/netutils/netutils.h" -#include "shared-bindings/network/__init__.h" +#include "shared-module/network/__init__.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/digitalio/DriveMode.h" #include "shared-bindings/busio/SPI.h" diff --git a/shared-module/network/__init__.c b/shared-module/network/__init__.c index 7e3b7fd26..6a955a0c4 100644 --- a/shared-module/network/__init__.c +++ b/shared-module/network/__init__.c @@ -24,10 +24,46 @@ * THE SOFTWARE. */ +#include "py/objlist.h" +#include "py/runtime.h" #include "py/mphal.h" +#include "py/mperrno.h" #include "shared-bindings/random/__init__.h" +// mod_network_nic_list needs to be declared in mpconfigport.h + + +void network_module_init(void) { + mp_obj_list_init(&MP_STATE_PORT(mod_network_nic_list), 0); +} + +void network_module_deinit(void) { +} + +void network_module_register_nic(mp_obj_t nic) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + if (MP_STATE_PORT(mod_network_nic_list).items[i] == nic) { + // nic already registered + return; + } + } + // nic not registered so add to list + mp_obj_list_append(MP_OBJ_FROM_PTR(&MP_STATE_PORT(mod_network_nic_list)), nic); +} + +mp_obj_t network_module_find_nic(const uint8_t *ip) { + // find a NIC that is suited to given IP address + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; + // TODO check IP suitability here + //mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); + return nic; + } + + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, translate("no available NIC"))); +} + void network_module_create_random_mac_address(uint8_t *mac) { uint32_t rb1 = shared_modules_random_getrandbits(24); uint32_t rb2 = shared_modules_random_getrandbits(24); @@ -40,4 +76,3 @@ void network_module_create_random_mac_address(uint8_t *mac) { mac[4] = (uint8_t)(rb2 >> 8); mac[5] = (uint8_t)(rb2); } - diff --git a/shared-module/network/__init__.h b/shared-module/network/__init__.h index 13f800fb0..ce5b4e6d3 100644 --- a/shared-module/network/__init__.h +++ b/shared-module/network/__init__.h @@ -3,6 +3,7 @@ * * The MIT License (MIT) * + * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2018 Nick Moore * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,3 +27,62 @@ void network_module_create_random_mac_address(uint8_t *mac); +#ifndef MICROPY_INCLUDED_SHARED_MODULE_NETWORK___INIT___H +#define MICROPY_INCLUDED_SHARED_MODULE_NETWORK___INIT___H + +#define MOD_NETWORK_IPADDR_BUF_SIZE (4) + +#define MOD_NETWORK_AF_INET (2) +#define MOD_NETWORK_AF_INET6 (10) + +#define MOD_NETWORK_SOCK_STREAM (1) +#define MOD_NETWORK_SOCK_DGRAM (2) +#define MOD_NETWORK_SOCK_RAW (3) + +struct _mod_network_socket_obj_t; + +typedef struct _mod_network_nic_type_t { + mp_obj_type_t base; + + // API for non-socket operations + int (*gethostbyname)(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *ip_out); + + // API for socket operations; return -1 on error + int (*socket)(struct _mod_network_socket_obj_t *socket, int *_errno); + void (*close)(struct _mod_network_socket_obj_t *socket); + int (*bind)(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); + int (*listen)(struct _mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno); + int (*accept)(struct _mod_network_socket_obj_t *socket, struct _mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno); + int (*connect)(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); + mp_uint_t (*send)(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno); + mp_uint_t (*recv)(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno); + mp_uint_t (*sendto)(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno); + mp_uint_t (*recvfrom)(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno); + int (*setsockopt)(struct _mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno); + int (*settimeout)(struct _mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno); + int (*ioctl)(struct _mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno); +} mod_network_nic_type_t; + +typedef struct _mod_network_socket_obj_t { + mp_obj_base_t base; + mp_obj_t nic; + mod_network_nic_type_t *nic_type; + union { + struct { + uint8_t domain; + uint8_t type; + int8_t fileno; + } u_param; + mp_uint_t u_state; + }; +} mod_network_socket_obj_t; + +extern const mod_network_nic_type_t mod_network_nic_type_wiznet5k; +extern const mod_network_nic_type_t mod_network_nic_type_cc3k; + +void network_module_init(void); +void network_module_deinit(void); +void network_module_register_nic(mp_obj_t nic); +mp_obj_t network_module_find_nic(const uint8_t *ip); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_NETWORK___INIT___H -- cgit v1.2.3 From bb239052ea5bf710da66db032de94e5760040090 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 11 Oct 2018 15:05:25 +1100 Subject: Split wiznet driver into shared-bindings vs shared-module --- shared-bindings/wiznet/wiznet5k.c | 415 +------------------------------------- shared-module/wiznet/wiznet5k.c | 413 +++++++++++++++++++++++++++++++++++++ shared-module/wiznet/wiznet5k.h | 63 ++++++ 3 files changed, 480 insertions(+), 411 deletions(-) create mode 100644 shared-module/wiznet/wiznet5k.h (limited to 'shared-module') diff --git a/shared-bindings/wiznet/wiznet5k.c b/shared-bindings/wiznet/wiznet5k.c index 26e02fb67..b407dc5cb 100644 --- a/shared-bindings/wiznet/wiznet5k.c +++ b/shared-bindings/wiznet/wiznet5k.c @@ -36,336 +36,15 @@ #include "py/mphal.h" #include "lib/netutils/netutils.h" -#include "shared-module/network/__init__.h" +#if MICROPY_PY_WIZNET5K + #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/digitalio/DriveMode.h" #include "shared-bindings/busio/SPI.h" #include "shared-module/network/__init__.h" +#include "shared-module/wiznet/wiznet5k.h" -#if MICROPY_PY_WIZNET5K - -#include "ethernet/wizchip_conf.h" -#include "ethernet/socket.h" -#include "internet/dns/dns.h" -#include "internet/dhcp/dhcp.h" - -//| .. currentmodule:: wiznet -//| -//| :class:`WIZNET5K` -- wrapper for Wiznet 5500 Ethernet interface -//| =============================================================== -//| -//| .. class:: WIZNET5K(spi, cs, rst) -//| -//| Create a new WIZNET5500 interface using the specified pins -//| - -typedef struct _wiznet5k_obj_t { - mp_obj_base_t base; - mp_uint_t cris_state; - busio_spi_obj_t *spi; - digitalio_digitalinout_obj_t cs; - digitalio_digitalinout_obj_t rst; - uint8_t socket_used; -} wiznet5k_obj_t; - -STATIC wiznet5k_obj_t wiznet5k_obj; - -STATIC void wiz_cris_enter(void) { - wiznet5k_obj.cris_state = MICROPY_BEGIN_ATOMIC_SECTION(); -} - -STATIC void wiz_cris_exit(void) { - MICROPY_END_ATOMIC_SECTION(wiznet5k_obj.cris_state); -} - -STATIC void wiz_cs_select(void) { - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 0); -} - -STATIC void wiz_cs_deselect(void) { - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 1); -} - -STATIC void wiz_spi_read(uint8_t *buf, uint32_t len) { - (void)common_hal_busio_spi_read(wiznet5k_obj.spi, buf, len, 0); -} - -STATIC void wiz_spi_write(const uint8_t *buf, uint32_t len) { - (void)common_hal_busio_spi_write(wiznet5k_obj.spi, buf, len); -} - -STATIC int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { - uint8_t dns_ip[MOD_NETWORK_IPADDR_BUF_SIZE] = {8, 8, 8, 8}; - uint8_t *buf = m_new(uint8_t, MAX_DNS_BUF_SIZE); - DNS_init(0, buf); - mp_int_t ret = DNS_run(dns_ip, (uint8_t*)name, out_ip); - m_del(uint8_t, buf, MAX_DNS_BUF_SIZE); - if (ret == 1) { - // success - return 0; - } else { - // failure - return -2; - } -} - -STATIC int wiznet5k_socket_socket(mod_network_socket_obj_t *socket, int *_errno) { - if (socket->u_param.domain != MOD_NETWORK_AF_INET) { - *_errno = MP_EAFNOSUPPORT; - return -1; - } - - switch (socket->u_param.type) { - case MOD_NETWORK_SOCK_STREAM: socket->u_param.type = Sn_MR_TCP; break; - case MOD_NETWORK_SOCK_DGRAM: socket->u_param.type = Sn_MR_UDP; break; - default: *_errno = MP_EINVAL; return -1; - } - - if (socket->u_param.fileno == -1) { - // get first unused socket number - for (mp_uint_t sn = 0; sn < _WIZCHIP_SOCK_NUM_; sn++) { - if ((wiznet5k_obj.socket_used & (1 << sn)) == 0) { - wiznet5k_obj.socket_used |= (1 << sn); - socket->u_param.fileno = sn; - break; - } - } - if (socket->u_param.fileno == -1) { - // too many open sockets - *_errno = MP_EMFILE; - return -1; - } - } - - // WIZNET does not have a concept of pure "open socket". You need to know - // if it's a server or client at the time of creation of the socket. - // So, we defer the open until we know what kind of socket we want. - - // use "domain" to indicate that this socket has not yet been opened - socket->u_param.domain = 0; - - return 0; -} - -STATIC void wiznet5k_socket_close(mod_network_socket_obj_t *socket) { - uint8_t sn = (uint8_t)socket->u_param.fileno; - if (sn < _WIZCHIP_SOCK_NUM_) { - wiznet5k_obj.socket_used &= ~(1 << sn); - WIZCHIP_EXPORT(close)(sn); - } -} - -STATIC int wiznet5k_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { - // open the socket in server mode (if port != 0) - mp_int_t ret = WIZCHIP_EXPORT(socket)(socket->u_param.fileno, socket->u_param.type, port, 0); - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - - // indicate that this socket has been opened - socket->u_param.domain = 1; - - // success - return 0; -} - -STATIC int wiznet5k_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) { - mp_int_t ret = WIZCHIP_EXPORT(listen)(socket->u_param.fileno); - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return 0; -} - -STATIC int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) { - for (;;) { - int sr = getSn_SR((uint8_t)socket->u_param.fileno); - if (sr == SOCK_ESTABLISHED) { - socket2->u_param = socket->u_param; - getSn_DIPR((uint8_t)socket2->u_param.fileno, ip); - *port = getSn_PORT(socket2->u_param.fileno); - - // WIZnet turns the listening socket into the client socket, so we - // need to re-bind and re-listen on another socket for the server. - // TODO handle errors, especially no-more-sockets error - socket->u_param.domain = MOD_NETWORK_AF_INET; - socket->u_param.fileno = -1; - int _errno2; - if (wiznet5k_socket_socket(socket, &_errno2) != 0) { - //printf("(bad resocket %d)\n", _errno2); - } else if (wiznet5k_socket_bind(socket, NULL, *port, &_errno2) != 0) { - //printf("(bad rebind %d)\n", _errno2); - } else if (wiznet5k_socket_listen(socket, 0, &_errno2) != 0) { - //printf("(bad relisten %d)\n", _errno2); - } - - return 0; - } - if (sr == SOCK_CLOSED || sr == SOCK_CLOSE_WAIT) { - wiznet5k_socket_close(socket); - *_errno = MP_ENOTCONN; // ?? - return -1; - } - mp_hal_delay_ms(1); - } -} - -STATIC int wiznet5k_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { - // use "bind" function to open the socket in client mode - if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { - return -1; - } - - // now connect - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(connect)(socket->u_param.fileno, ip, port); - MP_THREAD_GIL_ENTER(); - - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - - // success - return 0; -} - -STATIC mp_uint_t wiznet5k_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(send)(socket->u_param.fileno, (byte*)buf, len); - MP_THREAD_GIL_ENTER(); - - // TODO convert Wiz errno's to POSIX ones - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC mp_uint_t wiznet5k_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) { - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(recv)(socket->u_param.fileno, buf, len); - MP_THREAD_GIL_ENTER(); - - // TODO convert Wiz errno's to POSIX ones - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC mp_uint_t wiznet5k_socket_sendto(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { - if (socket->u_param.domain == 0) { - // socket not opened; use "bind" function to open the socket in client mode - if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { - return -1; - } - } - - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(sendto)(socket->u_param.fileno, (byte*)buf, len, ip, port); - MP_THREAD_GIL_ENTER(); - - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC mp_uint_t wiznet5k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { - uint16_t port2; - MP_THREAD_GIL_EXIT(); - mp_int_t ret = WIZCHIP_EXPORT(recvfrom)(socket->u_param.fileno, buf, len, ip, &port2); - MP_THREAD_GIL_ENTER(); - *port = port2; - if (ret < 0) { - wiznet5k_socket_close(socket); - *_errno = -ret; - return -1; - } - return ret; -} - -STATIC int wiznet5k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { - // TODO - *_errno = MP_EINVAL; - return -1; -} - -STATIC int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) { - // TODO - *_errno = MP_EINVAL; - return -1; - - /* - if (timeout_ms == 0) { - // set non-blocking mode - uint8_t arg = SOCK_IO_NONBLOCK; - WIZCHIP_EXPORT(ctlsocket)(socket->u_param.fileno, CS_SET_IOMODE, &arg); - } - */ -} - -STATIC int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { - if (request == MP_STREAM_POLL) { - int ret = 0; - if (arg & MP_STREAM_POLL_RD && getSn_RX_RSR(socket->u_param.fileno) != 0) { - ret |= MP_STREAM_POLL_RD; - } - if (arg & MP_STREAM_POLL_WR && getSn_TX_FSR(socket->u_param.fileno) != 0) { - ret |= MP_STREAM_POLL_WR; - } - return ret; - } else { - *_errno = MP_EINVAL; - return MP_STREAM_ERROR; - } -} - -#if 0 -STATIC void wiznet5k_socket_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { - wiznet5k_socket_obj_t *self = self_in; - print(env, "", self->sn, getSn_MR(self->sn)); -} - -STATIC mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in) { - mp_int_t ret = WIZCHIP_EXPORT(disconnect)(self->sn); - return 0; -} -#endif - -static void wiznet5k_try_dhcp(void) { - DHCP_INIT_BUFFER_TYPE dhcp_buf[DHCP_INIT_BUFFER_SIZE]; - - // Set up the socket to listen on UDP 68 before calling DHCP_init - WIZCHIP_EXPORT(socket)(0, MOD_NETWORK_SOCK_DGRAM, DHCP_CLIENT_PORT, 0); - DHCP_init(0, dhcp_buf); - - // try a few times for DHCP ... XXX this should be asynchronous. - for (int i=0; i<10; i++) { - DHCP_time_handler(); - int dhcp_state = DHCP_run(); - if (dhcp_state == DHCP_IP_LEASED || dhcp_state == DHCP_IP_CHANGED) break; - mp_hal_delay_ms(1000); - } - DHCP_stop(); - WIZCHIP_EXPORT(close)(0); -} - -/******************************************************************************/ -// MicroPython bindings /// \classmethod \constructor(spi, pin_cs, pin_rst) /// Create and return a WIZNET5K object. @@ -373,93 +52,8 @@ STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size // check arguments mp_arg_check_num(n_args, n_kw, 3, 3, false); - // init the wiznet5k object - wiznet5k_obj.base.type = (mp_obj_type_t*)&mod_network_nic_type_wiznet5k; - wiznet5k_obj.cris_state = 0; - wiznet5k_obj.spi = MP_OBJ_TO_PTR(args[0]); - common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.cs, args[1]); - common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.rst, args[2]); - wiznet5k_obj.socket_used = 0; - - /*!< SPI configuration */ - // XXX probably should check if the provided SPI is already configured, and - // if so skip configuration? - - common_hal_busio_spi_configure(wiznet5k_obj.spi, - 10000000, // BAUDRATE 10MHz - 1, // HIGH POLARITY - 1, // SECOND PHASE TRANSITION - 8 // 8 BITS - ); - - common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.cs, 1, DRIVE_MODE_PUSH_PULL); - common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.rst, 1, DRIVE_MODE_PUSH_PULL); - - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 0); - mp_hal_delay_us(10); // datasheet says 2us - common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 1); - mp_hal_delay_ms(160); // datasheet says 150ms - - reg_wizchip_cris_cbfunc(wiz_cris_enter, wiz_cris_exit); - reg_wizchip_cs_cbfunc(wiz_cs_select, wiz_cs_deselect); - reg_wizchip_spi_cbfunc(wiz_spi_read, wiz_spi_write); - - // 2k buffer for each socket - uint8_t sn_size[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; - ctlwizchip(CW_INIT_WIZCHIP, sn_size); - - wiz_NetInfo netinfo = { - .dhcp = NETINFO_DHCP, - }; - network_module_create_random_mac_address(netinfo.mac); - ctlnetwork(CN_SET_NETINFO, (void*)&netinfo); - - // seems we need a small delay after init - mp_hal_delay_ms(250); - - wiznet5k_try_dhcp(); - - // register with network module - network_module_register_nic(&wiznet5k_obj); - - // return wiznet5k object - return &wiznet5k_obj; -} - -/// \method regs() -/// Dump WIZNET5K registers. -STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { - //wiznet5k_obj_t *self = self_in; - printf("Wiz CREG:"); - for (int i = 0; i < 0x50; ++i) { - if (i % 16 == 0) { - printf("\n %04x:", i); - } - #if MICROPY_PY_WIZNET5K == 5200 - uint32_t reg = i; - #else - uint32_t reg = _W5500_IO_BASE_ | i << 8; - #endif - printf(" %02x", WIZCHIP_READ(reg)); - } - for (int sn = 0; sn < 4; ++sn) { - printf("\nWiz SREG[%d]:", sn); - for (int i = 0; i < 0x30; ++i) { - if (i % 16 == 0) { - printf("\n %04x:", i); - } - #if MICROPY_PY_WIZNET5K == 5200 - uint32_t reg = WIZCHIP_SREG_ADDR(sn, i); - #else - uint32_t reg = _W5500_IO_BASE_ | i << 8 | WIZCHIP_SREG_BLOCK(sn) << 3; - #endif - printf(" %02x", WIZCHIP_READ(reg)); - } - } - printf("\n"); - return mp_const_none; + return wiznet5k_create(args[0], args[1], args[2]); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_regs_obj, wiznet5k_regs); STATIC mp_obj_t wiznet5k_connected_get_value(mp_obj_t self_in) { (void)self_in; @@ -507,7 +101,6 @@ STATIC mp_obj_t wiznet5k_ifconfig(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k_ifconfig); STATIC const mp_rom_map_elem_t wiznet5k_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_regs), MP_ROM_PTR(&wiznet5k_regs_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wiznet5k_ifconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&wiznet5k_connected_obj) }, }; diff --git a/shared-module/wiznet/wiznet5k.c b/shared-module/wiznet/wiznet5k.c index e69de29bb..1ae73a515 100644 --- a/shared-module/wiznet/wiznet5k.c +++ b/shared-module/wiznet/wiznet5k.c @@ -0,0 +1,413 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 +#include +#include + +#include "py/objlist.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "lib/netutils/netutils.h" + +#if MICROPY_PY_WIZNET5K + +#include "shared-module/network/__init__.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/busio/SPI.h" + +#include "shared-module/network/__init__.h" + +#include "ethernet/wizchip_conf.h" +#include "ethernet/socket.h" +#include "internet/dns/dns.h" +#include "internet/dhcp/dhcp.h" + +//| .. currentmodule:: wiznet +//| +//| :class:`WIZNET5K` -- wrapper for Wiznet 5500 Ethernet interface +//| =============================================================== +//| +//| .. class:: WIZNET5K(spi, cs, rst) +//| +//| Create a new WIZNET5500 interface using the specified pins +//| + +typedef struct _wiznet5k_obj_t { + mp_obj_base_t base; + mp_uint_t cris_state; + busio_spi_obj_t *spi; + digitalio_digitalinout_obj_t cs; + digitalio_digitalinout_obj_t rst; + uint8_t socket_used; +} wiznet5k_obj_t; + +static wiznet5k_obj_t wiznet5k_obj; + +STATIC wiznet5k_obj_t wiznet5k_obj; + +STATIC void wiz_cris_enter(void) { + wiznet5k_obj.cris_state = MICROPY_BEGIN_ATOMIC_SECTION(); +} + +STATIC void wiz_cris_exit(void) { + MICROPY_END_ATOMIC_SECTION(wiznet5k_obj.cris_state); +} + +STATIC void wiz_cs_select(void) { + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 0); +} + +STATIC void wiz_cs_deselect(void) { + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.cs, 1); +} + +STATIC void wiz_spi_read(uint8_t *buf, uint32_t len) { + (void)common_hal_busio_spi_read(wiznet5k_obj.spi, buf, len, 0); +} + +STATIC void wiz_spi_write(const uint8_t *buf, uint32_t len) { + (void)common_hal_busio_spi_write(wiznet5k_obj.spi, buf, len); +} + +int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { + uint8_t dns_ip[MOD_NETWORK_IPADDR_BUF_SIZE] = {8, 8, 8, 8}; + uint8_t *buf = m_new(uint8_t, MAX_DNS_BUF_SIZE); + DNS_init(0, buf); + mp_int_t ret = DNS_run(dns_ip, (uint8_t*)name, out_ip); + m_del(uint8_t, buf, MAX_DNS_BUF_SIZE); + if (ret == 1) { + // success + return 0; + } else { + // failure + return -2; + } +} + +int wiznet5k_socket_socket(mod_network_socket_obj_t *socket, int *_errno) { + if (socket->u_param.domain != MOD_NETWORK_AF_INET) { + *_errno = MP_EAFNOSUPPORT; + return -1; + } + + switch (socket->u_param.type) { + case MOD_NETWORK_SOCK_STREAM: socket->u_param.type = Sn_MR_TCP; break; + case MOD_NETWORK_SOCK_DGRAM: socket->u_param.type = Sn_MR_UDP; break; + default: *_errno = MP_EINVAL; return -1; + } + + if (socket->u_param.fileno == -1) { + // get first unused socket number + for (mp_uint_t sn = 0; sn < _WIZCHIP_SOCK_NUM_; sn++) { + if ((wiznet5k_obj.socket_used & (1 << sn)) == 0) { + wiznet5k_obj.socket_used |= (1 << sn); + socket->u_param.fileno = sn; + break; + } + } + if (socket->u_param.fileno == -1) { + // too many open sockets + *_errno = MP_EMFILE; + return -1; + } + } + + // WIZNET does not have a concept of pure "open socket". You need to know + // if it's a server or client at the time of creation of the socket. + // So, we defer the open until we know what kind of socket we want. + + // use "domain" to indicate that this socket has not yet been opened + socket->u_param.domain = 0; + + return 0; +} + +void wiznet5k_socket_close(mod_network_socket_obj_t *socket) { + uint8_t sn = (uint8_t)socket->u_param.fileno; + if (sn < _WIZCHIP_SOCK_NUM_) { + wiznet5k_obj.socket_used &= ~(1 << sn); + WIZCHIP_EXPORT(close)(sn); + } +} + +int wiznet5k_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + // open the socket in server mode (if port != 0) + mp_int_t ret = WIZCHIP_EXPORT(socket)(socket->u_param.fileno, socket->u_param.type, port, 0); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + + // indicate that this socket has been opened + socket->u_param.domain = 1; + + // success + return 0; +} + +int wiznet5k_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) { + mp_int_t ret = WIZCHIP_EXPORT(listen)(socket->u_param.fileno); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return 0; +} + +int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) { + for (;;) { + int sr = getSn_SR((uint8_t)socket->u_param.fileno); + if (sr == SOCK_ESTABLISHED) { + socket2->u_param = socket->u_param; + getSn_DIPR((uint8_t)socket2->u_param.fileno, ip); + *port = getSn_PORT(socket2->u_param.fileno); + + // WIZnet turns the listening socket into the client socket, so we + // need to re-bind and re-listen on another socket for the server. + // TODO handle errors, especially no-more-sockets error + socket->u_param.domain = MOD_NETWORK_AF_INET; + socket->u_param.fileno = -1; + int _errno2; + if (wiznet5k_socket_socket(socket, &_errno2) != 0) { + //printf("(bad resocket %d)\n", _errno2); + } else if (wiznet5k_socket_bind(socket, NULL, *port, &_errno2) != 0) { + //printf("(bad rebind %d)\n", _errno2); + } else if (wiznet5k_socket_listen(socket, 0, &_errno2) != 0) { + //printf("(bad relisten %d)\n", _errno2); + } + + return 0; + } + if (sr == SOCK_CLOSED || sr == SOCK_CLOSE_WAIT) { + wiznet5k_socket_close(socket); + *_errno = MP_ENOTCONN; // ?? + return -1; + } + mp_hal_delay_ms(1); + } +} + +int wiznet5k_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + // use "bind" function to open the socket in client mode + if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { + return -1; + } + + // now connect + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(connect)(socket->u_param.fileno, ip, port); + MP_THREAD_GIL_ENTER(); + + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + + // success + return 0; +} + +mp_uint_t wiznet5k_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(send)(socket->u_param.fileno, (byte*)buf, len); + MP_THREAD_GIL_ENTER(); + + // TODO convert Wiz errno's to POSIX ones + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +mp_uint_t wiznet5k_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) { + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(recv)(socket->u_param.fileno, buf, len); + MP_THREAD_GIL_ENTER(); + + // TODO convert Wiz errno's to POSIX ones + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +mp_uint_t wiznet5k_socket_sendto(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { + if (socket->u_param.domain == 0) { + // socket not opened; use "bind" function to open the socket in client mode + if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { + return -1; + } + } + + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(sendto)(socket->u_param.fileno, (byte*)buf, len, ip, port); + MP_THREAD_GIL_ENTER(); + + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +mp_uint_t wiznet5k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { + uint16_t port2; + MP_THREAD_GIL_EXIT(); + mp_int_t ret = WIZCHIP_EXPORT(recvfrom)(socket->u_param.fileno, buf, len, ip, &port2); + MP_THREAD_GIL_ENTER(); + *port = port2; + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +int wiznet5k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { + // TODO + *_errno = MP_EINVAL; + return -1; +} + +int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) { + // TODO + *_errno = MP_EINVAL; + return -1; + + /* + if (timeout_ms == 0) { + // set non-blocking mode + uint8_t arg = SOCK_IO_NONBLOCK; + WIZCHIP_EXPORT(ctlsocket)(socket->u_param.fileno, CS_SET_IOMODE, &arg); + } + */ +} + +int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { + if (request == MP_STREAM_POLL) { + int ret = 0; + if (arg & MP_STREAM_POLL_RD && getSn_RX_RSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_RD; + } + if (arg & MP_STREAM_POLL_WR && getSn_TX_FSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_WR; + } + return ret; + } else { + *_errno = MP_EINVAL; + return MP_STREAM_ERROR; + } +} + +static void wiznet5k_try_dhcp(void) { + DHCP_INIT_BUFFER_TYPE dhcp_buf[DHCP_INIT_BUFFER_SIZE]; + + // Set up the socket to listen on UDP 68 before calling DHCP_init + WIZCHIP_EXPORT(socket)(0, MOD_NETWORK_SOCK_DGRAM, DHCP_CLIENT_PORT, 0); + DHCP_init(0, dhcp_buf); + + // try a few times for DHCP ... XXX this should be asynchronous. + for (int i=0; i<10; i++) { + DHCP_time_handler(); + int dhcp_state = DHCP_run(); + if (dhcp_state == DHCP_IP_LEASED || dhcp_state == DHCP_IP_CHANGED) break; + mp_hal_delay_ms(1000); + } + DHCP_stop(); + WIZCHIP_EXPORT(close)(0); +} + +/// Create and return a WIZNET5K object. +mp_obj_t wiznet5k_create(mp_obj_t spi_in, mp_obj_t cs_in, mp_obj_t rst_in) { + + // init the wiznet5k object + wiznet5k_obj.base.type = (mp_obj_type_t*)&mod_network_nic_type_wiznet5k; + wiznet5k_obj.cris_state = 0; + wiznet5k_obj.spi = MP_OBJ_TO_PTR(spi_in); + common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.cs, cs_in); + common_hal_digitalio_digitalinout_construct(&wiznet5k_obj.rst, rst_in); + wiznet5k_obj.socket_used = 0; + + /*!< SPI configuration */ + // XXX probably should check if the provided SPI is already configured, and + // if so skip configuration? + + common_hal_busio_spi_configure(wiznet5k_obj.spi, + 10000000, // BAUDRATE 10MHz + 1, // HIGH POLARITY + 1, // SECOND PHASE TRANSITION + 8 // 8 BITS + ); + + common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.cs, 1, DRIVE_MODE_PUSH_PULL); + common_hal_digitalio_digitalinout_switch_to_output(&wiznet5k_obj.rst, 1, DRIVE_MODE_PUSH_PULL); + + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 0); + mp_hal_delay_us(10); // datasheet says 2us + common_hal_digitalio_digitalinout_set_value(&wiznet5k_obj.rst, 1); + mp_hal_delay_ms(160); // datasheet says 150ms + + reg_wizchip_cris_cbfunc(wiz_cris_enter, wiz_cris_exit); + reg_wizchip_cs_cbfunc(wiz_cs_select, wiz_cs_deselect); + reg_wizchip_spi_cbfunc(wiz_spi_read, wiz_spi_write); + + // 2k buffer for each socket + uint8_t sn_size[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; + ctlwizchip(CW_INIT_WIZCHIP, sn_size); + + wiz_NetInfo netinfo = { + .dhcp = NETINFO_DHCP, + }; + network_module_create_random_mac_address(netinfo.mac); + ctlnetwork(CN_SET_NETINFO, (void*)&netinfo); + + // seems we need a small delay after init + mp_hal_delay_ms(250); + + wiznet5k_try_dhcp(); + + // register with network module + network_module_register_nic(&wiznet5k_obj); + + // return wiznet5k object + return &wiznet5k_obj; +} + +#endif // MICROPY_PY_WIZNET5K diff --git a/shared-module/wiznet/wiznet5k.h b/shared-module/wiznet/wiznet5k.h new file mode 100644 index 000000000..f3e3dcfe1 --- /dev/null +++ b/shared-module/wiznet/wiznet5k.h @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_WIZNET_WIZNET5K_H +#define MICROPY_INCLUDED_SHARED_MODULE_WIZNET_WIZNET5K_H + +#include "ethernet/wizchip_conf.h" +#include "ethernet/socket.h" +#include "internet/dns/dns.h" +#include "internet/dhcp/dhcp.h" + +typedef struct _wiznet5k_obj_t { + mp_obj_base_t base; + mp_uint_t cris_state; + busio_spi_obj_t *spi; + digitalio_digitalinout_obj_t cs; + digitalio_digitalinout_obj_t rst; + uint8_t socket_used; +} wiznet5k_obj_t; + +int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip); +int wiznet5k_socket_socket(mod_network_socket_obj_t *socket, int *_errno); +void wiznet5k_socket_close(mod_network_socket_obj_t *socket); +int wiznet5k_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); +int wiznet5k_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno); +int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno); +int wiznet5k_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); +mp_uint_t wiznet5k_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno); +mp_uint_t wiznet5k_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno); +mp_uint_t wiznet5k_socket_sendto(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno); +mp_uint_t wiznet5k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno); +int wiznet5k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno); +int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno); +int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno); +mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in); +mp_obj_t wiznet5k_create(mp_obj_t spi_in, mp_obj_t cs_in, mp_obj_t rst_in); + +extern const mod_network_nic_type_t mod_network_nic_type_wiznet5k; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_WIZNET_WIZNET5K_H -- cgit v1.2.3 From a4a0cf826b9d39f14317e6021ac2a5c08d575fbd Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 11 Oct 2018 17:02:29 +1100 Subject: fix doc comments, translations again --- locale/circuitpython.pot | 2 +- locale/fr.po | 10 +++++----- shared-bindings/wiznet/wiznet5k.c | 11 +++++++++-- shared-module/wiznet/wiznet5k.c | 10 ---------- 4 files changed, 15 insertions(+), 18 deletions(-) (limited to 'shared-module') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index be88a16f5..6c2a10891 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-10-11 16:27+1100\n" +"POT-Creation-Date: 2018-10-11 17:02+1100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/locale/fr.po b/locale/fr.po index d6f572069..1658d90ce 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-10-11 16:27+1100\n" +"POT-Creation-Date: 2018-10-11 17:02+1100\n" "PO-Revision-Date: 2018-08-14 11:01+0200\n" "Last-Translator: Pierrick Couturier \n" "Language-Team: fr\n" @@ -2522,10 +2522,10 @@ msgstr "'S' et 'O' ne sont pas des types de format supportĂ©s" msgid "too many arguments provided with the given format" msgstr "trop d'arguments fournis avec ce format" -#, fuzzy -#~ msgid "value_size must be power of two" -#~ msgstr "'len' doit Ăªtre un multiple de 4" - #, fuzzy #~ msgid "palette must be displayio.Palette" #~ msgstr "la palette doit Ăªtre longue de 32 octets" + +#, fuzzy +#~ msgid "value_size must be power of two" +#~ msgstr "'len' doit Ăªtre un multiple de 4" diff --git a/shared-bindings/wiznet/wiznet5k.c b/shared-bindings/wiznet/wiznet5k.c index b407dc5cb..32f100969 100644 --- a/shared-bindings/wiznet/wiznet5k.c +++ b/shared-bindings/wiznet/wiznet5k.c @@ -45,9 +45,16 @@ #include "shared-module/network/__init__.h" #include "shared-module/wiznet/wiznet5k.h" +//| .. currentmodule:: wiznet +//| +//| :class:`WIZNET5K` -- wrapper for Wiznet 5500 Ethernet interface +//| =============================================================== +//| +//| .. class:: WIZNET5K(spi, cs, rst) +//| +//| Create a new WIZNET5500 interface using the specified pins +//| -/// \classmethod \constructor(spi, pin_cs, pin_rst) -/// Create and return a WIZNET5K object. STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 3, 3, false); diff --git a/shared-module/wiznet/wiznet5k.c b/shared-module/wiznet/wiznet5k.c index 1ae73a515..5c311bf49 100644 --- a/shared-module/wiznet/wiznet5k.c +++ b/shared-module/wiznet/wiznet5k.c @@ -50,16 +50,6 @@ #include "internet/dns/dns.h" #include "internet/dhcp/dhcp.h" -//| .. currentmodule:: wiznet -//| -//| :class:`WIZNET5K` -- wrapper for Wiznet 5500 Ethernet interface -//| =============================================================== -//| -//| .. class:: WIZNET5K(spi, cs, rst) -//| -//| Create a new WIZNET5500 interface using the specified pins -//| - typedef struct _wiznet5k_obj_t { mp_obj_base_t base; mp_uint_t cris_state; -- cgit v1.2.3 From 1f760bded8d0623c8ef5f846c3fcb4c6f13f0ce0 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 16 Oct 2018 23:07:58 +1100 Subject: header file cleanup for wiznet --- shared-module/wiznet/wiznet5k.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'shared-module') diff --git a/shared-module/wiznet/wiznet5k.c b/shared-module/wiznet/wiznet5k.c index 5c311bf49..150478190 100644 --- a/shared-module/wiznet/wiznet5k.c +++ b/shared-module/wiznet/wiznet5k.c @@ -50,16 +50,7 @@ #include "internet/dns/dns.h" #include "internet/dhcp/dhcp.h" -typedef struct _wiznet5k_obj_t { - mp_obj_base_t base; - mp_uint_t cris_state; - busio_spi_obj_t *spi; - digitalio_digitalinout_obj_t cs; - digitalio_digitalinout_obj_t rst; - uint8_t socket_used; -} wiznet5k_obj_t; - -static wiznet5k_obj_t wiznet5k_obj; +#include "shared-module/wiznet/wiznet5k.h" STATIC wiznet5k_obj_t wiznet5k_obj; -- cgit v1.2.3 From a15f3361aa0182cbefe0aec7376ff56bdcb74aa4 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 16 Oct 2018 23:09:25 +1100 Subject: add mechanism for timer ticks in NICs --- ports/atmel-samd/background.c | 4 ++++ shared-module/network/__init__.c | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'shared-module') diff --git a/ports/atmel-samd/background.c b/ports/atmel-samd/background.c index f25ec7200..385f0c284 100644 --- a/ports/atmel-samd/background.c +++ b/ports/atmel-samd/background.c @@ -31,6 +31,7 @@ #include "usb_mass_storage.h" #include "shared-module/displayio/__init__.h" +#include "shared-module/network/__init__.h" volatile uint64_t last_finished_tick = 0; @@ -41,6 +42,9 @@ void run_background_tasks(void) { #ifdef CIRCUITPY_DISPLAYIO displayio_refresh_display(); #endif + #ifdef MICROPY_PY_NETWORK + network_module_background(); + #endif usb_msc_background(); usb_cdc_background(); last_finished_tick = ticks_ms; diff --git a/shared-module/network/__init__.c b/shared-module/network/__init__.c index 6a955a0c4..a674e8478 100644 --- a/shared-module/network/__init__.c +++ b/shared-module/network/__init__.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include + #include "py/objlist.h" #include "py/runtime.h" #include "py/mphal.h" @@ -31,6 +33,8 @@ #include "shared-bindings/random/__init__.h" +#include "shared-module/network/__init__.h" + // mod_network_nic_list needs to be declared in mpconfigport.h @@ -41,6 +45,19 @@ void network_module_init(void) { void network_module_deinit(void) { } +void network_module_background(void) { + static uint32_t next_tick = 0; + uint32_t this_tick = ticks_ms; + if (this_tick < next_tick) return; + next_tick = this_tick + 1000; + + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; + mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); + if (nic_type->timer_tick != NULL) nic_type->timer_tick(nic); + } +} + void network_module_register_nic(mp_obj_t nic) { for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { if (MP_STATE_PORT(mod_network_nic_list).items[i] == nic) { -- cgit v1.2.3 From 45974978efa7e81a5a2a46bfb9f8d3cf016b1cb4 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 16 Oct 2018 23:09:55 +1100 Subject: fixup --- shared-module/network/__init__.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'shared-module') diff --git a/shared-module/network/__init__.h b/shared-module/network/__init__.h index ce5b4e6d3..00a3c3957 100644 --- a/shared-module/network/__init__.h +++ b/shared-module/network/__init__.h @@ -61,6 +61,7 @@ typedef struct _mod_network_nic_type_t { int (*setsockopt)(struct _mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno); int (*settimeout)(struct _mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno); int (*ioctl)(struct _mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno); + void (*timer_tick)(struct _mod_network_socket_obj_t *socket); } mod_network_nic_type_t; typedef struct _mod_network_socket_obj_t { @@ -82,6 +83,7 @@ extern const mod_network_nic_type_t mod_network_nic_type_cc3k; void network_module_init(void); void network_module_deinit(void); +void network_module_background(void); void network_module_register_nic(mp_obj_t nic); mp_obj_t network_module_find_nic(const uint8_t *ip); -- cgit v1.2.3 From 06894be29415fd182e82503e3a658be5df82cebd Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 16 Oct 2018 23:10:18 +1100 Subject: timer ticks for DHCP state machine for wiznet --- shared-bindings/wiznet/wiznet5k.c | 1 + shared-module/wiznet/wiznet5k.c | 38 ++++++++++++++++++++++++-------------- shared-module/wiznet/wiznet5k.h | 2 ++ 3 files changed, 27 insertions(+), 14 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/wiznet/wiznet5k.c b/shared-bindings/wiznet/wiznet5k.c index 1dba5f380..e1076bc15 100644 --- a/shared-bindings/wiznet/wiznet5k.c +++ b/shared-bindings/wiznet/wiznet5k.c @@ -146,6 +146,7 @@ const mod_network_nic_type_t mod_network_nic_type_wiznet5k = { .setsockopt = wiznet5k_socket_setsockopt, .settimeout = wiznet5k_socket_settimeout, .ioctl = wiznet5k_socket_ioctl, + .timer_tick = wiznet5k_socket_timer_tick, }; #endif // MICROPY_PY_WIZNET5K diff --git a/shared-module/wiznet/wiznet5k.c b/shared-module/wiznet/wiznet5k.c index 150478190..4bf536226 100644 --- a/shared-module/wiznet/wiznet5k.c +++ b/shared-module/wiznet/wiznet5k.c @@ -317,23 +317,33 @@ int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, m } } -static void wiznet5k_try_dhcp(void) { - DHCP_INIT_BUFFER_TYPE dhcp_buf[DHCP_INIT_BUFFER_SIZE]; +void wiznet5k_socket_timer_tick(mod_network_socket_obj_t *socket) { + if (wiznet5k_obj.dhcp_active) { + DHCP_time_handler(); + DHCP_run(); + } +} - // Set up the socket to listen on UDP 68 before calling DHCP_init - WIZCHIP_EXPORT(socket)(0, MOD_NETWORK_SOCK_DGRAM, DHCP_CLIENT_PORT, 0); - DHCP_init(0, dhcp_buf); +static void wiznet5k_start_dhcp(void) { + static DHCP_INIT_BUFFER_TYPE dhcp_buf[DHCP_INIT_BUFFER_SIZE]; - // try a few times for DHCP ... XXX this should be asynchronous. - for (int i=0; i<10; i++) { - DHCP_time_handler(); - int dhcp_state = DHCP_run(); - if (dhcp_state == DHCP_IP_LEASED || dhcp_state == DHCP_IP_CHANGED) break; - mp_hal_delay_ms(1000); + if (!wiznet5k_obj.dhcp_active) { + // Set up the socket to listen on UDP 68 before calling DHCP_init + WIZCHIP_EXPORT(socket)(0, MOD_NETWORK_SOCK_DGRAM, DHCP_CLIENT_PORT, 0); + DHCP_init(0, dhcp_buf); + wiznet5k_obj.dhcp_active = 1; + } +} + +#if 0 +static void wiznet5k_stop_dhcp(void) { + if (wiznet5k_obj.dhcp_active) { + wiznet5k_obj.dhcp_active = 0; + DHCP_stop(); + WIZCHIP_EXPORT(close)(0); } - DHCP_stop(); - WIZCHIP_EXPORT(close)(0); } +#endif /// Create and return a WIZNET5K object. mp_obj_t wiznet5k_create(mp_obj_t spi_in, mp_obj_t cs_in, mp_obj_t rst_in) { @@ -382,7 +392,7 @@ mp_obj_t wiznet5k_create(mp_obj_t spi_in, mp_obj_t cs_in, mp_obj_t rst_in) { // seems we need a small delay after init mp_hal_delay_ms(250); - wiznet5k_try_dhcp(); + wiznet5k_start_dhcp(); // register with network module network_module_register_nic(&wiznet5k_obj); diff --git a/shared-module/wiznet/wiznet5k.h b/shared-module/wiznet/wiznet5k.h index f3e3dcfe1..04b8872fb 100644 --- a/shared-module/wiznet/wiznet5k.h +++ b/shared-module/wiznet/wiznet5k.h @@ -39,6 +39,7 @@ typedef struct _wiznet5k_obj_t { digitalio_digitalinout_obj_t cs; digitalio_digitalinout_obj_t rst; uint8_t socket_used; + bool dhcp_active; } wiznet5k_obj_t; int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip); @@ -55,6 +56,7 @@ mp_uint_t wiznet5k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, int wiznet5k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno); int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno); int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno); +void wiznet5k_socket_timer_tick(mod_network_socket_obj_t *socket); mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in); mp_obj_t wiznet5k_create(mp_obj_t spi_in, mp_obj_t cs_in, mp_obj_t rst_in); -- cgit v1.2.3 From 345334aaf13dabbbb5fbfc1778fc34ed78907f42 Mon Sep 17 00:00:00 2001 From: arturo182 Date: Tue, 17 Jul 2018 11:44:55 +0200 Subject: bleio: Add a new Address class Use the new in the Adapter singleton. --- ports/nrf/Makefile | 1 + ports/nrf/common-hal/bleio/Adapter.c | 15 ++- ports/nrf/modules/ubluepy/modubluepy.h | 2 - ports/nrf/modules/ubluepy/ubluepy_constants.c | 3 - shared-bindings/bleio/Adapter.c | 15 +-- shared-bindings/bleio/Adapter.h | 4 +- shared-bindings/bleio/Address.c | 167 ++++++++++++++++++++++++++ shared-bindings/bleio/Address.h | 34 ++++++ shared-bindings/bleio/AddressType.c | 2 +- shared-bindings/bleio/__init__.c | 3 + shared-module/bleio/Address.h | 40 ++++++ 11 files changed, 260 insertions(+), 26 deletions(-) create mode 100644 shared-bindings/bleio/Address.c create mode 100644 shared-bindings/bleio/Address.h create mode 100644 shared-module/bleio/Address.h (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index aebd87048..2de7f24bd 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -185,6 +185,7 @@ SRC_BINDINGS_ENUMS = \ ifneq ($(SD), ) SRC_BINDINGS_ENUMS += \ + bleio/Address.c \ bleio/AddressType.c \ bleio/UUIDType.c endif diff --git a/ports/nrf/common-hal/bleio/Adapter.c b/ports/nrf/common-hal/bleio/Adapter.c index 52069e305..f4c35ac69 100644 --- a/ports/nrf/common-hal/bleio/Adapter.c +++ b/ports/nrf/common-hal/bleio/Adapter.c @@ -26,11 +26,12 @@ */ #include +#include #include "ble_drv.h" #include "nrfx.h" #include "nrf_error.h" -#include "py/misc.h" +#include "shared-module/bleio/Address.h" void common_hal_bleio_adapter_set_enabled(bool enabled) { if (enabled) { @@ -49,12 +50,10 @@ bool common_hal_bleio_adapter_get_enabled(void) { return ble_drv_stack_enabled(); } -void common_hal_bleio_adapter_get_address(vstr_t *vstr) { - ble_drv_addr_t address; - ble_drv_address_get(&address); +void common_hal_bleio_adapter_get_address(bleio_address_obj_t *address) { + ble_drv_addr_t drv_addr; + ble_drv_address_get(&drv_addr); - vstr_printf(vstr, ""HEX2_FMT":"HEX2_FMT":"HEX2_FMT":" \ - HEX2_FMT":"HEX2_FMT":"HEX2_FMT"", - address.addr[5], address.addr[4], address.addr[3], - address.addr[2], address.addr[1], address.addr[0]); + address->type = drv_addr.addr_type; + memcpy(address->value, drv_addr.addr, BLEIO_ADDRESS_BYTES); } diff --git a/ports/nrf/modules/ubluepy/modubluepy.h b/ports/nrf/modules/ubluepy/modubluepy.h index 0ca491634..c5fd7b6f8 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.h +++ b/ports/nrf/modules/ubluepy/modubluepy.h @@ -90,10 +90,8 @@ typedef enum { typedef enum { UBLUEPY_ADDR_TYPE_PUBLIC = 0, UBLUEPY_ADDR_TYPE_RANDOM_STATIC = 1, -#if 0 UBLUEPY_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE = 2, UBLUEPY_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE = 3, -#endif } ubluepy_addr_type_t; typedef enum { diff --git a/ports/nrf/modules/ubluepy/ubluepy_constants.c b/ports/nrf/modules/ubluepy/ubluepy_constants.c index 14e433e6e..e53a6f068 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_constants.c +++ b/ports/nrf/modules/ubluepy/ubluepy_constants.c @@ -82,9 +82,6 @@ STATIC const mp_rom_map_elem_t ubluepy_constants_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_EVT_GATTS_WRITE), MP_ROM_INT(80) }, { MP_ROM_QSTR(MP_QSTR_UUID_CCCD), MP_ROM_INT(0x2902) }, - { MP_ROM_QSTR(MP_QSTR_ADDR_TYPE_PUBLIC), MP_ROM_INT(UBLUEPY_ADDR_TYPE_PUBLIC) }, - { MP_ROM_QSTR(MP_QSTR_ADDR_TYPE_RANDOM_STATIC), MP_ROM_INT(UBLUEPY_ADDR_TYPE_RANDOM_STATIC) }, - { MP_ROM_QSTR(MP_QSTR_ad_types), MP_ROM_PTR(&ubluepy_constants_ad_types_type) }, }; diff --git a/shared-bindings/bleio/Adapter.c b/shared-bindings/bleio/Adapter.c index bfe87071d..d9449d3ab 100644 --- a/shared-bindings/bleio/Adapter.c +++ b/shared-bindings/bleio/Adapter.c @@ -25,6 +25,7 @@ */ #include "py/objproperty.h" +#include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/Adapter.h" //| .. currentmodule:: bleio @@ -57,8 +58,6 @@ //| MAC address of the BLE adapter. (read-only) //| -#define BLE_ADDRESS_LEN 17 - STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) { return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled()); } @@ -81,16 +80,12 @@ const mp_obj_property_t bleio_adapter_enabled_obj = { }; STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) { - vstr_t vstr; - vstr_init(&vstr, BLE_ADDRESS_LEN); - - common_hal_bleio_adapter_get_address(&vstr); - - const mp_obj_t mac_str = mp_obj_new_str(vstr.buf, vstr.len); + mp_obj_t obj = bleio_address_type.make_new(&bleio_address_type, 1, 0, mp_const_none); + bleio_address_obj_t *address = MP_OBJ_TO_PTR(obj); - vstr_clear(&vstr); + common_hal_bleio_adapter_get_address(address); - return mac_str; + return obj; } MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address); diff --git a/shared-bindings/bleio/Adapter.h b/shared-bindings/bleio/Adapter.h index 6c5d56948..fe3886e59 100644 --- a/shared-bindings/bleio/Adapter.h +++ b/shared-bindings/bleio/Adapter.h @@ -27,12 +27,12 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H -#include "py/obj.h" +#include "shared-module/bleio/Address.h" const mp_obj_type_t bleio_adapter_type; extern bool common_hal_bleio_adapter_get_enabled(void); extern void common_hal_bleio_adapter_set_enabled(bool enabled); -extern void common_hal_bleio_adapter_get_address(vstr_t *address); +extern void common_hal_bleio_adapter_get_address(bleio_address_obj_t *address); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H diff --git a/shared-bindings/bleio/Address.c b/shared-bindings/bleio/Address.c new file mode 100644 index 000000000..7ac42c740 --- /dev/null +++ b/shared-bindings/bleio/Address.c @@ -0,0 +1,167 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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 +#include + +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/Address.h" +#include "shared-module/bleio/Address.h" + +#define ADDRESS_LONG_LEN 17 // XX:XX:XX:XX:XX:XX +#define ADDRESS_SHORT_LEN 12 // XXXXXXXXXXXX + +//| .. currentmodule:: bleio +//| +//| :class:`Address` -- BLE address +//| ========================================================= +//| +//| Encapsulates the address of a BLE device. +//| + +//| .. class:: Address(address) +//| +//| Create a new Address object encapsulating the address value. +//| The value itself can be one of: +//| +//| - a `str` value in the format of 'XXXXXXXXXXXX' or 'XX:XX:XX:XX:XX' +//| - a `bytes` or `bytearray` containing 6 bytes +//| - another Address object +//| +//| :param address: The address to encapsulate +//| + +//| .. attribute:: type +//| +//| The address type. One of: +//| +//| - `bleio.AddressType.PUBLIC` +//| - `bleio.AddressType.RANDOM_STATIC` +//| - `bleio.AddressType.RANDOM_PRIVATE_RESOLVABLE` +//| - `bleio.AddressType.RANDOM_PRIVATE_NON_RESOLVABLE` +//| +STATIC mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, true); + bleio_address_obj_t *self = m_new_obj(bleio_address_obj_t); + self->base.type = &bleio_address_type; + self->type = ADDRESS_PUBLIC; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + enum { ARG_address }; + static const mp_arg_t allowed_args[] = { + { ARG_address, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_t address = args[ARG_address].u_obj; + + if (MP_OBJ_IS_STR(address)) { + GET_STR_DATA_LEN(address, str_data, str_len); + const bool is_long = (str_len == ADDRESS_LONG_LEN); + const bool is_short = (str_len == ADDRESS_SHORT_LEN); + + if (is_long || is_short) { + size_t i = str_len - 1; + for (size_t b = 0; b < BLEIO_ADDRESS_BYTES; ++b) { + self->value[b] = unichar_xdigit_value(str_data[i]) | + unichar_xdigit_value(str_data[i - 1]) << 4; + + i -= is_long ? 3 : 2; + } + } else { + mp_raise_ValueError("Wrong address length"); + } + } else if (MP_OBJ_IS_TYPE(address, &mp_type_bytearray) || MP_OBJ_IS_TYPE(address, &mp_type_bytes)) { + mp_buffer_info_t buf_info; + mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); + if (buf_info.len != BLEIO_ADDRESS_BYTES) { + mp_raise_ValueError("Wrong number of bytes provided"); + } + + for (size_t b = 0; b < BLEIO_ADDRESS_BYTES; ++b) { + self->value[BLEIO_ADDRESS_BYTES - b - 1] = ((uint8_t*)buf_info.buf)[b]; + } + } else if (MP_OBJ_IS_TYPE(address, &bleio_address_type)) { + // deep copy + bleio_address_obj_t *other = MP_OBJ_TO_PTR(address); + self->type = other->type; + memcpy(self->value, other->value, BLEIO_ADDRESS_BYTES); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_printf(print, "Address('"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT"')", + self->value[5], self->value[4], self->value[3], + self->value[2], self->value[1], self->value[0]); +} + +STATIC mp_obj_t bleio_address_get_type(mp_obj_t self_in) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->type == ADDRESS_PUBLIC) { + return (mp_obj_t)&bleio_addresstype_public_obj; + } else if (self->type == ADDRESS_RANDOM_STATIC) { + return (mp_obj_t)&bleio_addresstype_random_static_obj; + } else if (self->type == ADDRESS_RANDOM_PRIVATE_RESOLVABLE) { + return (mp_obj_t)&bleio_addresstype_random_private_resolvable_obj; + } else if (self->type == ADDRESS_RANDOM_PRIVATE_NON_RESOLVABLE) { + return (mp_obj_t)&bleio_addresstype_random_private_non_resolvable_obj; + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_type_obj, bleio_address_get_type); + +const mp_obj_property_t bleio_address_type_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&bleio_address_get_type_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t bleio_address_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&bleio_address_type_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_address_locals_dict, bleio_address_locals_dict_table); + +const mp_obj_type_t bleio_address_type = { + { &mp_type_type }, + .name = MP_QSTR_Address, + .print = bleio_address_print, + .make_new = bleio_address_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_address_locals_dict +}; diff --git a/shared-bindings/bleio/Address.h b/shared-bindings/bleio/Address.h new file mode 100644 index 000000000..9c9e20968 --- /dev/null +++ b/shared-bindings/bleio/Address.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H + +#include "py/objtype.h" + +extern const mp_obj_type_t bleio_address_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H diff --git a/shared-bindings/bleio/AddressType.c b/shared-bindings/bleio/AddressType.c index 4aef9feed..cf2151c94 100644 --- a/shared-bindings/bleio/AddressType.c +++ b/shared-bindings/bleio/AddressType.c @@ -33,7 +33,7 @@ //| //| .. class:: bleio.AddressType //| -//| Enum-like class to define the type of a BLE address. +//| Enum-like class to define the type of a BLE address, see also `bleio.Address`. //| //| .. data:: PUBLIC //| diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index a343d2f05..570b71798 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -27,6 +27,7 @@ #include "py/obj.h" #include "shared-bindings/bleio/__init__.h" +#include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/AddressType.h" #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/UUID.h" @@ -46,6 +47,7 @@ //| .. toctree:: //| :maxdepth: 3 //| +//| Address //| AddressType //| Adapter //| Descriptor @@ -61,6 +63,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) }, + { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, { MP_ROM_QSTR(MP_QSTR_AddressType), MP_ROM_PTR(&bleio_addresstype_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, { MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, diff --git a/shared-module/bleio/Address.h b/shared-module/bleio/Address.h new file mode 100644 index 000000000..a520b8dd6 --- /dev/null +++ b/shared-module/bleio/Address.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADDRESS_H +#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADDRESS_H + +#include "shared-bindings/bleio/AddressType.h" + +#define BLEIO_ADDRESS_BYTES 6 + +typedef struct { + mp_obj_base_t base; + bleio_address_type_t type; + uint8_t value[BLEIO_ADDRESS_BYTES]; +} bleio_address_obj_t; + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADDRESS_H -- cgit v1.2.3 From 7390dc7dab2c5ccd34a1fa5a33e1e5241b8f9bd7 Mon Sep 17 00:00:00 2001 From: arturo182 Date: Tue, 17 Jul 2018 17:00:37 +0200 Subject: bleio: Move ScanEntry to shared module and add a new AdvertisementData class --- ports/nrf/Makefile | 3 +- ports/nrf/drivers/bluetooth/ble_drv.c | 12 + ports/nrf/drivers/bluetooth/ble_drv.h | 2 + ports/nrf/modules/ubluepy/modubluepy.c | 2 - ports/nrf/modules/ubluepy/modubluepy.h | 10 - ports/nrf/modules/ubluepy/ubluepy_constants.c | 47 ---- ports/nrf/modules/ubluepy/ubluepy_scan_entry.c | 146 ------------ ports/nrf/modules/ubluepy/ubluepy_scanner.c | 26 +-- shared-bindings/bleio/AdvertisementData.c | 92 ++++++++ shared-bindings/bleio/AdvertisementData.h | 34 +++ shared-bindings/bleio/ScanEntry.c | 307 +++++++++++++++++++++++++ shared-bindings/bleio/ScanEntry.h | 44 ++++ shared-bindings/bleio/__init__.c | 20 +- shared-module/bleio/AdvertisementData.h | 76 ++++++ 14 files changed, 592 insertions(+), 229 deletions(-) delete mode 100644 ports/nrf/modules/ubluepy/ubluepy_scan_entry.c create mode 100644 shared-bindings/bleio/AdvertisementData.c create mode 100644 shared-bindings/bleio/AdvertisementData.h create mode 100644 shared-bindings/bleio/ScanEntry.c create mode 100644 shared-bindings/bleio/ScanEntry.h create mode 100644 shared-module/bleio/AdvertisementData.h (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 2de7f24bd..c5e0df02f 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -135,7 +135,6 @@ DRIVERS_SRC_C += $(addprefix modules/,\ ubluepy/ubluepy_delegate.c \ ubluepy/ubluepy_constants.c \ ubluepy/ubluepy_scanner.c \ - ubluepy/ubluepy_scan_entry.c \ ) SRC_COMMON_HAL += \ @@ -187,6 +186,8 @@ ifneq ($(SD), ) SRC_BINDINGS_ENUMS += \ bleio/Address.c \ bleio/AddressType.c \ + bleio/AdvertisementData.c \ + bleio/ScanEntry.c \ bleio/UUIDType.c endif diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 81d2a8ece..766660828 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -785,6 +785,18 @@ void ble_drv_scan_start(void) { } } +void ble_drv_scan_continue(void) { + SD_TEST_OR_ENABLE(); + +#if (BLUETOOTH_SD == 140) + uint32_t err_code; + if ((err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer)) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not continue scanning. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } +#endif +} + void ble_drv_scan_stop(void) { sd_ble_gap_scan_stop(); } diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h index d8b715467..ca9a38fdd 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.h +++ b/ports/nrf/drivers/bluetooth/ble_drv.h @@ -108,6 +108,8 @@ void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, u void ble_drv_scan_start(void); +void ble_drv_scan_continue(void); + void ble_drv_scan_stop(void); void ble_drv_adv_report_handler_set(mp_obj_t obj, ble_drv_adv_evt_callback_t evt_handler); diff --git a/ports/nrf/modules/ubluepy/modubluepy.c b/ports/nrf/modules/ubluepy/modubluepy.c index 0c02b22f2..034cc806c 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.c +++ b/ports/nrf/modules/ubluepy/modubluepy.c @@ -34,7 +34,6 @@ extern const mp_obj_type_t ubluepy_characteristic_type; extern const mp_obj_type_t ubluepy_delegate_type; extern const mp_obj_type_t ubluepy_constants_type; extern const mp_obj_type_t ubluepy_scanner_type; -extern const mp_obj_type_t ubluepy_scan_entry_type; STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubluepy) }, @@ -46,7 +45,6 @@ STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { #endif #if MICROPY_PY_UBLUEPY_CENTRAL { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&ubluepy_scanner_type) }, - { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&ubluepy_scan_entry_type) }, #endif { MP_ROM_QSTR(MP_QSTR_DefaultDelegate), MP_ROM_PTR(&ubluepy_delegate_type) }, { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&ubluepy_service_type) }, diff --git a/ports/nrf/modules/ubluepy/modubluepy.h b/ports/nrf/modules/ubluepy/modubluepy.h index c5fd7b6f8..8fb9fadd0 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.h +++ b/ports/nrf/modules/ubluepy/modubluepy.h @@ -78,7 +78,6 @@ extern const mp_obj_type_t ubluepy_service_type; extern const mp_obj_type_t ubluepy_characteristic_type; extern const mp_obj_type_t ubluepy_peripheral_type; extern const mp_obj_type_t ubluepy_scanner_type; -extern const mp_obj_type_t ubluepy_scan_entry_type; extern const mp_obj_type_t ubluepy_constants_type; extern const mp_obj_type_t ubluepy_constants_ad_types_type; @@ -153,15 +152,6 @@ typedef struct _ubluepy_scanner_obj_t { mp_obj_t adv_reports; } ubluepy_scanner_obj_t; -typedef struct _ubluepy_scan_entry_obj_t { - mp_obj_base_t base; - mp_obj_t addr; - uint8_t addr_type; - bool connectable; - int8_t rssi; - mp_obj_t data; -} ubluepy_scan_entry_obj_t; - typedef enum _ubluepy_prop_t { UBLUEPY_PROP_BROADCAST = 0x01, UBLUEPY_PROP_READ = 0x02, diff --git a/ports/nrf/modules/ubluepy/ubluepy_constants.c b/ports/nrf/modules/ubluepy/ubluepy_constants.c index e53a6f068..b28bd3828 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_constants.c +++ b/ports/nrf/modules/ubluepy/ubluepy_constants.c @@ -31,58 +31,11 @@ #include "modubluepy.h" -STATIC const mp_rom_map_elem_t ubluepy_constants_ad_types_locals_dict_table[] = { - // GAP AD Types - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_FLAGS), MP_ROM_INT(0x01) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE), MP_ROM_INT(0x02) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE), MP_ROM_INT(0x03) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE), MP_ROM_INT(0x04) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE), MP_ROM_INT(0x05) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE), MP_ROM_INT(0x06) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE), MP_ROM_INT(0x07) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SHORT_LOCAL_NAME), MP_ROM_INT(0x08) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_COMPLETE_LOCAL_NAME), MP_ROM_INT(0x09) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_TX_POWER_LEVEL), MP_ROM_INT(0x0A) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_CLASS_OF_DEVICE), MP_ROM_INT(0x0D) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SIMPLE_PAIRING_HASH_C), MP_ROM_INT(0x0E) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R), MP_ROM_INT(0x0F) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SECURITY_MANAGER_TK_VALUE), MP_ROM_INT(0x10) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS), MP_ROM_INT(0x11) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE), MP_ROM_INT(0x12) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT), MP_ROM_INT(0x14) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT), MP_ROM_INT(0x15) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SERVICE_DATA), MP_ROM_INT(0x16) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_PUBLIC_TARGET_ADDRESS), MP_ROM_INT(0x17) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_RANDOM_TARGET_ADDRESS), MP_ROM_INT(0x18) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_APPEARANCE), MP_ROM_INT(0x19) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_ADVERTISING_INTERVAL), MP_ROM_INT(0x1A) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS), MP_ROM_INT(0x1B) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_LE_ROLE), MP_ROM_INT(0x1C) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SIMPLE_PAIRING_HASH_C256), MP_ROM_INT(0x1D) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256), MP_ROM_INT(0x1E) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SERVICE_DATA_32BIT_UUID), MP_ROM_INT(0x20) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_SERVICE_DATA_128BIT_UUID), MP_ROM_INT(0x21) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_URI), MP_ROM_INT(0x24) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_3D_INFORMATION_DATA), MP_ROM_INT(0x3D) }, - { MP_ROM_QSTR(MP_QSTR_AD_TYPE_MANUFACTURER_SPECIFIC_DATA), MP_ROM_INT(0xFF) }, -}; - -STATIC MP_DEFINE_CONST_DICT(ubluepy_constants_ad_types_locals_dict, ubluepy_constants_ad_types_locals_dict_table); - -const mp_obj_type_t ubluepy_constants_ad_types_type = { - { &mp_type_type }, - .name = MP_QSTR_ad_types, - .locals_dict = (mp_obj_dict_t*)&ubluepy_constants_ad_types_locals_dict -}; - STATIC const mp_rom_map_elem_t ubluepy_constants_locals_dict_table[] = { // GAP events { MP_ROM_QSTR(MP_QSTR_EVT_GAP_CONNECTED), MP_ROM_INT(16) }, { MP_ROM_QSTR(MP_QSTR_EVT_GAP_DISCONNECTED), MP_ROM_INT(17) }, { MP_ROM_QSTR(MP_QSTR_EVT_GATTS_WRITE), MP_ROM_INT(80) }, - { MP_ROM_QSTR(MP_QSTR_UUID_CCCD), MP_ROM_INT(0x2902) }, - - { MP_ROM_QSTR(MP_QSTR_ad_types), MP_ROM_PTR(&ubluepy_constants_ad_types_type) }, }; STATIC MP_DEFINE_CONST_DICT(ubluepy_constants_locals_dict, ubluepy_constants_locals_dict_table); diff --git a/ports/nrf/modules/ubluepy/ubluepy_scan_entry.c b/ports/nrf/modules/ubluepy/ubluepy_scan_entry.c deleted file mode 100644 index 773070b08..000000000 --- a/ports/nrf/modules/ubluepy/ubluepy_scan_entry.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 -#include "py/obj.h" -#include "py/runtime.h" -#include "py/objstr.h" -#include "py/objlist.h" -#include "py/objarray.h" -#include "py/objtuple.h" -#include "py/qstr.h" - -#if MICROPY_PY_UBLUEPY_CENTRAL - -#include "ble_drv.h" - -STATIC void ubluepy_scan_entry_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { - ubluepy_scan_entry_obj_t * self = (ubluepy_scan_entry_obj_t *)o; - (void)self; - mp_printf(print, "ScanEntry"); -} - -/// \method addr() -/// Return address as text string. -/// -STATIC mp_obj_t scan_entry_get_addr(mp_obj_t self_in) { - ubluepy_scan_entry_obj_t *self = MP_OBJ_TO_PTR(self_in); - return self->addr; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluepy_scan_entry_get_addr_obj, scan_entry_get_addr); - -/// \method addr_type() -/// Return address type value. -/// -STATIC mp_obj_t scan_entry_get_addr_type(mp_obj_t self_in) { - ubluepy_scan_entry_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(self->addr_type); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluepy_scan_entry_get_addr_type_obj, scan_entry_get_addr_type); - -/// \method rssi() -/// Return RSSI value. -/// -STATIC mp_obj_t scan_entry_get_rssi(mp_obj_t self_in) { - ubluepy_scan_entry_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(self->rssi); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluepy_scan_entry_get_rssi_obj, scan_entry_get_rssi); - -/// \method getScanData() -/// Return list of the scan data tupples (ad_type, description, value) -/// -STATIC mp_obj_t scan_entry_get_scan_data(mp_obj_t self_in) { - ubluepy_scan_entry_obj_t * self = MP_OBJ_TO_PTR(self_in); - - mp_obj_t retval_list = mp_obj_new_list(0, NULL); - - // TODO: check if self->data is set - mp_obj_array_t * data = MP_OBJ_TO_PTR(self->data); - - uint16_t byte_index = 0; - - while (byte_index < data->len) { - mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); - - uint8_t adv_item_len = ((uint8_t * )data->items)[byte_index]; - uint8_t adv_item_type = ((uint8_t * )data->items)[byte_index + 1]; - - mp_obj_t description = mp_const_none; - - mp_map_t *constant_map = mp_obj_dict_get_map(ubluepy_constants_ad_types_type.locals_dict); - mp_map_elem_t *ad_types_table = MP_OBJ_TO_PTR(constant_map->table); - - uint16_t num_of_elements = constant_map->used; - - for (uint16_t i = 0; i < num_of_elements; i++) { - mp_map_elem_t element = (mp_map_elem_t)*ad_types_table; - ad_types_table++; - uint16_t element_value = mp_obj_get_int(element.value); - - if (adv_item_type == element_value) { - qstr key_qstr = MP_OBJ_QSTR_VALUE(element.key); - const char * text = qstr_str(key_qstr); - size_t len = qstr_len(key_qstr); - - vstr_t vstr; - vstr_init(&vstr, len); - vstr_printf(&vstr, "%s", text); - description = mp_obj_new_str(vstr.buf, vstr.len); - vstr_clear(&vstr); - } - } - - t->items[0] = MP_OBJ_NEW_SMALL_INT(adv_item_type); - t->items[1] = description; - t->items[2] = mp_obj_new_bytearray(adv_item_len - 1, - &((uint8_t * )data->items)[byte_index + 2]); - mp_obj_list_append(retval_list, MP_OBJ_FROM_PTR(t)); - - byte_index += adv_item_len + 1; - } - - return retval_list; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_scan_entry_get_scan_data_obj, scan_entry_get_scan_data); - -STATIC const mp_rom_map_elem_t ubluepy_scan_entry_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_addr), MP_ROM_PTR(&bluepy_scan_entry_get_addr_obj) }, - { MP_ROM_QSTR(MP_QSTR_addr_type), MP_ROM_PTR(&bluepy_scan_entry_get_addr_type_obj) }, - { MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&bluepy_scan_entry_get_rssi_obj) }, - { MP_ROM_QSTR(MP_QSTR_getScanData), MP_ROM_PTR(&ubluepy_scan_entry_get_scan_data_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(ubluepy_scan_entry_locals_dict, ubluepy_scan_entry_locals_dict_table); - -const mp_obj_type_t ubluepy_scan_entry_type = { - { &mp_type_type }, - .name = MP_QSTR_ScanEntry, - .print = ubluepy_scan_entry_print, - .locals_dict = (mp_obj_dict_t*)&ubluepy_scan_entry_locals_dict -}; - -#endif // MICROPY_PY_UBLUEPY_CENTRAL diff --git a/ports/nrf/modules/ubluepy/ubluepy_scanner.c b/ports/nrf/modules/ubluepy/ubluepy_scanner.c index 1cde3d551..9d15037a5 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_scanner.c +++ b/ports/nrf/modules/ubluepy/ubluepy_scanner.c @@ -33,31 +33,25 @@ #if MICROPY_PY_UBLUEPY_CENTRAL +#include "shared-bindings/bleio/ScanEntry.h" #include "ble_drv.h" STATIC void adv_event_handler(mp_obj_t self_in, uint16_t event_id, ble_drv_adv_data_t * data) { ubluepy_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); - ubluepy_scan_entry_obj_t * item = m_new_obj(ubluepy_scan_entry_obj_t); - item->base.type = &ubluepy_scan_entry_type; + // TODO: Don't add new entry for each item, group by address and update + bleio_scanentry_obj_t *item = m_new_obj(bleio_scanentry_obj_t); + item->base.type = &bleio_scanentry_type; - vstr_t vstr; - vstr_init(&vstr, 17); + item->rssi = data->rssi; + item->data = mp_obj_new_bytearray(data->data_len, data->p_data); - vstr_printf(&vstr, ""HEX2_FMT":"HEX2_FMT":"HEX2_FMT":" \ - HEX2_FMT":"HEX2_FMT":"HEX2_FMT"", - data->p_peer_addr[5], data->p_peer_addr[4], data->p_peer_addr[3], - data->p_peer_addr[2], data->p_peer_addr[1], data->p_peer_addr[0]); - - item->addr = mp_obj_new_str(vstr.buf, vstr.len); - - vstr_clear(&vstr); - - item->addr_type = data->addr_type; - item->rssi = data->rssi; - item->data = mp_obj_new_bytearray(data->data_len, data->p_data); + item->address.type = data->addr_type; + memcpy(item->address.value, data->p_peer_addr, BLEIO_ADDRESS_BYTES); mp_obj_list_append(self->adv_reports, item); + + ble_drv_scan_continue(); } STATIC void ubluepy_scanner_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { diff --git a/shared-bindings/bleio/AdvertisementData.c b/shared-bindings/bleio/AdvertisementData.c new file mode 100644 index 000000000..545dd5c51 --- /dev/null +++ b/shared-bindings/bleio/AdvertisementData.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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 "py/obj.h" +#include "shared-module/bleio/AdvertisementData.h" + +//| .. currentmodule:: bleio +//| +//| :class:`AdvertisementData` -- data used during BLE advertising +//| ============================================================== +//| +//| Represents the data to be broadcast during BLE advertising. +//| + +// TODO: Implement constructor and methods + +STATIC const mp_rom_map_elem_t bleio_advertisementdata_locals_dict_table[] = { + // Static variables + { MP_ROM_QSTR(MP_QSTR_FLAGS), MP_ROM_INT(AdFlags) }, + { MP_ROM_QSTR(MP_QSTR_INCOMPLETE_LIST_OF_16BIT_SERVICE_CLASS_UUIDS), MP_ROM_INT(AdIncompleteListOf16BitServiceClassUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_COMPLETE_LIST_OF_16BIT_SERVICE_CLASS_UUIDS), MP_ROM_INT(AdCompleteListOf16BitServiceClassUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_INCOMPLETE_LIST_OF_32BIT_SERVICE_CLASS_UUIDS), MP_ROM_INT(AdIncompleteListOf32BitServiceClassUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_COMPLETE_LIST_OF_32BIT_SERVICE_CLASS_UUIDS), MP_ROM_INT(AdCompleteListOf32BitServiceClassUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_INCOMPLETE_LIST_OF_128BIT_SERVICE_CLASS_UUIDS), MP_ROM_INT(AdIncompleteListOf128BitServiceClassUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_COMPLETE_LIST_OF_128BIT_SERVICE_CLASS_UUIDS), MP_ROM_INT(AdCompleteListOf128BitServiceClassUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_SHORTENED_LOCAL_NAME), MP_ROM_INT(AdShortenedLocalName) }, + { MP_ROM_QSTR(MP_QSTR_COMPLETE_LOCAL_NAME), MP_ROM_INT(AdCompleteLocalName) }, + { MP_ROM_QSTR(MP_QSTR_TX_POWER_LEVEL), MP_ROM_INT(AdTxPowerLevel) }, + { MP_ROM_QSTR(MP_QSTR_CLASS_OF_DEVICE), MP_ROM_INT(AdClassOfDevice) }, + { MP_ROM_QSTR(MP_QSTR_SIMPLE_PAIRING_HASH_C), MP_ROM_INT(AdSimplePairingHashC) }, + { MP_ROM_QSTR(MP_QSTR_SIMPLE_PAIRING_RANDOMIZER_R), MP_ROM_INT(AdSimplePairingRandomizerR) }, + { MP_ROM_QSTR(MP_QSTR_SECURITY_MANAGER_TK_VALUE), MP_ROM_INT(AdSecurityManagerTKValue) }, + { MP_ROM_QSTR(MP_QSTR_SECURITY_MANAGER_OOB_FLAGS), MP_ROM_INT(AdSecurityManagerOOBFlags) }, + { MP_ROM_QSTR(MP_QSTR_SLAVE_CONNECTION_INTERVAL_RANGE), MP_ROM_INT(AdSlaveConnectionIntervalRange) }, + { MP_ROM_QSTR(MP_QSTR_LIST_OF_16BIT_SERVICE_SOLICITATION_UUIDS), MP_ROM_INT(AdListOf16BitServiceSolicitationUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_LIST_OF_128BIT_SERVICE_SOLICITATION_UUIDS), MP_ROM_INT(AdListOf128BitServiceSolicitationUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_SERVICE_DATA), MP_ROM_INT(AdServiceData) }, + { MP_ROM_QSTR(MP_QSTR_PUBLIC_TARGET_ADDRESS), MP_ROM_INT(AdPublicTargetAddress) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_TARGET_ADDRESS), MP_ROM_INT(AdRandomTargetAddress) }, + { MP_ROM_QSTR(MP_QSTR_APPEARANCE), MP_ROM_INT(AdAppearance) }, + { MP_ROM_QSTR(MP_QSTR_ADVERTISING_INTERNAL), MP_ROM_INT(AdAdvertisingInterval) }, + { MP_ROM_QSTR(MP_QSTR_LE_BLUETOOTH_DEVICE_ADDRESS), MP_ROM_INT(AdLEBluetoothDeviceAddress) }, + { MP_ROM_QSTR(MP_QSTR_LE_ROLE), MP_ROM_INT(AdLERole) }, + { MP_ROM_QSTR(MP_QSTR_SIMPLE_PAIRING_HASH_C256), MP_ROM_INT(AdSimplePairingHashC256) }, + { MP_ROM_QSTR(MP_QSTR_SIMPLE_PAIRING_RANDOMIZER_R256), MP_ROM_INT(AdSimplePairingRandomizerR256) }, + { MP_ROM_QSTR(MP_QSTR_LIST_OF_32BIT_SERVICE_SOLICITATION_UUIDS), MP_ROM_INT(AdListOf32BitServiceSolicitationUUIDs) }, + { MP_ROM_QSTR(MP_QSTR_SERVICE_DATA_32BIT_UUID), MP_ROM_INT(AdServiceData32BitUUID) }, + { MP_ROM_QSTR(MP_QSTR_SERVICE_DATA_128BIT_UUID), MP_ROM_INT(AdServiceData128BitUUID) }, + { MP_ROM_QSTR(MP_QSTR_LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE), MP_ROM_INT(AdLESecureConnectionsConfirmationValue) }, + { MP_ROM_QSTR(MP_QSTR_LE_SECURE_CONNECTIONS_RANDOM_VALUE), MP_ROM_INT(AdLESecureConnectionsRandomValue) }, + { MP_ROM_QSTR(MP_QSTR_URI), MP_ROM_INT(AdURI) }, + { MP_ROM_QSTR(MP_QSTR_INDOOR_POSITIONING), MP_ROM_INT(AdIndoorPositioning) }, + { MP_ROM_QSTR(MP_QSTR_TRANSPORT_DISCOVERY_DATA), MP_ROM_INT(AdTransportDiscoveryData) }, + { MP_ROM_QSTR(MP_QSTR_LE_SUPPORTED_FEATURES), MP_ROM_INT(AdLESupportedFeatures) }, + { MP_ROM_QSTR(MP_QSTR_CHANNEL_MAP_UPDATE_INDICATION), MP_ROM_INT(AdChannelMapUpdateIndication) }, + { MP_ROM_QSTR(MP_QSTR_PB_ADV), MP_ROM_INT(AdPBADV) }, + { MP_ROM_QSTR(MP_QSTR_MESH_MESSAGE), MP_ROM_INT(AdMeshMessage) }, + { MP_ROM_QSTR(MP_QSTR_MESH_BEACON), MP_ROM_INT(AdMeshBeacon) }, + { MP_ROM_QSTR(MP_QSTR_3D_INFORMATION_DATA), MP_ROM_INT(Ad3DInformationData) }, + { MP_ROM_QSTR(MP_QSTR_MANUFACTURER_SPECIFIC_DATA), MP_ROM_INT(AdManufacturerSpecificData) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_advertisementdata_locals_dict, bleio_advertisementdata_locals_dict_table); + +const mp_obj_type_t bleio_advertisementdata_type = { + { &mp_type_type }, + .name = MP_QSTR_AdvertisementData, + .locals_dict = (mp_obj_dict_t*)&bleio_advertisementdata_locals_dict +}; diff --git a/shared-bindings/bleio/AdvertisementData.h b/shared-bindings/bleio/AdvertisementData.h new file mode 100644 index 000000000..05b5a2c8d --- /dev/null +++ b/shared-bindings/bleio/AdvertisementData.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADVERTISEMENTDATA_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADVERTISEMENTDATA_H + +#include "py/obj.h" + +extern const mp_obj_type_t bleio_advertisementdata_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADVERTISEMENTDATA_H diff --git a/shared-bindings/bleio/ScanEntry.c b/shared-bindings/bleio/ScanEntry.c new file mode 100644 index 000000000..f41665581 --- /dev/null +++ b/shared-bindings/bleio/ScanEntry.c @@ -0,0 +1,307 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 + +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/objtuple.h" +#include "shared-bindings/bleio/Address.h" +#include "shared-module/bleio/AdvertisementData.h" +#include "shared-bindings/bleio/UUID.h" +#include "shared-bindings/bleio/ScanEntry.h" + +//| .. currentmodule:: bleio +//| +//| :class:`ScanEntry` -- BLE scan response entry +//| ========================================================= +//| +//| Encapsulates information about a device that was received as a +//| response to a BLE scan request. +//| + +//| .. attribute:: address +//| +//| The address of the device. (read-only) +//| This attribute is of type `bleio:Address`. +//| + +//| .. attribute:: manufacturer_specific_data +//| +//| The manufacturer-specific data present in the advertisement packet. (read-only) +//| + +//| .. attribute:: name +//| +//| The name of the device. (read-only) +//| This attribute might be `None` if the data was missing from the advertisement packet. +//| + +//| .. attribute:: raw_data +//| +//| All the advertisement data present in the packet. (read-only) +//| + +//| .. attribute:: rssi +//| +//| The signal strength of the device at the time of the scan. (read-only) +//| + +//| .. attribute:: service_uuids +//| +//| The address of the device. (read-only) +//| This attribute is a list of `bleio:UUID`. +//| This attribute might be empty or incomplete, depending on the advertisement packet. +//| Currently only 16-bit UUIDS are listed. +//| + +//| .. attribute:: tx_power_level +//| +//| The transmit power level of the device. (read-only) +//| This attribute might be `None` if the data was missing from the advertisement packet. +//| +static uint8_t find_data_item(mp_obj_array_t *data_in, uint8_t type, uint8_t **data_out) { + uint16_t i = 0; + while (i < data_in->len) { + const uint8_t item_len = ((uint8_t*)data_in->items)[i]; + const uint8_t item_type = ((uint8_t*)data_in->items)[i + 1]; + if (item_type != type) { + i += (item_len + 1); + continue; + } + + *data_out = &((uint8_t*)data_in->items)[i + 2]; + + return item_len; + } + + return 0; +} + +STATIC mp_obj_t scanentry_get_name(mp_obj_t self_in); + +STATIC void bleio_scanentry_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_scanentry_obj_t *self = (bleio_scanentry_obj_t *)self_in; + mp_printf(print, "ScanEntry(address: "HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT"", + self->address.value[5], self->address.value[4], self->address.value[3], + self->address.value[1], self->address.value[1], self->address.value[0]); + + const mp_obj_t name_obj = scanentry_get_name(self_in); + if (name_obj != mp_const_none) { + mp_obj_str_t *str = MP_OBJ_TO_PTR(name_obj); + mp_printf(print, " name: %s", str->data); + } + + mp_print_str(print, ")"); +} + +STATIC mp_obj_t bleio_scanentry_get_address(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_t obj = bleio_address_type.make_new(&bleio_address_type, 1, 0, (mp_obj_t)&mp_const_none_obj); + bleio_address_obj_t *address = MP_OBJ_TO_PTR(obj); + + address->type = self->address.type; + memcpy(address->value, self->address.value, BLEIO_ADDRESS_BYTES); + + return obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluepy_scanentry_get_address_obj, bleio_scanentry_get_address); + +const mp_obj_property_t bleio_scanentry_address_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bluepy_scanentry_get_address_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t scanentry_get_manufacturer_specific_data(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_array_t *data = MP_OBJ_TO_PTR(self->data); + uint8_t *manuf_data; + + const uint8_t manuf_data_len = find_data_item(data, AdManufacturerSpecificData, &manuf_data); + if (manuf_data_len == 0) { + return mp_const_none; + } + + return mp_obj_new_bytearray_by_ref(manuf_data_len, manuf_data); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(scanentry_get_manufacturer_specific_data_obj, scanentry_get_manufacturer_specific_data); + +const mp_obj_property_t bleio_scanentry_manufacturer_specific_data_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&scanentry_get_manufacturer_specific_data_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t scanentry_get_name(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_array_t *data = MP_OBJ_TO_PTR(self->data); + uint8_t *name; + + // Try for Complete but settle for Shortened + uint8_t name_len = find_data_item(data, AdCompleteLocalName, &name); + if (name_len == 0) { + name_len = find_data_item(data, AdShortenedLocalName, &name); + } + + if (name_len == 0) { + return mp_const_none; + } + + return mp_obj_new_str((const char*)name, name_len - 1, false); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluepy_scanentry_get_name_obj, scanentry_get_name); + +const mp_obj_property_t bleio_scanentry_name_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bluepy_scanentry_get_name_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t scanentry_get_raw_data(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_t entries = mp_obj_new_list(0, NULL); + + mp_obj_array_t *data = MP_OBJ_TO_PTR(self->data); + + uint16_t i = 0; + while (i < data->len) { + mp_obj_tuple_t *entry = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + + const uint8_t item_len = ((uint8_t*)data->items)[i]; + const uint8_t item_type = ((uint8_t*)data->items)[i + 1]; + + entry->items[0] = MP_OBJ_NEW_SMALL_INT(item_type); + entry->items[1] = mp_obj_new_bytearray(item_len - 1, &((uint8_t*)data->items)[i + 2]); + mp_obj_list_append(entries, MP_OBJ_FROM_PTR(entry)); + + i += (item_len + 1); + } + + return entries; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_raw_data_obj, scanentry_get_raw_data); + +const mp_obj_property_t bleio_scanentry_raw_data_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_scanentry_get_raw_data_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t scanentry_get_rssi(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_int(self->rssi); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluepy_scanentry_get_rssi_obj, scanentry_get_rssi); + +const mp_obj_property_t bleio_scanentry_rssi_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bluepy_scanentry_get_rssi_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t scanentry_get_service_uuids(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_array_t *data = MP_OBJ_TO_PTR(self->data); + uint8_t *uuids; + + // Try for Complete but settle for Incomplete + uint8_t uuids_len = find_data_item(data, AdCompleteListOf16BitServiceClassUUIDs, &uuids); + if (uuids_len == 0) { + uuids_len = find_data_item(data, AdIncompleteListOf16BitServiceClassUUIDs, &uuids); + } + + mp_obj_t entries = mp_obj_new_list(0, NULL); + for (size_t i = 0; i < uuids_len / sizeof(uint16_t); ++i) { + const mp_obj_t uuid_int = mp_obj_new_int(uuids[sizeof(uint16_t) * i] | (uuids[sizeof(uint16_t) * i + 1] << 8)); + const mp_obj_t uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &uuid_int); + + mp_obj_list_append(entries, uuid_obj); + } + + // TODO: 32-bit UUIDs + // TODO: 128-bit UUIDs + + return entries; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(scanentry_get_service_uuids_obj, scanentry_get_service_uuids); + +const mp_obj_property_t bleio_scanentry_service_uuids_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&scanentry_get_service_uuids_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t scanentry_get_tx_power_level(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_array_t *data = MP_OBJ_TO_PTR(self->data); + uint8_t *tx_power; + + const uint8_t tx_power_len = find_data_item(data, AdTxPowerLevel, &tx_power); + if (tx_power_len == 0) { + return mp_const_none; + } + + return mp_obj_new_int((int8_t)(*tx_power)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(scanentry_get_tx_power_level_obj, scanentry_get_tx_power_level); + +const mp_obj_property_t bleio_scanentry_tx_power_level_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&scanentry_get_tx_power_level_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_scanentry_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_scanentry_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_manufacturer_specific_data), MP_ROM_PTR(&bleio_scanentry_manufacturer_specific_data_obj) }, + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_scanentry_name_obj) }, + { MP_ROM_QSTR(MP_QSTR_raw_data), MP_ROM_PTR(&bleio_scanentry_raw_data_obj) }, + { MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&bleio_scanentry_rssi_obj) }, + { MP_ROM_QSTR(MP_QSTR_service_uuids), MP_ROM_PTR(&bleio_scanentry_service_uuids_obj) }, + { MP_ROM_QSTR(MP_QSTR_tx_power_level), MP_ROM_PTR(&bleio_scanentry_tx_power_level_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_scanentry_locals_dict, bleio_scanentry_locals_dict_table); + +const mp_obj_type_t bleio_scanentry_type = { + { &mp_type_type }, + .name = MP_QSTR_ScanEntry, + .print = bleio_scanentry_print, + .locals_dict = (mp_obj_dict_t*)&bleio_scanentry_locals_dict +}; diff --git a/shared-bindings/bleio/ScanEntry.h b/shared-bindings/bleio/ScanEntry.h new file mode 100644 index 000000000..4a201124e --- /dev/null +++ b/shared-bindings/bleio/ScanEntry.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H + +#include "shared-module/bleio/Address.h" +#include "py/objtype.h" + +typedef struct { + mp_obj_base_t base; + bleio_address_obj_t address; + bool connectable; + int8_t rssi; + mp_obj_t data; +} bleio_scanentry_obj_t; + +extern const mp_obj_type_t bleio_scanentry_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index 570b71798..315b2c32c 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -29,7 +29,9 @@ #include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/AddressType.h" +#include "shared-bindings/bleio/AdvertisementData.h" #include "shared-bindings/bleio/Descriptor.h" +#include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/UUID.h" #include "shared-bindings/bleio/UUIDType.h" @@ -49,8 +51,10 @@ //| //| Address //| AddressType +//| AdvertisementData //| Adapter //| Descriptor +//| ScanEntry //| UUID //| UUIDType //| @@ -62,17 +66,19 @@ //| STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) }, - { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, - { MP_ROM_QSTR(MP_QSTR_AddressType), MP_ROM_PTR(&bleio_addresstype_type) }, - { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, - { MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) }, + { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, + { MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) }, + { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, + { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, + { MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, // Properties - { MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) }, + { MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) }, // Enum-like Classes. - { MP_ROM_QSTR(MP_QSTR_UUIDType), MP_ROM_PTR(&bleio_uuidtype_type) }, + { MP_ROM_QSTR(MP_QSTR_AddressType), MP_ROM_PTR(&bleio_addresstype_type) }, + { MP_ROM_QSTR(MP_QSTR_UUIDType), MP_ROM_PTR(&bleio_uuidtype_type) }, }; STATIC MP_DEFINE_CONST_DICT(bleio_module_globals, bleio_module_globals_table); diff --git a/shared-module/bleio/AdvertisementData.h b/shared-module/bleio/AdvertisementData.h new file mode 100644 index 000000000..0ebc48ea7 --- /dev/null +++ b/shared-module/bleio/AdvertisementData.h @@ -0,0 +1,76 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H +#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H + +// Taken from https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile +enum { + AdFlags = 0x01, + AdIncompleteListOf16BitServiceClassUUIDs = 0x02, + AdCompleteListOf16BitServiceClassUUIDs = 0x03, + AdIncompleteListOf32BitServiceClassUUIDs = 0x04, + AdCompleteListOf32BitServiceClassUUIDs = 0x05, + AdIncompleteListOf128BitServiceClassUUIDs = 0x06, + AdCompleteListOf128BitServiceClassUUIDs = 0x07, + AdShortenedLocalName = 0x08, + AdCompleteLocalName = 0x09, + AdTxPowerLevel = 0x0A, + AdClassOfDevice = 0x0D, + AdSimplePairingHashC = 0x0E, + AdSimplePairingRandomizerR = 0x0F, + AdSecurityManagerTKValue = 0x10, + AdSecurityManagerOOBFlags = 0x11, + AdSlaveConnectionIntervalRange = 0x12, + AdListOf16BitServiceSolicitationUUIDs = 0x14, + AdListOf128BitServiceSolicitationUUIDs = 0x15, + AdServiceData = 0x16, + AdPublicTargetAddress = 0x17, + AdRandomTargetAddress = 0x18, + AdAppearance = 0x19, + AdAdvertisingInterval = 0x1A, + AdLEBluetoothDeviceAddress = 0x1B, + AdLERole = 0x1C, + AdSimplePairingHashC256 = 0x1D, + AdSimplePairingRandomizerR256 = 0x1E, + AdListOf32BitServiceSolicitationUUIDs = 0x1F, + AdServiceData32BitUUID = 0x20, + AdServiceData128BitUUID = 0x21, + AdLESecureConnectionsConfirmationValue = 0x22, + AdLESecureConnectionsRandomValue = 0x23, + AdURI = 0x24, + AdIndoorPositioning = 0x25, + AdTransportDiscoveryData = 0x26, + AdLESupportedFeatures = 0x27, + AdChannelMapUpdateIndication = 0x28, + AdPBADV = 0x29, + AdMeshMessage = 0x2A, + AdMeshBeacon = 0x2B, + Ad3DInformationData = 0x3D, + AdManufacturerSpecificData = 0xFF, +}; + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H -- cgit v1.2.3 From 1c6bf9a15061d64ea097a024d73ad0a6da8f8e2b Mon Sep 17 00:00:00 2001 From: arturo182 Date: Wed, 18 Jul 2018 10:22:11 +0200 Subject: bleio: Move the Scanner class to a shared module --- ports/nrf/Makefile | 2 +- ports/nrf/common-hal/bleio/Scanner.c | 59 ++++++++++ ports/nrf/drivers/bluetooth/ble_drv.c | 19 ++-- ports/nrf/drivers/bluetooth/ble_drv.h | 8 +- ports/nrf/modules/ubluepy/modubluepy.c | 4 - ports/nrf/modules/ubluepy/modubluepy.h | 6 -- ports/nrf/modules/ubluepy/ubluepy_scanner.c | 118 -------------------- shared-bindings/bleio/ScanEntry.c | 4 +- shared-bindings/bleio/Scanner.c | 161 ++++++++++++++++++++++++++++ shared-bindings/bleio/Scanner.h | 38 +++++++ shared-bindings/bleio/__init__.c | 3 + shared-module/bleio/Scanner.h | 39 +++++++ 12 files changed, 317 insertions(+), 144 deletions(-) create mode 100644 ports/nrf/common-hal/bleio/Scanner.c delete mode 100644 ports/nrf/modules/ubluepy/ubluepy_scanner.c create mode 100644 shared-bindings/bleio/Scanner.c create mode 100644 shared-bindings/bleio/Scanner.h create mode 100644 shared-module/bleio/Scanner.h (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index c5e0df02f..3873f2cbc 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -134,7 +134,6 @@ DRIVERS_SRC_C += $(addprefix modules/,\ ubluepy/ubluepy_characteristic.c \ ubluepy/ubluepy_delegate.c \ ubluepy/ubluepy_constants.c \ - ubluepy/ubluepy_scanner.c \ ) SRC_COMMON_HAL += \ @@ -167,6 +166,7 @@ SRC_COMMON_HAL += \ bleio/__init__.c \ bleio/Adapter.c \ bleio/Descriptor.c \ + bleio/Scanner.c \ bleio/UUID.c endif diff --git a/ports/nrf/common-hal/bleio/Scanner.c b/ports/nrf/common-hal/bleio/Scanner.c new file mode 100644 index 000000000..ed1987144 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Scanner.c @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec + * + * 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 + +#include "ble_drv.h" +#include "py/mphal.h" +#include "shared-bindings/bleio/Scanner.h" +#include "shared-bindings/bleio/ScanEntry.h" + +STATIC void adv_event_handler(bleio_scanner_obj_t *self, ble_drv_adv_data_t *data) { + // TODO: Don't add new entry for each item, group by address and update + bleio_scanentry_obj_t *item = m_new_obj(bleio_scanentry_obj_t); + item->base.type = &bleio_scanentry_type; + + item->rssi = data->rssi; + item->data = mp_obj_new_bytearray(data->data_len, data->p_data); + + item->address.type = data->addr_type; + memcpy(item->address.value, data->p_peer_addr, BLEIO_ADDRESS_BYTES); + + mp_obj_list_append(self->adv_reports, item); + + ble_drv_scan_continue(); +} + +void common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_int_t timeout) { + ble_drv_adv_report_handler_set(self, adv_event_handler); + + ble_drv_scan_start(self->interval, self->window); + + mp_hal_delay_ms(timeout); + + ble_drv_scan_stop(); +} diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 766660828..1d67842ef 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -94,7 +94,7 @@ static volatile ble_drv_disc_add_service_callback_t disc_add_service_handler; static volatile ble_drv_disc_add_char_callback_t disc_add_char_handler; static volatile ble_drv_gattc_char_data_callback_t gattc_char_data_handle; -static mp_obj_t mp_adv_observer; +static bleio_scanner_obj_t *mp_adv_observer; static mp_obj_t mp_gattc_observer; static mp_obj_t mp_gattc_disc_service_observer; static mp_obj_t mp_gattc_disc_char_observer; @@ -707,8 +707,8 @@ void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t gattc_event_handler = evt_handler; } -void ble_drv_adv_report_handler_set(mp_obj_t obj, ble_drv_adv_evt_callback_t evt_handler) { - mp_adv_observer = obj; +void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *self, ble_drv_adv_evt_callback_t evt_handler) { + mp_adv_observer = self; adv_event_handler = evt_handler; } @@ -760,15 +760,15 @@ void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, u ; } } -void ble_drv_scan_start(void) { +void ble_drv_scan_start(uint16_t interval, uint16_t window) { SD_TEST_OR_ENABLE(); ble_gap_scan_params_t scan_params; memset(&scan_params, 0, sizeof(ble_gap_scan_params_t)); scan_params.active = 1; - scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); - scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS); + scan_params.interval = MSEC_TO_UNITS(interval, UNIT_0_625_MS); + scan_params.window = MSEC_TO_UNITS(window, UNIT_0_625_MS); #if (BLUETOOTH_SD == 140) scan_params.scan_phys = BLE_GAP_PHY_1MBPS; #endif @@ -1003,10 +1003,9 @@ static void ble_evt_handler(ble_evt_t * p_ble_evt) { #endif }; - // TODO: Fix unsafe callback to possible undefined callback... - adv_event_handler(mp_adv_observer, - p_ble_evt->header.evt_id, - &adv_data); + if (adv_event_handler != NULL) { + adv_event_handler(mp_adv_observer, &adv_data); + } break; case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h index ca9a38fdd..d344e2690 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.h +++ b/ports/nrf/drivers/bluetooth/ble_drv.h @@ -32,6 +32,8 @@ #include #include +#include "shared-module/bleio/Scanner.h" + #include "modubluepy.h" typedef struct { @@ -67,7 +69,7 @@ typedef struct { typedef void (*ble_drv_gap_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data); typedef void (*ble_drv_gatts_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); typedef void (*ble_drv_gattc_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); -typedef void (*ble_drv_adv_evt_callback_t)(mp_obj_t self, uint16_t event_id, ble_drv_adv_data_t * data); +typedef void (*ble_drv_adv_evt_callback_t)(bleio_scanner_obj_t *self, ble_drv_adv_data_t *data); typedef void (*ble_drv_disc_add_service_callback_t)(mp_obj_t self, ble_drv_service_data_t * p_service_data); typedef void (*ble_drv_disc_add_char_callback_t)(mp_obj_t self, ble_drv_char_data_t * p_desc_data); typedef void (*ble_drv_gattc_char_data_callback_t)(mp_obj_t self, uint16_t length, uint8_t * p_data); @@ -106,13 +108,13 @@ void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data, bool w_response); -void ble_drv_scan_start(void); +void ble_drv_scan_start(uint16_t interval, uint16_t window); void ble_drv_scan_continue(void); void ble_drv_scan_stop(void); -void ble_drv_adv_report_handler_set(mp_obj_t obj, ble_drv_adv_evt_callback_t evt_handler); +void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *self, ble_drv_adv_evt_callback_t evt_handler); void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type); diff --git a/ports/nrf/modules/ubluepy/modubluepy.c b/ports/nrf/modules/ubluepy/modubluepy.c index 034cc806c..e817983fa 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.c +++ b/ports/nrf/modules/ubluepy/modubluepy.c @@ -33,7 +33,6 @@ extern const mp_obj_type_t ubluepy_service_type; extern const mp_obj_type_t ubluepy_characteristic_type; extern const mp_obj_type_t ubluepy_delegate_type; extern const mp_obj_type_t ubluepy_constants_type; -extern const mp_obj_type_t ubluepy_scanner_type; STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubluepy) }, @@ -42,9 +41,6 @@ STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { #endif #if 0 // MICROPY_PY_UBLUEPY_CENTRAL { MP_ROM_QSTR(MP_QSTR_Central), MP_ROM_PTR(&ubluepy_central_type) }, -#endif -#if MICROPY_PY_UBLUEPY_CENTRAL - { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&ubluepy_scanner_type) }, #endif { MP_ROM_QSTR(MP_QSTR_DefaultDelegate), MP_ROM_PTR(&ubluepy_delegate_type) }, { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&ubluepy_service_type) }, diff --git a/ports/nrf/modules/ubluepy/modubluepy.h b/ports/nrf/modules/ubluepy/modubluepy.h index 8fb9fadd0..f51cc9e87 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.h +++ b/ports/nrf/modules/ubluepy/modubluepy.h @@ -77,7 +77,6 @@ p.advertise(device_name="micr", services=[s]) extern const mp_obj_type_t ubluepy_service_type; extern const mp_obj_type_t ubluepy_characteristic_type; extern const mp_obj_type_t ubluepy_peripheral_type; -extern const mp_obj_type_t ubluepy_scanner_type; extern const mp_obj_type_t ubluepy_constants_type; extern const mp_obj_type_t ubluepy_constants_ad_types_type; @@ -147,11 +146,6 @@ typedef struct _ubluepy_advertise_data_t { bool connectable; } ubluepy_advertise_data_t; -typedef struct _ubluepy_scanner_obj_t { - mp_obj_base_t base; - mp_obj_t adv_reports; -} ubluepy_scanner_obj_t; - typedef enum _ubluepy_prop_t { UBLUEPY_PROP_BROADCAST = 0x01, UBLUEPY_PROP_READ = 0x02, diff --git a/ports/nrf/modules/ubluepy/ubluepy_scanner.c b/ports/nrf/modules/ubluepy/ubluepy_scanner.c deleted file mode 100644 index 9d15037a5..000000000 --- a/ports/nrf/modules/ubluepy/ubluepy_scanner.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 -#include "py/obj.h" -#include "py/runtime.h" -#include "py/objstr.h" -#include "py/objlist.h" -#include "py/mphal.h" - -#if MICROPY_PY_UBLUEPY_CENTRAL - -#include "shared-bindings/bleio/ScanEntry.h" -#include "ble_drv.h" - -STATIC void adv_event_handler(mp_obj_t self_in, uint16_t event_id, ble_drv_adv_data_t * data) { - ubluepy_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); - - // TODO: Don't add new entry for each item, group by address and update - bleio_scanentry_obj_t *item = m_new_obj(bleio_scanentry_obj_t); - item->base.type = &bleio_scanentry_type; - - item->rssi = data->rssi; - item->data = mp_obj_new_bytearray(data->data_len, data->p_data); - - item->address.type = data->addr_type; - memcpy(item->address.value, data->p_peer_addr, BLEIO_ADDRESS_BYTES); - - mp_obj_list_append(self->adv_reports, item); - - ble_drv_scan_continue(); -} - -STATIC void ubluepy_scanner_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { - ubluepy_scanner_obj_t * self = (ubluepy_scanner_obj_t *)o; - (void)self; - mp_printf(print, "Scanner"); -} - -STATIC mp_obj_t ubluepy_scanner_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - static const mp_arg_t allowed_args[] = { - - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - ubluepy_scanner_obj_t * s = m_new_obj(ubluepy_scanner_obj_t); - s->base.type = type; - - return MP_OBJ_FROM_PTR(s); -} - -/// \method scan(timeout) -/// Scan for devices. Timeout is in milliseconds and will set the duration -/// of the scanning. -/// -STATIC mp_obj_t scanner_scan(mp_obj_t self_in, mp_obj_t timeout_in) { - ubluepy_scanner_obj_t * self = MP_OBJ_TO_PTR(self_in); - mp_int_t timeout = mp_obj_get_int(timeout_in); - - self->adv_reports = mp_obj_new_list(0, NULL); - - ble_drv_adv_report_handler_set(MP_OBJ_FROM_PTR(self), adv_event_handler); - - // start - ble_drv_scan_start(); - - // sleep - mp_hal_delay_ms(timeout); - - // stop - ble_drv_scan_stop(); - - return self->adv_reports; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(ubluepy_scanner_scan_obj, scanner_scan); - -STATIC const mp_rom_map_elem_t ubluepy_scanner_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&ubluepy_scanner_scan_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(ubluepy_scanner_locals_dict, ubluepy_scanner_locals_dict_table); - - -const mp_obj_type_t ubluepy_scanner_type = { - { &mp_type_type }, - .name = MP_QSTR_Scanner, - .print = ubluepy_scanner_print, - .make_new = ubluepy_scanner_make_new, - .locals_dict = (mp_obj_dict_t*)&ubluepy_scanner_locals_dict -}; - -#endif // MICROPY_PY_UBLUEPY_CENTRAL diff --git a/shared-bindings/bleio/ScanEntry.c b/shared-bindings/bleio/ScanEntry.c index f41665581..e38a08909 100644 --- a/shared-bindings/bleio/ScanEntry.c +++ b/shared-bindings/bleio/ScanEntry.c @@ -48,7 +48,7 @@ //| .. attribute:: address //| //| The address of the device. (read-only) -//| This attribute is of type `bleio:Address`. +//| This attribute is of type `bleio.Address`. //| //| .. attribute:: manufacturer_specific_data @@ -75,7 +75,7 @@ //| .. attribute:: service_uuids //| //| The address of the device. (read-only) -//| This attribute is a list of `bleio:UUID`. +//| This attribute is a list of `bleio.UUID`. //| This attribute might be empty or incomplete, depending on the advertisement packet. //| Currently only 16-bit UUIDS are listed. //| diff --git a/shared-bindings/bleio/Scanner.c b/shared-bindings/bleio/Scanner.c new file mode 100644 index 000000000..00e29d9d5 --- /dev/null +++ b/shared-bindings/bleio/Scanner.c @@ -0,0 +1,161 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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 "py/objproperty.h" +#include "shared-bindings/bleio/ScanEntry.h" +#include "shared-bindings/bleio/Scanner.h" + +#define DEFAULT_INTERVAL 100 +#define DEFAULT_WINDOW 100 + +//| .. currentmodule:: bleio +//| +//| :class:`Scanner` -- scan for nearby BLE devices +//| ========================================================= +//| +//| Allows scanning for nearby BLE devices. +//| +//| Usage:: +//| +//| import bleio +//| scanner = bleio.Scanner() +//| entries = scanner.scan(2500) +//| print(entries) +//| + +//| .. class:: Scanner() +//| +//| Create a new Scanner object. +//| + +//| .. attribute:: interval +//| +//| The interval (in ms) between the start of two consecutive scan windows. +//| Allowed values are between 10ms and 10.24 sec. +//| + +//| .. attribute:: window +//| +//| The duration (in ms) in which a single BLE channel is scanned. +//| Allowed values are between 10ms and 10.24 sec. +//| + +//| .. method:: scan(timeout) +//| +//| Performs a BLE scan lasting :py:data:`timeout` ms. +//| +//| :returns: advertising packets found +//| :rtype: list of :py:class:`bleio.ScanEntry` +//| +STATIC void bleio_scanner_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_printf(print, "Scanner(interval: %d window: %d)", self->interval, self->window); +} + +STATIC mp_obj_t bleio_scanner_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + bleio_scanner_obj_t *self = m_new_obj(bleio_scanner_obj_t); + self->base.type = type; + + self->interval = DEFAULT_INTERVAL; + self->window = DEFAULT_WINDOW; + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t bleio_scanner_get_interval(mp_obj_t self_in) { + bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_int(self->interval); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanner_get_interval_obj, bleio_scanner_get_interval); + +static mp_obj_t bleio_scanner_set_interval(mp_obj_t self_in, mp_obj_t value) { + bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->interval = mp_obj_get_int(value); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_scanner_set_interval_obj, bleio_scanner_set_interval); + +const mp_obj_property_t bleio_scanner_interval_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_scanner_get_interval_obj, + (mp_obj_t)&bleio_scanner_set_interval_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t scanner_scan(mp_obj_t self_in, mp_obj_t timeout_in) { + bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); + const mp_int_t timeout = mp_obj_get_int(timeout_in); + + self->adv_reports = mp_obj_new_list(0, NULL); + + common_hal_bleio_scanner_scan(self, timeout); + + return self->adv_reports; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_scanner_scan_obj, scanner_scan); + +STATIC mp_obj_t bleio_scanner_get_window(mp_obj_t self_in) { + bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_int(self->window); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanner_get_window_obj, bleio_scanner_get_window); + +static mp_obj_t bleio_scanner_set_window(mp_obj_t self_in, mp_obj_t value) { + bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->window = mp_obj_get_int(value); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_scanner_set_window_obj, bleio_scanner_set_window); + +const mp_obj_property_t bleio_scanner_window_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_scanner_get_window_obj, + (mp_obj_t)&bleio_scanner_set_window_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_scanner_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_interval), MP_ROM_PTR(&bleio_scanner_interval_obj) }, + { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&bleio_scanner_scan_obj) }, + { MP_ROM_QSTR(MP_QSTR_window), MP_ROM_PTR(&bleio_scanner_window_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_scanner_locals_dict, bleio_scanner_locals_dict_table); + +const mp_obj_type_t bleio_scanner_type = { + { &mp_type_type }, + .name = MP_QSTR_Scanner, + .print = bleio_scanner_print, + .make_new = bleio_scanner_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_scanner_locals_dict +}; diff --git a/shared-bindings/bleio/Scanner.h b/shared-bindings/bleio/Scanner.h new file mode 100644 index 000000000..03db5cd8b --- /dev/null +++ b/shared-bindings/bleio/Scanner.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H + +#include "py/objtype.h" +#include "shared-module/bleio/Scanner.h" + +extern const mp_obj_type_t bleio_scanner_type; + +extern void common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_int_t timeout); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index 315b2c32c..4ad858ac9 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -32,6 +32,7 @@ #include "shared-bindings/bleio/AdvertisementData.h" #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/ScanEntry.h" +#include "shared-bindings/bleio/Scanner.h" #include "shared-bindings/bleio/UUID.h" #include "shared-bindings/bleio/UUIDType.h" @@ -55,6 +56,7 @@ //| Adapter //| Descriptor //| ScanEntry +//| Scanner //| UUID //| UUIDType //| @@ -71,6 +73,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, + { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, { MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, // Properties diff --git a/shared-module/bleio/Scanner.h b/shared-module/bleio/Scanner.h new file mode 100644 index 000000000..f1159adfe --- /dev/null +++ b/shared-module/bleio/Scanner.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_SCANNER_H +#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_SCANNER_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t adv_reports; + uint16_t interval; + uint16_t window; +} bleio_scanner_obj_t; + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_SCANNER_H -- cgit v1.2.3 From fb422ccf5e4dcf644a4c99fe4843af2838a23907 Mon Sep 17 00:00:00 2001 From: arturo182 Date: Wed, 18 Jul 2018 10:47:58 +0200 Subject: bleio: Remove SAMD mention in include guard Damn copy-paste! --- shared-module/bleio/Address.h | 6 +++--- shared-module/bleio/AdvertisementData.h | 6 +++--- shared-module/bleio/Scanner.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'shared-module') diff --git a/shared-module/bleio/Address.h b/shared-module/bleio/Address.h index a520b8dd6..f0998c163 100644 --- a/shared-module/bleio/Address.h +++ b/shared-module/bleio/Address.h @@ -24,8 +24,8 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADDRESS_H -#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADDRESS_H +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H #include "shared-bindings/bleio/AddressType.h" @@ -37,4 +37,4 @@ typedef struct { uint8_t value[BLEIO_ADDRESS_BYTES]; } bleio_address_obj_t; -#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADDRESS_H +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H diff --git a/shared-module/bleio/AdvertisementData.h b/shared-module/bleio/AdvertisementData.h index 0ebc48ea7..2a9addf57 100644 --- a/shared-module/bleio/AdvertisementData.h +++ b/shared-module/bleio/AdvertisementData.h @@ -24,8 +24,8 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H -#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H // Taken from https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile enum { @@ -73,4 +73,4 @@ enum { AdManufacturerSpecificData = 0xFF, }; -#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H diff --git a/shared-module/bleio/Scanner.h b/shared-module/bleio/Scanner.h index f1159adfe..76f5e5866 100644 --- a/shared-module/bleio/Scanner.h +++ b/shared-module/bleio/Scanner.h @@ -24,8 +24,8 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_SCANNER_H -#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_SCANNER_H +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANNER_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANNER_H #include "py/obj.h" @@ -36,4 +36,4 @@ typedef struct { uint16_t window; } bleio_scanner_obj_t; -#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BLEIO_SCANNER_H +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANNER_H -- cgit v1.2.3 From cc782492269f2311995269507ff1411d3bbb8a2f Mon Sep 17 00:00:00 2001 From: arturo182 Date: Wed, 18 Jul 2018 23:47:06 +0200 Subject: nrf: Move the Characteristic class from ubluepy to the shared bleio module --- ports/nrf/Makefile | 2 +- ports/nrf/common-hal/bleio/Characteristic.c | 53 ++++ ports/nrf/common-hal/bleio/UUID.h | 1 - ports/nrf/drivers/bluetooth/ble_drv.c | 151 +++++----- ports/nrf/drivers/bluetooth/ble_drv.h | 22 +- ports/nrf/modules/ubluepy/modubluepy.c | 2 - ports/nrf/modules/ubluepy/modubluepy.h | 15 - ports/nrf/modules/ubluepy/ubluepy_characteristic.c | 224 -------------- ports/nrf/modules/ubluepy/ubluepy_peripheral.c | 18 +- ports/nrf/modules/ubluepy/ubluepy_service.c | 11 +- shared-bindings/bleio/Characteristic.c | 330 +++++++++++++++++++++ shared-bindings/bleio/Characteristic.h | 37 +++ shared-bindings/bleio/__init__.c | 3 + shared-module/bleio/Characteristic.h | 52 ++++ 14 files changed, 578 insertions(+), 343 deletions(-) create mode 100644 ports/nrf/common-hal/bleio/Characteristic.c delete mode 100644 ports/nrf/modules/ubluepy/ubluepy_characteristic.c create mode 100644 shared-bindings/bleio/Characteristic.c create mode 100644 shared-bindings/bleio/Characteristic.h create mode 100644 shared-module/bleio/Characteristic.h (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 8a4454a0a..6ed2d3054 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -131,7 +131,6 @@ DRIVERS_SRC_C += $(addprefix modules/,\ ubluepy/modubluepy.c \ ubluepy/ubluepy_peripheral.c \ ubluepy/ubluepy_service.c \ - ubluepy/ubluepy_characteristic.c \ ) SRC_COMMON_HAL += \ @@ -163,6 +162,7 @@ ifneq ($(SD), ) SRC_COMMON_HAL += \ bleio/__init__.c \ bleio/Adapter.c \ + bleio/Characteristic.c \ bleio/Descriptor.c \ bleio/Scanner.c \ bleio/UUID.c diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c new file mode 100644 index 000000000..3087b6707 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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 "ble_drv.h" +#include "shared-module/bleio/Characteristic.h" + +void data_callback(bleio_characteristic_obj_t *self, uint16_t length, uint8_t *data) { + self->value_data = mp_obj_new_bytearray(length, data); +} + +void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self) { + ble_drv_attr_c_read(self, data_callback); +} + +void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { + ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(self->service); + ubluepy_role_type_t role = service->p_periph->role; + + if (role == UBLUEPY_ROLE_PERIPHERAL) { + // TODO: Add indications + if (self->props.notify) { + ble_drv_attr_s_notify(self, bufinfo); + } else { + ble_drv_attr_s_write(self, bufinfo); + } + } else { + ble_drv_attr_c_write(self, bufinfo); + } + +} diff --git a/ports/nrf/common-hal/bleio/UUID.h b/ports/nrf/common-hal/bleio/UUID.h index b919fec28..cb0fddcfd 100644 --- a/ports/nrf/common-hal/bleio/UUID.h +++ b/ports/nrf/common-hal/bleio/UUID.h @@ -28,7 +28,6 @@ #ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_UUID_H #define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_UUID_H -#include "py/obj.h" #include "shared-bindings/bleio/UUIDType.h" typedef struct { diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 1d67842ef..6240a18e4 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -98,7 +98,7 @@ static bleio_scanner_obj_t *mp_adv_observer; static mp_obj_t mp_gattc_observer; static mp_obj_t mp_gattc_disc_service_observer; static mp_obj_t mp_gattc_disc_char_observer; -static mp_obj_t mp_gattc_char_data_observer; +static bleio_characteristic_obj_t *mp_gattc_char_data_observer; #if (BLUETOOTH_SD == 140) static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; @@ -270,38 +270,26 @@ bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx) { bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj) { SD_TEST_OR_ENABLE(); - if (p_service_obj->p_uuid->type > BLE_UUID_TYPE_BLE) { + ble_uuid_t uuid; + uuid.type = BLE_UUID_TYPE_BLE; + uuid.uuid = p_service_obj->p_uuid->value[0] | (p_service_obj->p_uuid->value[1] << 8); - ble_uuid_t uuid; + if (p_service_obj->p_uuid->type == UUID_TYPE_128BIT) { uuid.type = p_service_obj->p_uuid->uuid_vs_idx; - uuid.uuid = p_service_obj->p_uuid->value[0]; - uuid.uuid += p_service_obj->p_uuid->value[1] << 8; - - if (sd_ble_gatts_service_add(p_service_obj->type, - &uuid, - &p_service_obj->handle) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not add Service."))); - } - } else if (p_service_obj->p_uuid->type == BLE_UUID_TYPE_BLE) { - BLE_DRIVER_LOG("adding service\n"); - - ble_uuid_t uuid; - uuid.type = p_service_obj->p_uuid->type; - uuid.uuid = p_service_obj->p_uuid->value[0]; - uuid.uuid += p_service_obj->p_uuid->value[1] << 8; + } - if (sd_ble_gatts_service_add(p_service_obj->type, - &uuid, - &p_service_obj->handle) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not add Service."))); - } + uint32_t err_code = sd_ble_gatts_service_add(p_service_obj->type, + &uuid, + &p_service_obj->handle); + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + translate("Can not add Service. status: 0x%08lX"), err_code)); } + return true; } -bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj) { +bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic) { ble_gatts_char_md_t char_md; ble_gatts_attr_md_t cccd_md; ble_gatts_attr_t attr_char_value; @@ -310,24 +298,19 @@ bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj) { memset(&char_md, 0, sizeof(char_md)); - char_md.char_props.broadcast = (p_char_obj->props & UBLUEPY_PROP_BROADCAST) ? 1 : 0; - char_md.char_props.read = (p_char_obj->props & UBLUEPY_PROP_READ) ? 1 : 0; - char_md.char_props.write_wo_resp = (p_char_obj->props & UBLUEPY_PROP_WRITE_WO_RESP) ? 1 : 0; - char_md.char_props.write = (p_char_obj->props & UBLUEPY_PROP_WRITE) ? 1 : 0; - char_md.char_props.notify = (p_char_obj->props & UBLUEPY_PROP_NOTIFY) ? 1 : 0; - char_md.char_props.indicate = (p_char_obj->props & UBLUEPY_PROP_INDICATE) ? 1 : 0; -#if 0 - char_md.char_props.auth_signed_wr = (p_char_obj->props & UBLUEPY_PROP_NOTIFY) ? 1 : 0; -#endif - + char_md.char_props.broadcast = characteristic->props.broadcast; + char_md.char_props.read = characteristic->props.read; + char_md.char_props.write_wo_resp = characteristic->props.write_wo_resp; + char_md.char_props.write = characteristic->props.write; + char_md.char_props.notify = characteristic->props.notify; + char_md.char_props.indicate = characteristic->props.indicate; char_md.p_char_user_desc = NULL; char_md.p_char_pf = NULL; char_md.p_user_desc_md = NULL; char_md.p_sccd_md = NULL; - // if cccd - if (p_char_obj->attrs & UBLUEPY_ATTR_CCCD) { + if (characteristic->props.notify || characteristic->props.notify) { memset(&cccd_md, 0, sizeof(cccd_md)); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm); @@ -337,9 +320,12 @@ bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj) { char_md.p_cccd_md = NULL; } - uuid.type = p_char_obj->p_uuid->type; - uuid.uuid = p_char_obj->p_uuid->value[0]; - uuid.uuid += p_char_obj->p_uuid->value[1] << 8; + uuid.type = BLE_UUID_TYPE_BLE; + if (characteristic->uuid->type == UUID_TYPE_128BIT) + uuid.type = characteristic->uuid->uuid_vs_idx; + + uuid.uuid = characteristic->uuid->value[0]; + uuid.uuid += characteristic->uuid->value[1] << 8; memset(&attr_md, 0, sizeof(attr_md)); @@ -365,19 +351,20 @@ bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj) { ble_gatts_char_handles_t handles; - if (sd_ble_gatts_characteristic_add(p_char_obj->service_handle, - &char_md, - &attr_char_value, - &handles) != 0) { + uint32_t err_code = sd_ble_gatts_characteristic_add(characteristic->service_handle, + &char_md, + &attr_char_value, + &handles); + if (err_code != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not add Characteristic."))); + translate("Can not add Characteristic. status: 0x%08lX"), err_code)); } // apply handles to object instance - p_char_obj->handle = handles.value_handle; - p_char_obj->user_desc_handle = handles.user_desc_handle; - p_char_obj->cccd_handle = handles.cccd_handle; - p_char_obj->sccd_handle = handles.sccd_handle; + characteristic->handle = handles.value_handle; + characteristic->user_desc_handle = handles.user_desc_handle; + characteristic->cccd_handle = handles.cccd_handle; + characteristic->sccd_handle = handles.sccd_handle; return true; } @@ -652,15 +639,18 @@ void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, ui } -void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) { +void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { + ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); + uint16_t conn_handle = service->p_periph->conn_handle; ble_gatts_value_t gatts_value; + memset(&gatts_value, 0, sizeof(gatts_value)); - gatts_value.len = len; + gatts_value.len = bufinfo->len; gatts_value.offset = 0; - gatts_value.p_value = p_data; + gatts_value.p_value = bufinfo->buf; - uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value); + uint32_t err_code = sd_ble_gatts_value_set(conn_handle, characteristic->handle, &gatts_value); if (err_code != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, @@ -668,17 +658,19 @@ void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, u } } -void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) { - uint16_t hvx_len = len; +void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { + ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); + uint16_t conn_handle = service->p_periph->conn_handle; ble_gatts_hvx_params_t hvx_params; + uint16_t hvx_len = bufinfo->len; memset(&hvx_params, 0, sizeof(hvx_params)); - hvx_params.handle = handle; + hvx_params.handle = characteristic->handle; hvx_params.type = BLE_GATT_HVX_NOTIFICATION; hvx_params.offset = 0; hvx_params.p_len = &hvx_len; - hvx_params.p_data = p_data; + hvx_params.p_data = bufinfo->buf; while (m_tx_in_progress) { ; @@ -713,13 +705,13 @@ void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *self, ble_drv_adv_evt_c } -void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, ble_drv_gattc_char_data_callback_t cb) { - - mp_gattc_char_data_observer = obj; +void ble_drv_attr_c_read(bleio_characteristic_obj_t *characteristic, ble_drv_gattc_char_data_callback_t cb) { + ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); + mp_gattc_char_data_observer = characteristic; gattc_char_data_handle = cb; - uint32_t err_code = sd_ble_gattc_read(conn_handle, - handle, + const uint32_t err_code = sd_ble_gattc_read(service->p_periph->conn_handle, + characteristic->handle, 0); if (err_code != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, @@ -731,26 +723,26 @@ void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, bl } } -void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data, bool w_response) { +void ble_drv_attr_c_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { + ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); + uint16_t conn_handle = service->p_periph->conn_handle; ble_gattc_write_params_t write_params; + write_params.write_op = BLE_GATT_OP_WRITE_REQ; - if (w_response) { - write_params.write_op = BLE_GATT_OP_WRITE_REQ; - } else { + if (characteristic->props.write_wo_resp) { write_params.write_op = BLE_GATT_OP_WRITE_CMD; } write_params.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_CANCEL; - write_params.handle = handle; + write_params.handle = characteristic->handle; write_params.offset = 0; - write_params.len = len; - write_params.p_value = p_data; + write_params.len = bufinfo->len; + write_params.p_value = bufinfo->buf; - m_write_done = !w_response; + m_write_done = (write_params.write_op == BLE_GATT_OP_WRITE_CMD); uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); - if (err_code != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, translate("Can not write attribute value. status: 0x%02x"), (uint16_t)err_code)); @@ -1053,15 +1045,12 @@ static void ble_evt_handler(ble_evt_t * p_ble_evt) { char_data.decl_handle = p_char->handle_decl; char_data.value_handle = p_char->handle_value; - char_data.props |= (p_char->char_props.broadcast) ? UBLUEPY_PROP_BROADCAST : 0; - char_data.props |= (p_char->char_props.read) ? UBLUEPY_PROP_READ : 0; - char_data.props |= (p_char->char_props.write_wo_resp) ? UBLUEPY_PROP_WRITE_WO_RESP : 0; - char_data.props |= (p_char->char_props.write) ? UBLUEPY_PROP_WRITE : 0; - char_data.props |= (p_char->char_props.notify) ? UBLUEPY_PROP_NOTIFY : 0; - char_data.props |= (p_char->char_props.indicate) ? UBLUEPY_PROP_INDICATE : 0; - #if 0 - char_data.props |= (p_char->char_props.auth_signed_wr) ? UBLUEPY_PROP_NOTIFY : 0; - #endif + char_data.props.broadcast = p_char->char_props.broadcast; + char_data.props.read = p_char->char_props.read; + char_data.props.write_wo_resp = p_char->char_props.write_wo_resp; + char_data.props.write = p_char->char_props.write; + char_data.props.notify = p_char->char_props.notify; + char_data.props.indicate = p_char->char_props.indicate; disc_add_char_handler(mp_gattc_disc_char_observer, &char_data); } diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h index d344e2690..7283dbfce 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.h +++ b/ports/nrf/drivers/bluetooth/ble_drv.h @@ -32,6 +32,7 @@ #include #include +#include "shared-module/bleio/Characteristic.h" #include "shared-module/bleio/Scanner.h" #include "modubluepy.h" @@ -61,7 +62,14 @@ typedef struct { typedef struct { uint16_t uuid; uint8_t uuid_type; - uint8_t props; + struct { + bool broadcast : 1; + bool read : 1; + bool write_wo_resp : 1; + bool write : 1; + bool notify : 1; + bool indicate : 1; + } props; uint16_t decl_handle; uint16_t value_handle; } ble_drv_char_data_t; @@ -72,7 +80,7 @@ typedef void (*ble_drv_gattc_evt_callback_t)(mp_obj_t self, uint16_t event_id, u typedef void (*ble_drv_adv_evt_callback_t)(bleio_scanner_obj_t *self, ble_drv_adv_data_t *data); typedef void (*ble_drv_disc_add_service_callback_t)(mp_obj_t self, ble_drv_service_data_t * p_service_data); typedef void (*ble_drv_disc_add_char_callback_t)(mp_obj_t self, ble_drv_char_data_t * p_desc_data); -typedef void (*ble_drv_gattc_char_data_callback_t)(mp_obj_t self, uint16_t length, uint8_t * p_data); +typedef void (*ble_drv_gattc_char_data_callback_t)(bleio_characteristic_obj_t *self, uint16_t length, uint8_t * p_data); uint32_t ble_drv_stack_enable(void); @@ -86,7 +94,7 @@ bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx); bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj); -bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj); +bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic); bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params); @@ -100,13 +108,13 @@ void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); -void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, ble_drv_gattc_char_data_callback_t cb); +void ble_drv_attr_c_read(bleio_characteristic_obj_t *characteristic, ble_drv_gattc_char_data_callback_t cb); -void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); +void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo); -void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); +void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo); -void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data, bool w_response); +void ble_drv_attr_c_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo); void ble_drv_scan_start(uint16_t interval, uint16_t window); diff --git a/ports/nrf/modules/ubluepy/modubluepy.c b/ports/nrf/modules/ubluepy/modubluepy.c index 1470f4a37..2d1a6a550 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.c +++ b/ports/nrf/modules/ubluepy/modubluepy.c @@ -30,7 +30,6 @@ extern const mp_obj_type_t ubluepy_peripheral_type; extern const mp_obj_type_t ubluepy_service_type; -extern const mp_obj_type_t ubluepy_characteristic_type; STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubluepy) }, @@ -38,7 +37,6 @@ STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&ubluepy_peripheral_type) }, #endif { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&ubluepy_service_type) }, - { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&ubluepy_characteristic_type) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_ubluepy_globals, mp_module_ubluepy_globals_table); diff --git a/ports/nrf/modules/ubluepy/modubluepy.h b/ports/nrf/modules/ubluepy/modubluepy.h index 5d80e3536..ad33001cb 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.h +++ b/ports/nrf/modules/ubluepy/modubluepy.h @@ -75,7 +75,6 @@ p.advertise(device_name="micr", services=[s]) #include "py/obj.h" extern const mp_obj_type_t ubluepy_service_type; -extern const mp_obj_type_t ubluepy_characteristic_type; extern const mp_obj_type_t ubluepy_peripheral_type; typedef enum { @@ -116,20 +115,6 @@ typedef struct _ubluepy_service_obj_t { uint16_t end_handle; } ubluepy_service_obj_t; -typedef struct _ubluepy_characteristic_obj_t { - mp_obj_base_t base; - uint16_t handle; - bleio_uuid_obj_t * p_uuid; - uint16_t service_handle; - uint16_t user_desc_handle; - uint16_t cccd_handle; - uint16_t sccd_handle; - uint8_t props; - uint8_t attrs; - ubluepy_service_obj_t * p_service; - mp_obj_t value_data; -} ubluepy_characteristic_obj_t; - typedef struct _ubluepy_advertise_data_t { uint8_t * p_device_name; uint8_t device_name_len; diff --git a/ports/nrf/modules/ubluepy/ubluepy_characteristic.c b/ports/nrf/modules/ubluepy/ubluepy_characteristic.c deleted file mode 100644 index e0259fb06..000000000 --- a/ports/nrf/modules/ubluepy/ubluepy_characteristic.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 "py/obj.h" -#include "py/runtime.h" -#include "supervisor/shared/translate.h" - -#if MICROPY_PY_UBLUEPY_PERIPHERAL || MICROPY_PY_UBLUEPY_CENTRAL - -#include "modubluepy.h" -#include "ble_drv.h" -#include "shared-bindings/bleio/UUID.h" - -STATIC void ubluepy_characteristic_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { - ubluepy_characteristic_obj_t * self = (ubluepy_characteristic_obj_t *)o; - - mp_printf(print, "Characteristic(handle: 0x" HEX2_FMT ", conn_handle: " HEX2_FMT ")", - self->handle, self->p_service->p_periph->conn_handle); -} - -STATIC mp_obj_t ubluepy_characteristic_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - static const mp_arg_t allowed_args[] = { - { MP_QSTR_uuid, MP_ARG_REQUIRED| MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_props, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UBLUEPY_PROP_READ | UBLUEPY_PROP_WRITE} }, - { MP_QSTR_attrs, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - ubluepy_characteristic_obj_t *s = m_new_obj(ubluepy_characteristic_obj_t); - s->base.type = type; - - mp_obj_t uuid_obj = args[0].u_obj; - - if (uuid_obj == mp_const_none) { - return MP_OBJ_FROM_PTR(s); - } - - if (MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - s->p_uuid = MP_OBJ_TO_PTR(uuid_obj); - // (void)sd_characterstic_add(s); - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - translate("Invalid UUID parameter"))); - } - - if (args[1].u_int > 0) { - s->props = (uint8_t)args[1].u_int; - } - - if (args[2].u_int > 0) { - s->attrs = (uint8_t)args[2].u_int; - } - - // clear pointer to service - s->p_service = NULL; - - // clear pointer to char value data - s->value_data = NULL; - - return MP_OBJ_FROM_PTR(s); -} - -void char_data_callback(mp_obj_t self_in, uint16_t length, uint8_t * p_data) { - ubluepy_characteristic_obj_t * self = MP_OBJ_TO_PTR(self_in); - self->value_data = mp_obj_new_bytearray(length, p_data); -} - -/// \method read() -/// Read Characteristic value. -/// -STATIC mp_obj_t char_read(mp_obj_t self_in) { - ubluepy_characteristic_obj_t * self = MP_OBJ_TO_PTR(self_in); - -#if MICROPY_PY_UBLUEPY_CENTRAL - // TODO: free any previous allocation of value_data - - ble_drv_attr_c_read(self->p_service->p_periph->conn_handle, - self->handle, - self_in, - char_data_callback); - - return self->value_data; -#else - (void)self; - return mp_const_none; -#endif -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_characteristic_read_obj, char_read); - -/// \method write(data, [with_response=False]) -/// Write Characteristic value. -/// -STATIC mp_obj_t char_write(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - ubluepy_characteristic_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - mp_obj_t data = pos_args[1]; - - static const mp_arg_t allowed_args[] = { - { MP_QSTR_with_response, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false } }, - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); - - // figure out mode of the Peripheral - ubluepy_role_type_t role = self->p_service->p_periph->role; - - if (role == UBLUEPY_ROLE_PERIPHERAL) { - if (self->props & UBLUEPY_PROP_NOTIFY) { - ble_drv_attr_s_notify(self->p_service->p_periph->conn_handle, - self->handle, - bufinfo.len, - bufinfo.buf); - } else { - ble_drv_attr_s_write(self->p_service->p_periph->conn_handle, - self->handle, - bufinfo.len, - bufinfo.buf); - } - } else { -#if MICROPY_PY_UBLUEPY_CENTRAL - bool with_response = args[0].u_bool; - - ble_drv_attr_c_write(self->p_service->p_periph->conn_handle, - self->handle, - bufinfo.len, - bufinfo.buf, - with_response); -#endif - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ubluepy_characteristic_write_obj, 2, char_write); - -/// \method properties() -/// Read Characteristic value properties. -/// -STATIC mp_obj_t char_properties(mp_obj_t self_in) { - ubluepy_characteristic_obj_t * self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_NEW_SMALL_INT(self->props); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_characteristic_get_properties_obj, char_properties); - -/// \method uuid() -/// Get UUID instance of the characteristic. -/// -STATIC mp_obj_t char_uuid(mp_obj_t self_in) { - ubluepy_characteristic_obj_t * self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_FROM_PTR(self->p_uuid); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_characteristic_get_uuid_obj, char_uuid); - - -STATIC const mp_rom_map_elem_t ubluepy_characteristic_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&ubluepy_characteristic_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&ubluepy_characteristic_write_obj) }, -#if 0 - { MP_ROM_QSTR(MP_QSTR_supportsRead), MP_ROM_PTR(&ubluepy_characteristic_supports_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_propertiesToString), MP_ROM_PTR(&ubluepy_characteristic_properties_to_str_obj) }, - { MP_ROM_QSTR(MP_QSTR_getHandle), MP_ROM_PTR(&ubluepy_characteristic_get_handle_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_peripheral), MP_ROM_PTR(&ubluepy_characteristic_get_peripheral_obj) }, -#endif - { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&ubluepy_characteristic_get_uuid_obj) }, - { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&ubluepy_characteristic_get_properties_obj) }, - - { MP_ROM_QSTR(MP_QSTR_PROP_BROADCAST), MP_ROM_INT(UBLUEPY_PROP_BROADCAST) }, - { MP_ROM_QSTR(MP_QSTR_PROP_READ), MP_ROM_INT(UBLUEPY_PROP_READ) }, - { MP_ROM_QSTR(MP_QSTR_PROP_WRITE_WO_RESP), MP_ROM_INT(UBLUEPY_PROP_WRITE_WO_RESP) }, - { MP_ROM_QSTR(MP_QSTR_PROP_WRITE), MP_ROM_INT(UBLUEPY_PROP_WRITE) }, - { MP_ROM_QSTR(MP_QSTR_PROP_NOTIFY), MP_ROM_INT(UBLUEPY_PROP_NOTIFY) }, - { MP_ROM_QSTR(MP_QSTR_PROP_INDICATE), MP_ROM_INT(UBLUEPY_PROP_INDICATE) }, - { MP_ROM_QSTR(MP_QSTR_PROP_AUTH_SIGNED_WR), MP_ROM_INT(UBLUEPY_PROP_AUTH_SIGNED_WR) }, - -#if MICROPY_PY_UBLUEPY_PERIPHERAL - { MP_ROM_QSTR(MP_QSTR_ATTR_CCCD), MP_ROM_INT(UBLUEPY_ATTR_CCCD) }, -#endif - -#if MICROPY_PY_UBLUEPY_CENTRAL - { MP_ROM_QSTR(MP_QSTR_PROP_AUTH_SIGNED_WR), MP_ROM_INT(UBLUEPY_ATTR_SCCD) }, -#endif -}; - -STATIC MP_DEFINE_CONST_DICT(ubluepy_characteristic_locals_dict, ubluepy_characteristic_locals_dict_table); - -const mp_obj_type_t ubluepy_characteristic_type = { - { &mp_type_type }, - .name = MP_QSTR_Characteristic, - .print = ubluepy_characteristic_print, - .make_new = ubluepy_characteristic_make_new, - .locals_dict = (mp_obj_dict_t*)&ubluepy_characteristic_locals_dict -}; - -#endif // MICROPY_PY_UBLUEPY_PERIPHERAL || MICROPY_PY_UBLUEPY_CENTRAL diff --git a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c b/ports/nrf/modules/ubluepy/ubluepy_peripheral.c index 6fced1695..2a826db4d 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c +++ b/ports/nrf/modules/ubluepy/ubluepy_peripheral.c @@ -34,6 +34,7 @@ #include "ble_drv.h" #include "common-hal/bleio/UUID.h" +#include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/UUID.h" STATIC void ubluepy_peripheral_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { @@ -316,26 +317,31 @@ void static disc_add_service(mp_obj_t self, ble_drv_service_data_t * p_service_d void static disc_add_char(mp_obj_t service_in, ble_drv_char_data_t * p_desc_data) { ubluepy_service_obj_t * p_service = MP_OBJ_TO_PTR(service_in); - ubluepy_characteristic_obj_t * p_char = m_new_obj(ubluepy_characteristic_obj_t); - p_char->base.type = &ubluepy_characteristic_type; + bleio_characteristic_obj_t * p_char = m_new_obj(bleio_characteristic_obj_t); + p_char->base.type = &bleio_characteristic_type; bleio_uuid_obj_t * p_uuid = m_new_obj(bleio_uuid_obj_t); p_uuid->base.type = &bleio_uuid_type; - p_char->p_uuid = p_uuid; + p_char->uuid = p_uuid; p_uuid->type = p_desc_data->uuid_type; p_uuid->value[0] = p_desc_data->uuid & 0xFF; p_uuid->value[1] = p_desc_data->uuid >> 8; // add characteristic specific data from discovery - p_char->props = p_desc_data->props; + p_char->props.broadcast = p_desc_data->props.broadcast; + p_char->props.indicate = p_desc_data->props.indicate; + p_char->props.notify = p_desc_data->props.notify; + p_char->props.read = p_desc_data->props.read; + p_char->props.write = p_desc_data->props.write; + p_char->props.write_wo_resp = p_desc_data->props.write_wo_resp; p_char->handle = p_desc_data->value_handle; // equivalent to ubluepy_service.c - service_add_characteristic() // except the registration of the characteristic towards the bluetooth stack p_char->service_handle = p_service->handle; - p_char->p_service = p_service; + p_char->service = p_service; mp_obj_list_append(p_service->char_list, MP_OBJ_FROM_PTR(p_char)); } @@ -430,7 +436,7 @@ STATIC mp_obj_t peripheral_connect(mp_uint_t n_args, const mp_obj_t *pos_args, m mp_uint_t num_chars; mp_obj_get_array(p_service->char_list, &num_chars, &characteristics); - ubluepy_characteristic_obj_t * p_char = (ubluepy_characteristic_obj_t *)characteristics[num_chars - 1]; + bleio_characteristic_obj_t * p_char = (bleio_characteristic_obj_t *)characteristics[num_chars - 1]; uint16_t next_handle = p_char->handle + 1; if ((next_handle) < p_service->end_handle) { char_disc_retval = ble_drv_discover_characteristic(p_service, diff --git a/ports/nrf/modules/ubluepy/ubluepy_service.c b/ports/nrf/modules/ubluepy/ubluepy_service.c index 69d98bb7e..3834c61de 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_service.c +++ b/ports/nrf/modules/ubluepy/ubluepy_service.c @@ -94,14 +94,13 @@ STATIC mp_obj_t ubluepy_service_make_new(const mp_obj_type_t *type, size_t n_arg /// STATIC mp_obj_t service_add_characteristic(mp_obj_t self_in, mp_obj_t characteristic) { ubluepy_service_obj_t * self = MP_OBJ_TO_PTR(self_in); - ubluepy_characteristic_obj_t * p_char = MP_OBJ_TO_PTR(characteristic); + bleio_characteristic_obj_t * p_char = MP_OBJ_TO_PTR(characteristic); p_char->service_handle = self->handle; bool retval = ble_drv_characteristic_add(p_char); - if (retval) { - p_char->p_service = self; + p_char->service = self_in; } mp_obj_list_append(self->char_list, characteristic); @@ -139,10 +138,10 @@ STATIC mp_obj_t service_get_characteristic(mp_obj_t self_in, mp_obj_t uuid) { mp_obj_get_array(self->char_list, &num_chars, &chars); for (uint8_t i = 0; i < num_chars; i++) { - ubluepy_characteristic_obj_t * p_char = (ubluepy_characteristic_obj_t *)chars[i]; + bleio_characteristic_obj_t * p_char = (bleio_characteristic_obj_t *)chars[i]; - bool type_match = p_char->p_uuid->type == p_uuid->type; - bool uuid_match = ((uint16_t)(*(uint16_t *)&p_char->p_uuid->value[0]) == + bool type_match = p_char->uuid->type == p_uuid->type; + bool uuid_match = ((uint16_t)(*(uint16_t *)&p_char->uuid->value[0]) == (uint16_t)(*(uint16_t *)&p_uuid->value[0])); if (type_match && uuid_match) { diff --git a/shared-bindings/bleio/Characteristic.c b/shared-bindings/bleio/Characteristic.c new file mode 100644 index 000000000..fb96be703 --- /dev/null +++ b/shared-bindings/bleio/Characteristic.c @@ -0,0 +1,330 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec + * + * 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 "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/UUID.h" + +//| .. currentmodule:: bleio +//| +//| :class:`Characteristic` -- BLE service characteristic +//| ========================================================= +//| +//| Stores information about a BLE service characteristic and allows to read +//| and write the characteristic's value. +//| + +//| .. class:: Characteristic(uuid) +//| +//| Create a new Characteristic object identified by the specified UUID. +//| +//| :param uuid: The uuid of the characteristic +//| + +//| .. attribute:: broadcast +//| +//| A `bool` specifying if the characteristic allows broadcasting its value. +//| + +//| .. attribute:: indicate +//| +//| A `bool` specifying if the characteristic allows indicating its value. +//| + +//| .. attribute:: notify +//| +//| A `bool` specifying if the characteristic allows notifying its value. +//| + +//| .. attribute:: read +//| +//| A `bool` specifying if the characteristic allows reading its value. +//| + +//| .. attribute:: uuid +//| +//| The UUID of this characteristic. (read-only) +//| + +//| .. attribute:: value +//| +//| The value of this characteristic. The value can be written to if the `write` property allows it. +//| If the `read` property allows it, the value can be read. If the `notify` property is set, writting +//| to the value will generate a BLE notification. +//| + +//| .. attribute:: write +//| +//| A `bool` specifying if the characteristic allows writting to its value. +//| + +//| .. attribute:: write_no_resp +//| +//| A `bool` specifying if the characteristic allows writting to its value without response. +//| +STATIC void bleio_characteristic_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_printf(print, "Characteristic(uuid: 0x"HEX2_FMT""HEX2_FMT" handle: 0x" HEX2_FMT ")", + self->uuid->value[0], self->uuid->value[1], self->handle); +} + +STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, true); + bleio_characteristic_obj_t *self = m_new_obj(bleio_characteristic_obj_t); + self->base.type = &bleio_characteristic_type; + self->service = mp_const_none; + self->value_data = NULL; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + enum { ARG_uuid }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_uuid, MP_ARG_REQUIRED| MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_t uuid = args[ARG_uuid].u_obj; + + if (uuid == mp_const_none) { + return MP_OBJ_FROM_PTR(self); + } + + if (MP_OBJ_IS_TYPE(uuid, &bleio_uuid_type)) { + self->uuid = MP_OBJ_TO_PTR(uuid); + } else { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Invalid UUID parameter")); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t bleio_characteristic_get_broadcast(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(self->props.broadcast); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_broadcast_obj, bleio_characteristic_get_broadcast); + +STATIC mp_obj_t bleio_characteristic_set_broadcast(mp_obj_t self_in, mp_obj_t broadcast_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->props.broadcast = mp_obj_is_true(broadcast_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_broadcast_obj, bleio_characteristic_set_broadcast); + +const mp_obj_property_t bleio_characteristic_broadcast_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_broadcast_obj, + (mp_obj_t)&bleio_characteristic_set_broadcast_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_characteristic_get_indicate(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(self->props.indicate); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_indicate_obj, bleio_characteristic_get_indicate); + +STATIC mp_obj_t bleio_characteristic_set_indicate(mp_obj_t self_in, mp_obj_t indicate_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->props.indicate = mp_obj_is_true(indicate_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_indicate_obj, bleio_characteristic_set_indicate); + +const mp_obj_property_t bleio_characteristic_indicate_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_indicate_obj, + (mp_obj_t)&bleio_characteristic_set_indicate_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_characteristic_get_notify(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(self->props.notify); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_notify_obj, bleio_characteristic_get_notify); + +STATIC mp_obj_t bleio_characteristic_set_notify(mp_obj_t self_in, mp_obj_t notify_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->props.notify = mp_obj_is_true(notify_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_notify_obj, bleio_characteristic_set_notify); + +const mp_obj_property_t bleio_characteristic_notify_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_notify_obj, + (mp_obj_t)&bleio_characteristic_set_notify_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_characteristic_get_read(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(self->props.read); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_read_obj, bleio_characteristic_get_read); + +STATIC mp_obj_t bleio_characteristic_set_read(mp_obj_t self_in, mp_obj_t read_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->props.read = mp_obj_is_true(read_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_read_obj, bleio_characteristic_set_read); + +const mp_obj_property_t bleio_characteristic_read_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_read_obj, + (mp_obj_t)&bleio_characteristic_set_read_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_characteristic_get_write(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(self->props.write); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_write_obj, bleio_characteristic_get_write); + +STATIC mp_obj_t bleio_characteristic_set_write(mp_obj_t self_in, mp_obj_t write_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->props.write = mp_obj_is_true(write_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_write_obj, bleio_characteristic_set_write); + +const mp_obj_property_t bleio_characteristic_write_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_write_obj, + (mp_obj_t)&bleio_characteristic_set_write_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_characteristic_get_write_wo_resp(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(self->props.write_wo_resp); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_write_wo_resp_obj, bleio_characteristic_get_write_wo_resp); + +STATIC mp_obj_t bleio_characteristic_set_write_wo_resp(mp_obj_t self_in, mp_obj_t write_wo_resp_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->props.write_wo_resp = mp_obj_is_true(write_wo_resp_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_write_wo_resp_obj, bleio_characteristic_set_write_wo_resp); + +const mp_obj_property_t bleio_characteristic_write_wo_resp_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_write_wo_resp_obj, + (mp_obj_t)&bleio_characteristic_set_write_wo_resp_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_characteristic_get_uuid(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_FROM_PTR(self->uuid); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_uuid_obj, bleio_characteristic_get_uuid); + +const mp_obj_property_t bleio_characteristic_uuid_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_uuid_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_characteristic_read_value(self); + + return self->value_data; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_value_obj, bleio_characteristic_get_value); + +STATIC mp_obj_t bleio_characteristic_set_value(mp_obj_t self_in, mp_obj_t value_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); + + common_hal_bleio_characteristic_write_value(self, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_value_obj, bleio_characteristic_set_value); + +const mp_obj_property_t bleio_characteristic_value_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_value_obj, + (mp_obj_t)&bleio_characteristic_set_value_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_broadcast), MP_ROM_PTR(&bleio_characteristic_broadcast_obj) }, + { MP_ROM_QSTR(MP_QSTR_indicate), MP_ROM_PTR(&bleio_characteristic_indicate_obj) }, + { MP_ROM_QSTR(MP_QSTR_notify), MP_ROM_PTR(&bleio_characteristic_notify_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&bleio_characteristic_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&bleio_characteristic_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_wo_resp), MP_ROM_PTR(&bleio_characteristic_write_wo_resp_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_locals_dict, bleio_characteristic_locals_dict_table); + +const mp_obj_type_t bleio_characteristic_type = { + { &mp_type_type }, + .name = MP_QSTR_Characteristic, + .print = bleio_characteristic_print, + .make_new = bleio_characteristic_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_characteristic_locals_dict +}; diff --git a/shared-bindings/bleio/Characteristic.h b/shared-bindings/bleio/Characteristic.h new file mode 100644 index 000000000..dd67170d9 --- /dev/null +++ b/shared-bindings/bleio/Characteristic.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H + +#include "shared-module/bleio/Characteristic.h" + +extern const mp_obj_type_t bleio_characteristic_type; + +extern void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self); +extern void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index 4ad858ac9..f96abc449 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -30,6 +30,7 @@ #include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/AddressType.h" #include "shared-bindings/bleio/AdvertisementData.h" +#include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/Scanner.h" @@ -54,6 +55,7 @@ //| AddressType //| AdvertisementData //| Adapter +//| Characteristic //| Descriptor //| ScanEntry //| Scanner @@ -71,6 +73,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) }, { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, { MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) }, + { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h new file mode 100644 index 000000000..aa313afd3 --- /dev/null +++ b/shared-module/bleio/Characteristic.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H + +#include "common-hal/bleio/UUID.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t service; + uint16_t service_handle; + bleio_uuid_obj_t *uuid; + mp_obj_t value_data; + uint16_t handle; + struct { + bool broadcast : 1; + bool read : 1; + bool write_wo_resp : 1; + bool write : 1; + bool notify : 1; + bool indicate : 1; + } props; + uint16_t user_desc_handle; + uint16_t cccd_handle; + uint16_t sccd_handle; +} bleio_characteristic_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H -- cgit v1.2.3 From bda734223e75382be048d47fa9331afd7e9cfdce Mon Sep 17 00:00:00 2001 From: arturo182 Date: Thu, 19 Jul 2018 01:01:41 +0200 Subject: nrf: Move the Service class from ubluepy to the shared bleio module --- ports/nrf/Makefile | 2 +- ports/nrf/common-hal/bleio/Characteristic.c | 4 +- ports/nrf/common-hal/bleio/Service.c | 38 +++++ ports/nrf/drivers/bluetooth/ble_drv.c | 71 +++++----- ports/nrf/drivers/bluetooth/ble_drv.h | 3 +- ports/nrf/modules/ubluepy/modubluepy.c | 1 - ports/nrf/modules/ubluepy/modubluepy.h | 17 --- ports/nrf/modules/ubluepy/ubluepy_peripheral.c | 17 +-- ports/nrf/modules/ubluepy/ubluepy_service.c | 188 ------------------------- shared-bindings/bleio/Characteristic.c | 4 +- shared-bindings/bleio/Service.c | 167 ++++++++++++++++++++++ shared-bindings/bleio/Service.h | 38 +++++ shared-bindings/bleio/__init__.c | 4 +- shared-module/bleio/Characteristic.h | 3 +- shared-module/bleio/Service.h | 44 ++++++ 15 files changed, 346 insertions(+), 255 deletions(-) create mode 100644 ports/nrf/common-hal/bleio/Service.c delete mode 100644 ports/nrf/modules/ubluepy/ubluepy_service.c create mode 100644 shared-bindings/bleio/Service.c create mode 100644 shared-bindings/bleio/Service.h create mode 100644 shared-module/bleio/Service.h (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 6ed2d3054..6112678f5 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -130,7 +130,6 @@ SRC_C += \ DRIVERS_SRC_C += $(addprefix modules/,\ ubluepy/modubluepy.c \ ubluepy/ubluepy_peripheral.c \ - ubluepy/ubluepy_service.c \ ) SRC_COMMON_HAL += \ @@ -165,6 +164,7 @@ SRC_COMMON_HAL += \ bleio/Characteristic.c \ bleio/Descriptor.c \ bleio/Scanner.c \ + bleio/Service.c \ bleio/UUID.c endif diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index 3087b6707..56afc52c5 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -36,8 +36,8 @@ void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self } void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(self->service); - ubluepy_role_type_t role = service->p_periph->role; + ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(self->service->periph); + ubluepy_role_type_t role = peripheral->role; if (role == UBLUEPY_ROLE_PERIPHERAL) { // TODO: Add indications diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c new file mode 100644 index 000000000..b6e0595b1 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Service.c @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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 "ble_drv.h" +#include "shared-module/bleio/Service.h" + +void common_hal_bleio_service_construct(bleio_service_obj_t *self) { + ble_drv_service_add(self); +} + +void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic) { + if (ble_drv_characteristic_add(characteristic)) { + characteristic->service = self; + } +} diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 6240a18e4..e421036ae 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -267,26 +267,29 @@ bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx) { return true; } -bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj) { +void ble_drv_service_add(bleio_service_obj_t *service) { SD_TEST_OR_ENABLE(); - ble_uuid_t uuid; - uuid.type = BLE_UUID_TYPE_BLE; - uuid.uuid = p_service_obj->p_uuid->value[0] | (p_service_obj->p_uuid->value[1] << 8); + ble_uuid_t uuid = { + .type = BLE_UUID_TYPE_BLE, + .uuid = service->uuid->value[0] | (service->uuid->value[1] << 8) + }; - if (p_service_obj->p_uuid->type == UUID_TYPE_128BIT) { - uuid.type = p_service_obj->p_uuid->uuid_vs_idx; + if (service->uuid->type == UUID_TYPE_128BIT) { + uuid.type = service->uuid->uuid_vs_idx; } - uint32_t err_code = sd_ble_gatts_service_add(p_service_obj->type, - &uuid, - &p_service_obj->handle); - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not add Service. status: 0x%08lX"), err_code)); + uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; + if (service->is_secondary) { + service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; } - return true; + if (sd_ble_gatts_service_add(service_type, + &uuid, + &service->handle) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + translate("Can not add Service."))); + } } bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic) { @@ -320,7 +323,7 @@ bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic) { char_md.p_cccd_md = NULL; } - uuid.type = BLE_UUID_TYPE_BLE; + uuid.type = BLE_UUID_TYPE_BLE; if (characteristic->uuid->type == UUID_TYPE_128BIT) uuid.type = characteristic->uuid->uuid_vs_idx; @@ -416,12 +419,12 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { bool type_128bit_present = false; for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { - ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i]; - if (p_service->p_uuid->type == UUID_TYPE_16BIT) { + bleio_service_obj_t * p_service = (bleio_service_obj_t *)p_adv_params->p_services[i]; + if (p_service->uuid->type == UUID_TYPE_16BIT) { type_16bit_present = true; } - if (p_service->p_uuid->type == UUID_TYPE_128BIT) { + if (p_service->uuid->type == UUID_TYPE_128BIT) { type_128bit_present = true; } } @@ -439,12 +442,12 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { uint8_t encoded_size = 0; for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { - ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i]; + bleio_service_obj_t * p_service = (bleio_service_obj_t *)p_adv_params->p_services[i]; ble_uuid_t uuid; - uuid.type = p_service->p_uuid->type; - uuid.uuid = p_service->p_uuid->value[0]; - uuid.uuid += p_service->p_uuid->value[1] << 8; + uuid.type = p_service->uuid->type; + uuid.uuid = p_service->uuid->value[0]; + uuid.uuid += p_service->uuid->value[1] << 8; // calculate total size of uuids if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, @@ -488,12 +491,12 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { uint8_t encoded_size = 0; for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { - ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i]; + bleio_service_obj_t * p_service = (bleio_service_obj_t *)p_adv_params->p_services[i]; ble_uuid_t uuid; - uuid.type = p_service->p_uuid->uuid_vs_idx; - uuid.uuid = p_service->p_uuid->value[0]; - uuid.uuid += p_service->p_uuid->value[1] << 8; + uuid.type = p_service->uuid->uuid_vs_idx; + uuid.uuid = p_service->uuid->value[0]; + uuid.uuid += p_service->uuid->value[1] << 8; // calculate total size of uuids if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { @@ -640,8 +643,8 @@ void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, ui } void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); - uint16_t conn_handle = service->p_periph->conn_handle; + ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(characteristic->service->periph); + uint16_t conn_handle = peripheral->conn_handle; ble_gatts_value_t gatts_value; memset(&gatts_value, 0, sizeof(gatts_value)); @@ -659,8 +662,8 @@ void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_ } void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); - uint16_t conn_handle = service->p_periph->conn_handle; + ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(characteristic->service->periph); + uint16_t conn_handle = peripheral->conn_handle; ble_gatts_hvx_params_t hvx_params; uint16_t hvx_len = bufinfo->len; @@ -706,11 +709,13 @@ void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *self, ble_drv_adv_evt_c void ble_drv_attr_c_read(bleio_characteristic_obj_t *characteristic, ble_drv_gattc_char_data_callback_t cb) { - ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); + bleio_service_obj_t *service = characteristic->service; + ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(service->periph); + mp_gattc_char_data_observer = characteristic; gattc_char_data_handle = cb; - const uint32_t err_code = sd_ble_gattc_read(service->p_periph->conn_handle, + const uint32_t err_code = sd_ble_gattc_read(peripheral->conn_handle, characteristic->handle, 0); if (err_code != 0) { @@ -724,8 +729,8 @@ void ble_drv_attr_c_read(bleio_characteristic_obj_t *characteristic, ble_drv_gat } void ble_drv_attr_c_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - ubluepy_service_obj_t *service = MP_OBJ_TO_PTR(characteristic->service); - uint16_t conn_handle = service->p_periph->conn_handle; + ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(characteristic->service->periph); + uint16_t conn_handle = peripheral->conn_handle; ble_gattc_write_params_t write_params; write_params.write_op = BLE_GATT_OP_WRITE_REQ; diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h index 7283dbfce..8c6bd6ce3 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.h +++ b/ports/nrf/drivers/bluetooth/ble_drv.h @@ -34,6 +34,7 @@ #include "shared-module/bleio/Characteristic.h" #include "shared-module/bleio/Scanner.h" +#include "shared-module/bleio/Service.h" #include "modubluepy.h" @@ -92,7 +93,7 @@ void ble_drv_address_get(ble_drv_addr_t * p_addr); bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx); -bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj); +void ble_drv_service_add(bleio_service_obj_t *service); bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic); diff --git a/ports/nrf/modules/ubluepy/modubluepy.c b/ports/nrf/modules/ubluepy/modubluepy.c index 2d1a6a550..f8266a82d 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.c +++ b/ports/nrf/modules/ubluepy/modubluepy.c @@ -36,7 +36,6 @@ STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { #if MICROPY_PY_UBLUEPY_PERIPHERAL { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&ubluepy_peripheral_type) }, #endif - { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&ubluepy_service_type) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_ubluepy_globals, mp_module_ubluepy_globals_table); diff --git a/ports/nrf/modules/ubluepy/modubluepy.h b/ports/nrf/modules/ubluepy/modubluepy.h index ad33001cb..e301f0476 100644 --- a/ports/nrf/modules/ubluepy/modubluepy.h +++ b/ports/nrf/modules/ubluepy/modubluepy.h @@ -74,14 +74,8 @@ p.advertise(device_name="micr", services=[s]) #include "common-hal/bleio/UUID.h" #include "py/obj.h" -extern const mp_obj_type_t ubluepy_service_type; extern const mp_obj_type_t ubluepy_peripheral_type; -typedef enum { - UBLUEPY_SERVICE_PRIMARY = 1, - UBLUEPY_SERVICE_SECONDARY = 2 -} ubluepy_service_type_t; - typedef enum { UBLUEPY_ADDR_TYPE_PUBLIC = 0, UBLUEPY_ADDR_TYPE_RANDOM_STATIC = 1, @@ -104,17 +98,6 @@ typedef struct _ubluepy_peripheral_obj_t { mp_obj_t service_list; } ubluepy_peripheral_obj_t; -typedef struct _ubluepy_service_obj_t { - mp_obj_base_t base; - uint16_t handle; - uint8_t type; - bleio_uuid_obj_t * p_uuid; - ubluepy_peripheral_obj_t * p_periph; - mp_obj_t char_list; - uint16_t start_handle; - uint16_t end_handle; -} ubluepy_service_obj_t; - typedef struct _ubluepy_advertise_data_t { uint8_t * p_device_name; uint8_t device_name_len; diff --git a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c b/ports/nrf/modules/ubluepy/ubluepy_peripheral.c index 2a826db4d..3b1a0d279 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c +++ b/ports/nrf/modules/ubluepy/ubluepy_peripheral.c @@ -35,6 +35,7 @@ #include "ble_drv.h" #include "common-hal/bleio/UUID.h" #include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/UUID.h" STATIC void ubluepy_peripheral_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { @@ -271,9 +272,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_peripheral_disconnect_obj, peripheral_d /// STATIC mp_obj_t peripheral_add_service(mp_obj_t self_in, mp_obj_t service) { ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); - ubluepy_service_obj_t * p_service = MP_OBJ_TO_PTR(service); + bleio_service_obj_t * p_service = MP_OBJ_TO_PTR(service); - p_service->p_periph = self; + p_service->periph = self_in; mp_obj_list_append(self->service_list, service); @@ -294,13 +295,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_peripheral_get_services_obj, peripheral #if MICROPY_PY_UBLUEPY_CENTRAL void static disc_add_service(mp_obj_t self, ble_drv_service_data_t * p_service_data) { - ubluepy_service_obj_t * p_service = m_new_obj(ubluepy_service_obj_t); - p_service->base.type = &ubluepy_service_type; + bleio_service_obj_t * p_service = m_new_obj(bleio_service_obj_t); + p_service->base.type = &bleio_service_type; bleio_uuid_obj_t * p_uuid = m_new_obj(bleio_uuid_obj_t); p_uuid->base.type = &bleio_uuid_type; - p_service->p_uuid = p_uuid; + p_service->uuid = p_uuid; p_uuid->type = p_service_data->uuid_type; p_uuid->value[0] = p_service_data->uuid & 0xFF; @@ -316,7 +317,7 @@ void static disc_add_service(mp_obj_t self, ble_drv_service_data_t * p_service_d } void static disc_add_char(mp_obj_t service_in, ble_drv_char_data_t * p_desc_data) { - ubluepy_service_obj_t * p_service = MP_OBJ_TO_PTR(service_in); + bleio_service_obj_t * p_service = MP_OBJ_TO_PTR(service_in); bleio_characteristic_obj_t * p_char = m_new_obj(bleio_characteristic_obj_t); p_char->base.type = &bleio_characteristic_type; @@ -410,7 +411,7 @@ STATIC mp_obj_t peripheral_connect(mp_uint_t n_args, const mp_obj_t *pos_args, m mp_uint_t num_services; mp_obj_get_array(self->service_list, &num_services, &services); - ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)services[num_services - 1]; + bleio_service_obj_t * p_service = (bleio_service_obj_t *)services[num_services - 1]; service_disc_retval = ble_drv_discover_services(self, self->conn_handle, @@ -424,7 +425,7 @@ STATIC mp_obj_t peripheral_connect(mp_uint_t n_args, const mp_obj_t *pos_args, m mp_obj_get_array(self->service_list, &num_services, &services); for (uint16_t s = 0; s < num_services; s++) { - ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)services[s]; + bleio_service_obj_t * p_service = (bleio_service_obj_t *)services[s]; bool char_disc_retval = ble_drv_discover_characteristic(p_service, self->conn_handle, p_service->start_handle, diff --git a/ports/nrf/modules/ubluepy/ubluepy_service.c b/ports/nrf/modules/ubluepy/ubluepy_service.c deleted file mode 100644 index 3834c61de..000000000 --- a/ports/nrf/modules/ubluepy/ubluepy_service.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 "py/obj.h" -#include "py/runtime.h" -#include "py/objlist.h" -#include "supervisor/shared/translate.h" - -#if MICROPY_PY_UBLUEPY_PERIPHERAL || MICROPY_PY_UBLUEPY_CENTRAL - -#include "modubluepy.h" -#include "ble_drv.h" -#include "common-hal/bleio/UUID.h" -#include "shared-bindings/bleio/UUID.h" - -STATIC void ubluepy_service_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { - ubluepy_service_obj_t * self = (ubluepy_service_obj_t *)o; - - mp_printf(print, "Service(handle: 0x" HEX2_FMT ")", self->handle); -} - -STATIC mp_obj_t ubluepy_service_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - - enum { ARG_NEW_UUID, ARG_NEW_TYPE }; - - static const mp_arg_t allowed_args[] = { - { ARG_NEW_UUID, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { ARG_NEW_TYPE, MP_ARG_INT, {.u_int = UBLUEPY_SERVICE_PRIMARY} }, - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - ubluepy_service_obj_t *s = m_new_obj(ubluepy_service_obj_t); - s->base.type = type; - - mp_obj_t uuid_obj = args[ARG_NEW_UUID].u_obj; - - if (uuid_obj == MP_OBJ_NULL) { - return MP_OBJ_FROM_PTR(s); - } - - if (MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - s->p_uuid = MP_OBJ_TO_PTR(uuid_obj); - - uint8_t type = args[ARG_NEW_TYPE].u_int; - if (type > 0 && type <= UBLUEPY_SERVICE_PRIMARY) { - s->type = type; - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - translate("Invalid Service type"))); - } - - (void)ble_drv_service_add(s); - - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - translate("Invalid UUID parameter"))); - } - - // clear reference to peripheral - s->p_periph = NULL; - s->char_list = mp_obj_new_list(0, NULL); - - return MP_OBJ_FROM_PTR(s); -} - -/// \method addCharacteristic(Characteristic) -/// Add Characteristic to the Service. -/// -STATIC mp_obj_t service_add_characteristic(mp_obj_t self_in, mp_obj_t characteristic) { - ubluepy_service_obj_t * self = MP_OBJ_TO_PTR(self_in); - bleio_characteristic_obj_t * p_char = MP_OBJ_TO_PTR(characteristic); - - p_char->service_handle = self->handle; - - bool retval = ble_drv_characteristic_add(p_char); - if (retval) { - p_char->service = self_in; - } - - mp_obj_list_append(self->char_list, characteristic); - - // return mp_obj_new_bool(retval); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(ubluepy_service_add_char_obj, service_add_characteristic); - -/// \method getCharacteristics() -/// Return list with all characteristics registered in the Service. -/// -STATIC mp_obj_t service_get_chars(mp_obj_t self_in) { - ubluepy_service_obj_t * self = MP_OBJ_TO_PTR(self_in); - - return self->char_list; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_service_get_chars_obj, service_get_chars); - -/// \method getCharacteristic(UUID) -/// Return Characteristic with the given UUID. -/// -STATIC mp_obj_t service_get_characteristic(mp_obj_t self_in, mp_obj_t uuid) { - ubluepy_service_obj_t * self = MP_OBJ_TO_PTR(self_in); - bleio_uuid_obj_t * p_uuid = MP_OBJ_TO_PTR(uuid); - - // validate that there is an UUID object passed in as parameter - if (!(MP_OBJ_IS_TYPE(uuid, &bleio_uuid_type))) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - translate("Invalid UUID parameter"))); - } - - mp_obj_t * chars = NULL; - mp_uint_t num_chars = 0; - mp_obj_get_array(self->char_list, &num_chars, &chars); - - for (uint8_t i = 0; i < num_chars; i++) { - bleio_characteristic_obj_t * p_char = (bleio_characteristic_obj_t *)chars[i]; - - bool type_match = p_char->uuid->type == p_uuid->type; - bool uuid_match = ((uint16_t)(*(uint16_t *)&p_char->uuid->value[0]) == - (uint16_t)(*(uint16_t *)&p_uuid->value[0])); - - if (type_match && uuid_match) { - return MP_OBJ_FROM_PTR(p_char); - } - } - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(ubluepy_service_get_char_obj, service_get_characteristic); - -/// \method uuid() -/// Get UUID instance of the Service. -/// -STATIC mp_obj_t service_uuid(mp_obj_t self_in) { - ubluepy_service_obj_t * self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_FROM_PTR(self->p_uuid); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_service_get_uuid_obj, service_uuid); - -STATIC const mp_rom_map_elem_t ubluepy_service_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_getCharacteristic), MP_ROM_PTR(&ubluepy_service_get_char_obj) }, - { MP_ROM_QSTR(MP_QSTR_addCharacteristic), MP_ROM_PTR(&ubluepy_service_add_char_obj) }, - { MP_ROM_QSTR(MP_QSTR_getCharacteristics), MP_ROM_PTR(&ubluepy_service_get_chars_obj) }, -#if 0 - // Properties - { MP_ROM_QSTR(MP_QSTR_peripheral), MP_ROM_PTR(&ubluepy_service_get_peripheral_obj) }, -#endif - { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&ubluepy_service_get_uuid_obj) }, - { MP_ROM_QSTR(MP_QSTR_PRIMARY), MP_ROM_INT(UBLUEPY_SERVICE_PRIMARY) }, - { MP_ROM_QSTR(MP_QSTR_SECONDARY), MP_ROM_INT(UBLUEPY_SERVICE_SECONDARY) }, -}; - -STATIC MP_DEFINE_CONST_DICT(ubluepy_service_locals_dict, ubluepy_service_locals_dict_table); - -const mp_obj_type_t ubluepy_service_type = { - { &mp_type_type }, - .name = MP_QSTR_Service, - .print = ubluepy_service_print, - .make_new = ubluepy_service_make_new, - .locals_dict = (mp_obj_dict_t*)&ubluepy_service_locals_dict -}; - -#endif // MICROPY_PY_UBLUEPY_PERIPHERAL || MICROPY_PY_UBLUEPY_CENTRAL diff --git a/shared-bindings/bleio/Characteristic.c b/shared-bindings/bleio/Characteristic.c index fb96be703..97aed679e 100644 --- a/shared-bindings/bleio/Characteristic.c +++ b/shared-bindings/bleio/Characteristic.c @@ -91,14 +91,14 @@ STATIC void bleio_characteristic_print(const mp_print_t *print, mp_obj_t self_in bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "Characteristic(uuid: 0x"HEX2_FMT""HEX2_FMT" handle: 0x" HEX2_FMT ")", - self->uuid->value[0], self->uuid->value[1], self->handle); + self->uuid->value[1], self->uuid->value[0], self->handle); } STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { mp_arg_check_num(n_args, n_kw, 1, 1, true); bleio_characteristic_obj_t *self = m_new_obj(bleio_characteristic_obj_t); self->base.type = &bleio_characteristic_type; - self->service = mp_const_none; + self->service = NULL; self->value_data = NULL; mp_map_t kw_args; diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c new file mode 100644 index 000000000..6451991e9 --- /dev/null +++ b/shared-bindings/bleio/Service.c @@ -0,0 +1,167 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec + * + * 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 "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" + +//| .. currentmodule:: bleio +//| +//| :class:`Service` -- BLE service +//| ========================================================= +//| +//| Stores information about a BLE service and its characteristics. +//| + +//| .. class:: Service(uuid, secondary=False) +//| +//| Create a new Service object identified by the specified UUID. +//| To mark the service as secondary, pass `True` as :py:data:`secondary`. +//| +//| :param uuid: The uuid of the service +//| + +//| .. method:: add_characteristic(characteristic) +//| +//| Appends the :py:data:`characteristic` to the list of this service's characteristics. +//| +//| :param bleio.Characteristic characteristic: the characteristic to append +//| + +//| .. attribute:: characteristics +//| +//| A `list` of `bleio.Characteristic` that are offered by this service. (read-only) +//| + +//| .. attribute:: uuid +//| +//| The UUID of this service. (read-only) +//| +STATIC void bleio_service_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_printf(print, "Service(uuid: 0x"HEX2_FMT""HEX2_FMT")", + self->uuid->value[1], self->uuid->value[0]); +} + +STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, true); + bleio_service_obj_t *self = m_new_obj(bleio_service_obj_t); + self->base.type = &bleio_service_type; + self->periph = mp_const_none; + self->char_list = mp_obj_new_list(0, NULL); + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + enum { ARG_uuid, ARG_secondary }; + static const mp_arg_t allowed_args[] = { + { ARG_uuid, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self->is_secondary = args[ARG_secondary].u_bool; + + const mp_obj_t uuid = args[ARG_uuid].u_obj; + + if (uuid == MP_OBJ_NULL) { + return MP_OBJ_FROM_PTR(self); + } + + if (MP_OBJ_IS_TYPE(uuid, &bleio_uuid_type)) { + self->uuid = MP_OBJ_TO_PTR(uuid); + } else { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Invalid UUID parameter")); + } + + common_hal_bleio_service_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t bleio_service_add_characteristic(mp_obj_t self_in, mp_obj_t characteristic_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(characteristic_in); + + characteristic->service_handle = self->handle; + + common_hal_bleio_service_add_characteristic(self, characteristic); + + mp_obj_list_append(self->char_list, characteristic); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_service_add_characteristic_obj, bleio_service_add_characteristic); + +STATIC mp_obj_t bleio_service_get_characteristics(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return self->char_list; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_characteristics_obj, bleio_service_get_characteristics); + +const mp_obj_property_t bleio_service_characteristics_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_service_get_characteristics_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_service_get_uuid(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_FROM_PTR(self->uuid); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_uuid_obj, bleio_service_get_uuid); + +const mp_obj_property_t bleio_service_uuid_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_service_get_uuid_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_service_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_characteristic), MP_ROM_PTR(&bleio_service_add_characteristic_obj) }, + { MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict_table); + +const mp_obj_type_t bleio_service_type = { + { &mp_type_type }, + .name = MP_QSTR_Service, + .print = bleio_service_print, + .make_new = bleio_service_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_service_locals_dict +}; diff --git a/shared-bindings/bleio/Service.h b/shared-bindings/bleio/Service.h new file mode 100644 index 000000000..6e3079cf9 --- /dev/null +++ b/shared-bindings/bleio/Service.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H + +#include "shared-module/bleio/Characteristic.h" +#include "shared-module/bleio/Service.h" + +const mp_obj_type_t bleio_service_type; + +extern void common_hal_bleio_service_construct(bleio_service_obj_t *self); +extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index f96abc449..9f5a8f8f2 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -25,7 +25,6 @@ * THE SOFTWARE. */ -#include "py/obj.h" #include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/AddressType.h" @@ -34,6 +33,7 @@ #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/Scanner.h" +#include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/UUID.h" #include "shared-bindings/bleio/UUIDType.h" @@ -59,6 +59,7 @@ //| Descriptor //| ScanEntry //| Scanner +//| Service //| UUID //| UUIDType //| @@ -77,6 +78,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, + { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, { MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, // Properties diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h index aa313afd3..2347c9c30 100644 --- a/shared-module/bleio/Characteristic.h +++ b/shared-module/bleio/Characteristic.h @@ -27,11 +27,12 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H +#include "shared-module/bleio/Service.h" #include "common-hal/bleio/UUID.h" typedef struct { mp_obj_base_t base; - mp_obj_t service; + bleio_service_obj_t *service; uint16_t service_handle; bleio_uuid_obj_t *uuid; mp_obj_t value_data; diff --git a/shared-module/bleio/Service.h b/shared-module/bleio/Service.h new file mode 100644 index 000000000..bd359a41d --- /dev/null +++ b/shared-module/bleio/Service.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SERVICE_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SERVICE_H + +#include "modubluepy.h" +#include "common-hal/bleio/UUID.h" + +typedef struct { + mp_obj_base_t base; + uint16_t handle; + bool is_secondary; + bleio_uuid_obj_t *uuid; + mp_obj_t periph; + mp_obj_t char_list; + uint16_t start_handle; + uint16_t end_handle; +} bleio_service_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SERVICE_H -- cgit v1.2.3 From 3bd65fbae5d0150139c0ffd4cf02f8c9f547bbf5 Mon Sep 17 00:00:00 2001 From: arturo182 Date: Thu, 19 Jul 2018 20:33:24 +0200 Subject: nrf: Move the Peripheral class to bleio as Device This was the last class from ubluepy and so that module is now gone. The Device class offers both Peripheral and Central functionality. See the inline docs for more info. --- ports/nrf/Makefile | 5 +- ports/nrf/common-hal/bleio/Characteristic.c | 6 +- ports/nrf/common-hal/bleio/Device.c | 166 ++++++++ ports/nrf/common-hal/bleio/Scanner.c | 3 +- ports/nrf/drivers/bluetooth/ble_drv.c | 396 +++++++++---------- ports/nrf/drivers/bluetooth/ble_drv.h | 36 +- ports/nrf/drivers/bluetooth/ble_uart.h | 1 - ports/nrf/modules/ubluepy/modubluepy.c | 48 --- ports/nrf/modules/ubluepy/modubluepy.h | 126 ------ ports/nrf/modules/ubluepy/ubluepy_peripheral.c | 507 ------------------------- ports/nrf/mpconfigport.h | 33 +- shared-bindings/bleio/Device.c | 348 +++++++++++++++++ shared-bindings/bleio/Device.h | 40 ++ shared-bindings/bleio/ScanEntry.c | 5 +- shared-bindings/bleio/ScanEntry.h | 11 +- shared-bindings/bleio/Service.c | 6 +- shared-bindings/bleio/__init__.c | 3 + shared-module/bleio/AdvertisementData.h | 9 + shared-module/bleio/Device.h | 45 +++ shared-module/bleio/ScanEntry.h | 40 ++ shared-module/bleio/Service.h | 3 +- 21 files changed, 860 insertions(+), 977 deletions(-) create mode 100644 ports/nrf/common-hal/bleio/Device.c delete mode 100644 ports/nrf/modules/ubluepy/modubluepy.c delete mode 100644 ports/nrf/modules/ubluepy/modubluepy.h delete mode 100644 ports/nrf/modules/ubluepy/ubluepy_peripheral.c create mode 100644 shared-bindings/bleio/Device.c create mode 100644 shared-bindings/bleio/Device.h create mode 100644 shared-module/bleio/Device.h create mode 100644 shared-module/bleio/ScanEntry.h (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 6112678f5..b059569ab 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -127,10 +127,6 @@ SRC_C += \ peripherals/nrf/timers.c \ supervisor/shared/memory.c -DRIVERS_SRC_C += $(addprefix modules/,\ - ubluepy/modubluepy.c \ - ubluepy/ubluepy_peripheral.c \ - ) SRC_COMMON_HAL += \ analogio/AnalogIn.c \ @@ -163,6 +159,7 @@ SRC_COMMON_HAL += \ bleio/Adapter.c \ bleio/Characteristic.c \ bleio/Descriptor.c \ + bleio/Device.c \ bleio/Scanner.c \ bleio/Service.c \ bleio/UUID.c diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index 56afc52c5..938289f2d 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -26,6 +26,7 @@ #include "ble_drv.h" #include "shared-module/bleio/Characteristic.h" +#include "shared-module/bleio/Device.h" void data_callback(bleio_characteristic_obj_t *self, uint16_t length, uint8_t *data) { self->value_data = mp_obj_new_bytearray(length, data); @@ -36,10 +37,9 @@ void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self } void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(self->service->periph); - ubluepy_role_type_t role = peripheral->role; + const bleio_device_obj_t *device = MP_OBJ_TO_PTR(self->service->device); - if (role == UBLUEPY_ROLE_PERIPHERAL) { + if (device->is_peripheral) { // TODO: Add indications if (self->props.notify) { ble_drv_attr_s_notify(self, bufinfo); diff --git a/ports/nrf/common-hal/bleio/Device.c b/ports/nrf/common-hal/bleio/Device.c new file mode 100644 index 000000000..ab2898e72 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Device.c @@ -0,0 +1,166 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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 + +#include "ble_drv.h" +#include "ble_gap.h" +#include "ble_gatt.h" +#include "ble_types.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Device.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" + +static volatile bool m_disc_evt_received; + +STATIC void gap_event_handler(bleio_device_obj_t *device, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data) { + if (event_id == BLE_GAP_EVT_CONNECTED) { + device->conn_handle = conn_handle; + } else if (event_id == BLE_GAP_EVT_DISCONNECTED) { + device->conn_handle = BLE_CONN_HANDLE_INVALID; + } +} + +STATIC void gatts_event_handler(bleio_device_obj_t *device, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) { + +} + +STATIC void gattc_event_handler(bleio_device_obj_t *device, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) { + m_disc_evt_received = true; +} + +STATIC void disc_add_service(bleio_device_obj_t *device, ble_drv_service_data_t * service_data) { + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + uuid->type = (service_data->uuid_type == BLE_UUID_TYPE_BLE) ? UUID_TYPE_16BIT : UUID_TYPE_128BIT; + uuid->value[0] = service_data->uuid & 0xFF; + uuid->value[1] = service_data->uuid >> 8; + + service->char_list = mp_obj_new_list(0, NULL); + service->uuid = uuid; + service->device = device; + service->handle = service_data->start_handle; + service->start_handle = service_data->start_handle; + service->end_handle = service_data->end_handle; + + mp_obj_list_append(device->service_list, service); +} + +STATIC void disc_add_char(bleio_service_obj_t *service, ble_drv_char_data_t *chara_data) { + bleio_characteristic_obj_t *chara = m_new_obj(bleio_characteristic_obj_t); + chara->base.type = &bleio_characteristic_type; + + bleio_uuid_obj_t * p_uuid = m_new_obj(bleio_uuid_obj_t); + p_uuid->base.type = &bleio_uuid_type; + + chara->uuid = p_uuid; + + p_uuid->type = chara_data->uuid_type; + p_uuid->value[0] = chara_data->uuid & 0xFF; + p_uuid->value[1] = chara_data->uuid >> 8; + + // add characteristic specific data from discovery + chara->props.broadcast = chara_data->props.broadcast; + chara->props.indicate = chara_data->props.indicate; + chara->props.notify = chara_data->props.notify; + chara->props.read = chara_data->props.read; + chara->props.write = chara_data->props.write; + chara->props.write_wo_resp = chara_data->props.write_wo_resp; + chara->handle = chara_data->value_handle; + + chara->service_handle = service->handle; + chara->service = service; + + mp_obj_list_append(service->char_list, MP_OBJ_FROM_PTR(chara)); +} + + +void common_hal_bleio_device_start_advertising(bleio_device_obj_t *device, bleio_advertisement_data_t *adv_data) { + if (adv_data->connectable) { + ble_drv_gap_event_handler_set(device, gap_event_handler); + ble_drv_gatts_event_handler_set(device, gatts_event_handler); + } + + ble_drv_advertise_data(adv_data); +} + +void common_hal_bleio_device_stop_advertising(bleio_device_obj_t *device) { + (void)device; + + ble_drv_advertise_stop(); +} + +void common_hal_bleio_device_connect(bleio_device_obj_t *device) { + ble_drv_gap_event_handler_set(device, gap_event_handler); + + ble_drv_connect(device); + + while (device->conn_handle == BLE_CONN_HANDLE_INVALID) { + run_background_tasks(); +// __asm volatile ("wfi"); + } + + ble_drv_gattc_event_handler_set(device, gattc_event_handler); + + // TODO: read name + + // find services + bool found_service = ble_drv_discover_services(device, BLE_GATT_HANDLE_START, disc_add_service); + while (found_service) { + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(device->service_list); + const bleio_service_obj_t *service = service_list->items[service_list->len - 1]; + + found_service = ble_drv_discover_services(device, service->end_handle + 1, disc_add_service); + } + + // find characteristics in each service + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(device->service_list); + for (size_t i = 0; i < service_list->len; ++i) { + bleio_service_obj_t *service = service_list->items[i]; + + bool found_char = ble_drv_discover_characteristic(device, service, service->start_handle, disc_add_char); + while (found_char) { + const mp_obj_list_t *char_list = MP_OBJ_TO_PTR(service->char_list); + const bleio_characteristic_obj_t *characteristic = char_list->items[char_list->len - 1]; + + const uint16_t next_handle = characteristic->handle + 1; + if (next_handle >= service->end_handle) { + break; + } + + found_char = ble_drv_discover_characteristic(device, service, next_handle, disc_add_char); + } + } +} + +void common_hal_bleio_device_disconnect(bleio_device_obj_t *device) { + ble_drv_disconnect(device); +} diff --git a/ports/nrf/common-hal/bleio/Scanner.c b/ports/nrf/common-hal/bleio/Scanner.c index ed1987144..0eb50a1f6 100644 --- a/ports/nrf/common-hal/bleio/Scanner.c +++ b/ports/nrf/common-hal/bleio/Scanner.c @@ -29,8 +29,9 @@ #include "ble_drv.h" #include "py/mphal.h" -#include "shared-bindings/bleio/Scanner.h" #include "shared-bindings/bleio/ScanEntry.h" +#include "shared-bindings/bleio/Scanner.h" +#include "shared-module/bleio/ScanEntry.h" STATIC void adv_event_handler(bleio_scanner_obj_t *self, ble_drv_adv_data_t *data) { // TODO: Don't add new entry for each item, group by address and update diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index e421036ae..1ab1d9337 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2016 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,6 +35,8 @@ #define NRF52 // Needed for SD132 v2 #endif +#include "shared-module/bleio/Device.h" +#include "py/objstr.h" #include "py/runtime.h" #include "supervisor/shared/translate.h" #include "ble_drv.h" @@ -41,15 +44,10 @@ #include "nrf_sdm.h" #include "nrfx_power.h" #include "ble_gap.h" +#include "ble_hci.h" #include "ble.h" // sd_ble_uuid_encode - -#define BLE_DRIVER_VERBOSE 0 -#if BLE_DRIVER_VERBOSE #define BLE_DRIVER_LOG printf -#else -#define BLE_DRIVER_LOG(...) -#endif #define BLE_ADV_LENGTH_FIELD_SIZE 1 #define BLE_ADV_AD_TYPE_FIELD_SIZE 1 @@ -61,8 +59,8 @@ #define APP_CFG_NON_CONN_ADV_TIMEOUT 0 // Disable timeout. #define NON_CONNECTABLE_ADV_INTERVAL MSEC_TO_UNITS(100, UNIT_0_625_MS) -#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(12, UNIT_0_625_MS) -#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(12, UNIT_0_625_MS) +#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) +#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS) #define BLE_SLAVE_LATENCY 0 #define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) @@ -81,8 +79,8 @@ static volatile bool m_tx_in_progress; static ble_drv_gap_evt_callback_t gap_event_handler; static ble_drv_gatts_evt_callback_t gatts_event_handler; -static mp_obj_t mp_gap_observer; -static mp_obj_t mp_gatts_observer; +static bleio_device_obj_t *mp_gap_observer; +static bleio_device_obj_t *mp_gatts_observer; static volatile bool m_primary_service_found; static volatile bool m_characteristic_found; @@ -95,10 +93,11 @@ static volatile ble_drv_disc_add_char_callback_t disc_add_char_handler; static volatile ble_drv_gattc_char_data_callback_t gattc_char_data_handle; static bleio_scanner_obj_t *mp_adv_observer; -static mp_obj_t mp_gattc_observer; -static mp_obj_t mp_gattc_disc_service_observer; -static mp_obj_t mp_gattc_disc_char_observer; +static bleio_device_obj_t *mp_gattc_observer; +static bleio_device_obj_t *mp_gattc_disc_service_observer; +static bleio_service_obj_t *mp_gattc_disc_char_observer; static bleio_characteristic_obj_t *mp_gattc_char_data_observer; +static bleio_address_obj_t *mp_connect_address; #if (BLUETOOTH_SD == 140) static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; @@ -123,18 +122,6 @@ uint32_t ble_drv_stack_enable(void) { m_adv_in_progress = false; m_tx_in_progress = false; -#if BLUETOOTH_LFCLK_RC - nrf_clock_lf_cfg_t clock_config = { - .source = NRF_CLOCK_LF_SRC_RC, - .rc_ctiv = 16, - .rc_temp_ctiv = 2, -#if (BLE_API_VERSION == 4) - .accuracy = 0 -#else - .xtal_accuracy = 0 -#endif - }; -#else nrf_clock_lf_cfg_t clock_config = { .source = NRF_CLOCK_LF_SRC_XTAL, .rc_ctiv = 0, @@ -145,23 +132,22 @@ uint32_t ble_drv_stack_enable(void) { .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM #endif }; -#endif #if (BLUETOOTH_SD == 140) // The SD takes over the POWER IRQ and will fail if the IRQ is already in use nrfx_power_uninit(); #endif - uint32_t err_code = sd_softdevice_enable(&clock_config, - softdevice_assert_handler); - - BLE_DRIVER_LOG("SoftDevice enable status: " UINT_FMT "\n", (uint16_t)err_code); + uint32_t err_code = sd_softdevice_enable(&clock_config, softdevice_assert_handler); + if (err_code != NRF_SUCCESS) + BLE_DRIVER_LOG("SoftDevice enable status: " UINT_FMT "\n", (uint16_t)err_code); - err_code = sd_nvic_EnableIRQ(SWI2_EGU2_IRQn); - - BLE_DRIVER_LOG("IRQ enable status: " UINT_FMT "\n", (uint16_t)err_code); + err_code = sd_nvic_EnableIRQ(SD_EVT_IRQn); + if (err_code != NRF_SUCCESS) + BLE_DRIVER_LOG("IRQ enable status: " UINT_FMT "\n", (uint16_t)err_code); // Enable BLE stack. + uint32_t app_ram_start; #if (BLE_API_VERSION == 2) ble_enable_params_t ble_enable_params; memset(&ble_enable_params, 0x00, sizeof(ble_enable_params)); @@ -169,48 +155,17 @@ uint32_t ble_drv_stack_enable(void) { ble_enable_params.gatts_enable_params.service_changed = 0; ble_enable_params.gap_enable_params.periph_conn_count = 1; ble_enable_params.gap_enable_params.central_conn_count = 1; -#endif -#if (BLE_API_VERSION == 2) - uint32_t app_ram_start = 0x200039c0; + app_ram_start = 0x200039c0; err_code = sd_ble_enable(&ble_enable_params, &app_ram_start); // 8K SD headroom from linker script. - BLE_DRIVER_LOG("BLE ram size: " UINT_FMT "\n", (uint16_t)app_ram_start); #else - uint32_t app_ram_start = 0x20004000; + app_ram_start = 0x20004000; err_code = sd_ble_enable(&app_ram_start); - BLE_DRIVER_LOG("BLE ram size: " UINT_FMT "\n", (uint16_t)app_ram_start); #endif - - BLE_DRIVER_LOG("BLE enable status: " UINT_FMT "\n", (uint16_t)err_code); - - // set up security mode - ble_gap_conn_params_t gap_conn_params; - ble_gap_conn_sec_mode_t sec_mode; - - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); - - const char device_name[] = "micr"; - - if ((err_code = sd_ble_gap_device_name_set(&sec_mode, - (const uint8_t *)device_name, - strlen(device_name))) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Cannot apply GAP parameters."))); - } - - // set connection parameters - memset(&gap_conn_params, 0, sizeof(gap_conn_params)); - - gap_conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL; - gap_conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL; - gap_conn_params.slave_latency = BLE_SLAVE_LATENCY; - gap_conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT; - - if (sd_ble_gap_ppcp_set(&gap_conn_params) != 0) { - - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Cannot set PPCP parameters."))); + if (err_code != NRF_SUCCESS) { + BLE_DRIVER_LOG("BLE ram size: " UINT_FMT "\n", (uint16_t)app_ram_start); + BLE_DRIVER_LOG("BLE enable status: " UINT_FMT "\n", (uint16_t)err_code); } return err_code; @@ -223,9 +178,10 @@ void ble_drv_stack_disable(void) { uint8_t ble_drv_stack_enabled(void) { uint8_t is_enabled; uint32_t err_code = sd_softdevice_is_enabled(&is_enabled); - (void)err_code; - BLE_DRIVER_LOG("Is enabled status: " UINT_FMT "\n", (uint16_t)err_code); + if (err_code != NRF_SUCCESS) { + BLE_DRIVER_LOG("Is enabled status: " UINT_FMT "\n", (uint16_t)err_code); + } return is_enabled; } @@ -256,10 +212,10 @@ void ble_drv_address_get(ble_drv_addr_t * p_addr) { memcpy(p_addr->addr, local_ble_addr.addr, 6); } -bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx) { +bool ble_drv_uuid_add_vs(uint8_t *uuid, uint8_t *idx) { SD_TEST_OR_ENABLE(); - if (sd_ble_uuid_vs_add((ble_uuid128_t const *)p_uuid, idx) != 0) { + if (sd_ble_uuid_vs_add((ble_uuid128_t const *)uuid, idx) != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, translate("Can not add Vendor Specific 128-bit UUID."))); } @@ -284,9 +240,7 @@ void ble_drv_service_add(bleio_service_obj_t *service) { service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; } - if (sd_ble_gatts_service_add(service_type, - &uuid, - &service->handle) != 0) { + if (sd_ble_gatts_service_add(service_type, &uuid, &service->handle) != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, translate("Can not add Service."))); } @@ -372,59 +326,66 @@ bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic) { return true; } -bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { +// TODO: Replace with just bleio_device_obj_t + data +bool ble_drv_advertise_data(bleio_advertisement_data_t *adv_params) { SD_TEST_OR_ENABLE(); uint8_t byte_pos = 0; uint8_t adv_data[BLE_GAP_ADV_MAX_SIZE]; - if (p_adv_params->device_name_len > 0) { - ble_gap_conn_sec_mode_t sec_mode; + GET_STR_DATA_LEN(adv_params->device_name, name_data, name_len); + if (name_len > 0) { + ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); if (sd_ble_gap_device_name_set(&sec_mode, - p_adv_params->p_device_name, - p_adv_params->device_name_len) != 0) { + name_data, + name_len) != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, translate("Can not apply device name in the stack."))); } - BLE_DRIVER_LOG("Device name applied\n"); - - adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + p_adv_params->device_name_len); + adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + name_len); byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + + // TODO: Shorten if too long adv_data[byte_pos] = BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME; byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE; - memcpy(&adv_data[byte_pos], p_adv_params->p_device_name, p_adv_params->device_name_len); - // increment position counter to see if it fits, and in case more content should - // follow in this adv packet. - byte_pos += p_adv_params->device_name_len; + + memcpy(&adv_data[byte_pos], name_data, name_len); + + byte_pos += name_len; } - // Add FLAGS only if manually controlled data has not been used. - if (p_adv_params->data_len == 0) { - // set flags, default to disc mode + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(adv_params->data, &bufinfo, MP_BUFFER_WRITE); + + // set flags, default to disc mode + if (bufinfo.len == 0) { adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + BLE_AD_TYPE_FLAGS_DATA_SIZE); byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + adv_data[byte_pos] = BLE_GAP_AD_TYPE_FLAGS; byte_pos += BLE_AD_TYPE_FLAGS_DATA_SIZE; + adv_data[byte_pos] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; byte_pos += 1; } - if (p_adv_params->num_of_services > 0) { - + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(adv_params->services); + if (service_list->len > 0) { bool type_16bit_present = false; bool type_128bit_present = false; - for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { - bleio_service_obj_t * p_service = (bleio_service_obj_t *)p_adv_params->p_services[i]; - if (p_service->uuid->type == UUID_TYPE_16BIT) { + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + + if (service->uuid->type == UUID_TYPE_16BIT) { type_16bit_present = true; } - if (p_service->uuid->type == UUID_TYPE_128BIT) { + if (service->uuid->type == UUID_TYPE_128BIT) { type_128bit_present = true; } } @@ -441,13 +402,17 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { uint8_t uuid_total_size = 0; uint8_t encoded_size = 0; - for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { - bleio_service_obj_t * p_service = (bleio_service_obj_t *)p_adv_params->p_services[i]; + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + + if (service->uuid->type != UUID_TYPE_16BIT) { + continue; + } ble_uuid_t uuid; - uuid.type = p_service->uuid->type; - uuid.uuid = p_service->uuid->value[0]; - uuid.uuid += p_service->uuid->value[1] << 8; + uuid.type = BLE_UUID_TYPE_BLE; + uuid.uuid = service->uuid->value[0] | (service->uuid->value[1] << 8); + // calculate total size of uuids if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, @@ -460,19 +425,8 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { translate("Can encode UUID into the advertisement packet."))); } - BLE_DRIVER_LOG("encoded uuid for service %u: ", 0); - for (uint8_t j = 0; j < encoded_size; j++) { - BLE_DRIVER_LOG(HEX2_FMT " ", adv_data[byte_pos + j]); - } - BLE_DRIVER_LOG("\n"); - uuid_total_size += encoded_size; // size of entry byte_pos += encoded_size; // relative to adv data packet - BLE_DRIVER_LOG("ADV: uuid size: %u, type: %u, uuid: %x%x, vs_idx: %u\n", - encoded_size, p_service->p_uuid->type, - p_service->p_uuid->value[1], - p_service->p_uuid->value[0], - p_service->p_uuid->uuid_vs_idx); } adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); @@ -490,13 +444,16 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { uint8_t uuid_total_size = 0; uint8_t encoded_size = 0; - for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { - bleio_service_obj_t * p_service = (bleio_service_obj_t *)p_adv_params->p_services[i]; + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + + if (service->uuid->type != UUID_TYPE_128BIT) { + continue; + } ble_uuid_t uuid; - uuid.type = p_service->uuid->uuid_vs_idx; - uuid.uuid = p_service->uuid->value[0]; - uuid.uuid += p_service->uuid->value[1] << 8; + uuid.type = service->uuid->uuid_vs_idx; + uuid.uuid = service->uuid->value[0] | (service->uuid->value[1] << 8); // calculate total size of uuids if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { @@ -510,51 +467,37 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { translate("Can encode UUID into the advertisement packet."))); } - BLE_DRIVER_LOG("encoded uuid for service %u: ", 0); - for (uint8_t j = 0; j < encoded_size; j++) { - BLE_DRIVER_LOG(HEX2_FMT " ", adv_data[byte_pos + j]); - } - BLE_DRIVER_LOG("\n"); - uuid_total_size += encoded_size; // size of entry byte_pos += encoded_size; // relative to adv data packet - BLE_DRIVER_LOG("ADV: uuid size: %u, type: %x%x, uuid: %u, vs_idx: %u\n", - encoded_size, p_service->p_uuid->type, - p_service->p_uuid->value[1], - p_service->p_uuid->value[0], - p_service->p_uuid->uuid_vs_idx); } adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); } } - if ((p_adv_params->data_len > 0) && (p_adv_params->p_data != NULL)) { - if (p_adv_params->data_len + byte_pos > BLE_GAP_ADV_MAX_SIZE) { + if (bufinfo.len > 0) { + if (byte_pos + bufinfo.len > BLE_GAP_ADV_MAX_SIZE) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, translate("Can not fit data into the advertisement packet."))); } - memcpy(adv_data, p_adv_params->p_data, p_adv_params->data_len); - byte_pos += p_adv_params->data_len; + memcpy(adv_data, bufinfo.buf, bufinfo.len); + byte_pos += bufinfo.len; } - // scan response data not set uint32_t err_code; #if (BLUETOOTH_SD == 132) if ((err_code = sd_ble_gap_adv_data_set(adv_data, byte_pos, NULL, 0)) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, translate("Can not apply advertisement data. status: 0x%02x"), (uint16_t)err_code)); } - BLE_DRIVER_LOG("Set Adv data size: " UINT_FMT "\n", byte_pos); #endif static ble_gap_adv_params_t m_adv_params; // initialize advertising params memset(&m_adv_params, 0, sizeof(m_adv_params)); - if (p_adv_params->connectable) { + if (adv_params->connectable) { #if (BLUETOOTH_SD == 140) m_adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; #else @@ -643,8 +586,8 @@ void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, ui } void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(characteristic->service->periph); - uint16_t conn_handle = peripheral->conn_handle; + bleio_device_obj_t *device = MP_OBJ_TO_PTR(characteristic->service->device); + uint16_t conn_handle = device->conn_handle; ble_gatts_value_t gatts_value; memset(&gatts_value, 0, sizeof(gatts_value)); @@ -662,8 +605,8 @@ void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_ } void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(characteristic->service->periph); - uint16_t conn_handle = peripheral->conn_handle; + bleio_device_obj_t *device = MP_OBJ_TO_PTR(characteristic->service->device); + uint16_t conn_handle = device->conn_handle; ble_gatts_hvx_params_t hvx_params; uint16_t hvx_len = bufinfo->len; @@ -676,7 +619,9 @@ void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer hvx_params.p_data = bufinfo->buf; while (m_tx_in_progress) { - ; +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif } m_tx_in_progress = true; @@ -687,50 +632,49 @@ void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer } } -void ble_drv_gap_event_handler_set(mp_obj_t obj, ble_drv_gap_evt_callback_t evt_handler) { - mp_gap_observer = obj; +void ble_drv_gap_event_handler_set(bleio_device_obj_t *device, ble_drv_gap_evt_callback_t evt_handler) { + mp_gap_observer = device; gap_event_handler = evt_handler; } -void ble_drv_gatts_event_handler_set(mp_obj_t obj, ble_drv_gatts_evt_callback_t evt_handler) { - mp_gatts_observer = obj; +void ble_drv_gatts_event_handler_set(bleio_device_obj_t *device, ble_drv_gatts_evt_callback_t evt_handler) { + mp_gatts_observer = device; gatts_event_handler = evt_handler; } -void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t evt_handler) { - mp_gattc_observer = obj; +void ble_drv_gattc_event_handler_set(bleio_device_obj_t *device, ble_drv_gattc_evt_callback_t evt_handler) { + mp_gattc_observer = device; gattc_event_handler = evt_handler; } -void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *self, ble_drv_adv_evt_callback_t evt_handler) { - mp_adv_observer = self; +void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *device, ble_drv_adv_evt_callback_t evt_handler) { + mp_adv_observer = device; adv_event_handler = evt_handler; } - void ble_drv_attr_c_read(bleio_characteristic_obj_t *characteristic, ble_drv_gattc_char_data_callback_t cb) { bleio_service_obj_t *service = characteristic->service; - ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(service->periph); + bleio_device_obj_t *device = MP_OBJ_TO_PTR(service->device); mp_gattc_char_data_observer = characteristic; gattc_char_data_handle = cb; - const uint32_t err_code = sd_ble_gattc_read(peripheral->conn_handle, - characteristic->handle, - 0); + const uint32_t err_code = sd_ble_gattc_read(device->conn_handle, characteristic->handle, 0); if (err_code != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, translate("Can not read attribute value. status: 0x%02x"), (uint16_t)err_code)); } while (gattc_char_data_handle != NULL) { - ; +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif } } void ble_drv_attr_c_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - ubluepy_peripheral_obj_t *peripheral = MP_OBJ_TO_PTR(characteristic->service->periph); - uint16_t conn_handle = peripheral->conn_handle; + bleio_device_obj_t *device = MP_OBJ_TO_PTR(characteristic->service->device); + uint16_t conn_handle = device->conn_handle; ble_gattc_write_params_t write_params; write_params.write_op = BLE_GATT_OP_WRITE_REQ; @@ -753,10 +697,13 @@ void ble_drv_attr_c_write(bleio_characteristic_obj_t *characteristic, mp_buffer_ translate("Can not write attribute value. status: 0x%02x"), (uint16_t)err_code)); } - while (m_write_done != true) { - ; + while (m_write_done != true) { +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif } } + void ble_drv_scan_start(uint16_t interval, uint16_t window) { SD_TEST_OR_ENABLE(); @@ -798,109 +745,108 @@ void ble_drv_scan_stop(void) { sd_ble_gap_scan_stop(); } -void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type) { - SD_TEST_OR_ENABLE(); +STATIC void ble_drv_connect_scan_callback(bleio_scanner_obj_t *scanner, ble_drv_adv_data_t *data) { + if (memcmp(data->p_peer_addr, mp_connect_address->value, BLEIO_ADDRESS_BYTES) == 0) { + ble_drv_adv_report_handler_set(NULL, NULL); - ble_gap_scan_params_t scan_params; - scan_params.active = 1; - scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); - scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS); - scan_params.timeout = 0; // Infinite + ble_gap_scan_params_t scan_params; + memset(&scan_params, 0, sizeof(scan_params)); - ble_gap_addr_t addr; - memset(&addr, 0, sizeof(addr)); + scan_params.active = 1; + scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); + scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS); + scan_params.timeout = 0; - addr.addr_type = addr_type; - memcpy(addr.addr, p_addr, 6); + ble_gap_addr_t addr; + memset(&addr, 0, sizeof(addr)); - BLE_DRIVER_LOG("GAP CONNECTING: "HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT", type: %d\n", - addr.addr[0], addr.addr[1], addr.addr[2], addr.addr[3], addr.addr[4], addr.addr[5], addr.addr_type); + addr.addr_type = data->addr_type; + memcpy(addr.addr, data->p_peer_addr, BLEIO_ADDRESS_BYTES); - ble_gap_conn_params_t conn_params; + BLE_DRIVER_LOG("GAP CONNECTING: "HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT", type: %d\n", + addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2], addr.addr[1], addr.addr[0], addr.addr_type); + + ble_gap_conn_params_t conn_params = { + .min_conn_interval = BLE_MIN_CONN_INTERVAL, + .max_conn_interval = BLE_MAX_CONN_INTERVAL, + .conn_sup_timeout = BLE_CONN_SUP_TIMEOUT, + .slave_latency = BLE_SLAVE_LATENCY, + }; + + uint32_t err_code; + #if (BLE_API_VERSION == 2) + if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params)) != 0) { + #else + if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_DEFAULT)) != 0) { + #endif + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not connect. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } + } +} -// (void)sd_ble_gap_ppcp_get(&conn_params); +void ble_drv_connect(bleio_device_obj_t *device) { + SD_TEST_OR_ENABLE(); - // set connection parameters - memset(&conn_params, 0, sizeof(conn_params)); + mp_connect_address = &device->address; + ble_drv_adv_report_handler_set(NULL, ble_drv_connect_scan_callback); - conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL; - conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL; - conn_params.slave_latency = BLE_SLAVE_LATENCY; - conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT; + ble_drv_scan_start(100, 100); +} - uint32_t err_code; -#if (BLE_API_VERSION == 2) - if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params)) != 0) { -#else - if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_DEFAULT)) != 0) { -#endif - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not connect. status: 0x%02x"), (uint16_t)err_code)); - } +void ble_drv_disconnect(bleio_device_obj_t *device) { + sd_ble_gap_disconnect(device->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); } -bool ble_drv_discover_services(mp_obj_t obj, uint16_t conn_handle, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb) { - BLE_DRIVER_LOG("Discover primary services. Conn handle: 0x" HEX2_FMT "\n", - conn_handle); +bool ble_drv_discover_services(bleio_device_obj_t *device, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb) { + BLE_DRIVER_LOG("Discover primary services. Conn handle: 0x" HEX2_FMT "\n", device->conn_handle); - mp_gattc_disc_service_observer = obj; + mp_gattc_disc_service_observer = device; disc_add_service_handler = cb; m_primary_service_found = false; uint32_t err_code; - err_code = sd_ble_gattc_primary_services_discover(conn_handle, - start_handle, - NULL); + err_code = sd_ble_gattc_primary_services_discover(device->conn_handle, start_handle, NULL); if (err_code != 0) { return false; } // busy loop until last service has been iterated while (disc_add_service_handler != NULL) { - ; +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif } - if (m_primary_service_found) { - return true; - } else { - return false; - } + return m_primary_service_found; } -bool ble_drv_discover_characteristic(mp_obj_t obj, - uint16_t conn_handle, - uint16_t start_handle, - uint16_t end_handle, - ble_drv_disc_add_char_callback_t cb) { - BLE_DRIVER_LOG("Discover characteristicts. Conn handle: 0x" HEX2_FMT "\n", - conn_handle); +bool ble_drv_discover_characteristic(bleio_device_obj_t *device, bleio_service_obj_t *service, uint16_t start_handle, ble_drv_disc_add_char_callback_t cb) { + BLE_DRIVER_LOG("Discover characteristicts. Conn handle: 0x" HEX2_FMT "\n", device->conn_handle); - mp_gattc_disc_char_observer = obj; + mp_gattc_disc_char_observer = service; disc_add_char_handler = cb; ble_gattc_handle_range_t handle_range; handle_range.start_handle = start_handle; - handle_range.end_handle = end_handle; + handle_range.end_handle = service->end_handle; m_characteristic_found = false; - uint32_t err_code; - err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range); + uint32_t err_code = sd_ble_gattc_characteristics_discover(device->conn_handle, &handle_range); if (err_code != 0) { return false; } // busy loop until last service has been iterated while (disc_add_char_handler != NULL) { - ; +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif } - if (m_characteristic_found) { - return true; - } else { - return false; - } + return m_characteristic_found; } void ble_drv_discover_descriptors(void) { @@ -908,12 +854,8 @@ void ble_drv_discover_descriptors(void) { } static void ble_evt_handler(ble_evt_t * p_ble_evt) { -// S132 event ranges. -// Common 0x01 -> 0x0F -// GAP 0x10 -> 0x2F -// GATTC 0x30 -> 0x4F -// GATTS 0x50 -> 0x6F -// L2CAP 0x70 -> 0x8F + printf("%s - 0x%02X\r\n", __func__, p_ble_evt->header.evt_id); + switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: BLE_DRIVER_LOG("GAP CONNECT\n"); @@ -1104,7 +1046,7 @@ static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (GATT_MTU_SIZE_DEFAULT)] __attr static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (BLE_GATT_ATT_MTU_DEFAULT)] __attribute__ ((aligned (4))); #endif -void SWI2_EGU2_IRQHandler(void) { +void SD_EVT_IRQHandler(void) { uint32_t evt_id; uint32_t err_code; do { diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h index 8c6bd6ce3..e5282db73 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.h +++ b/ports/nrf/drivers/bluetooth/ble_drv.h @@ -32,12 +32,12 @@ #include #include +#include "shared-module/bleio/AdvertisementData.h" #include "shared-module/bleio/Characteristic.h" +#include "shared-module/bleio/Device.h" #include "shared-module/bleio/Scanner.h" #include "shared-module/bleio/Service.h" -#include "modubluepy.h" - typedef struct { uint8_t addr[6]; uint8_t addr_type; @@ -75,12 +75,12 @@ typedef struct { uint16_t value_handle; } ble_drv_char_data_t; -typedef void (*ble_drv_gap_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data); -typedef void (*ble_drv_gatts_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); -typedef void (*ble_drv_gattc_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); -typedef void (*ble_drv_adv_evt_callback_t)(bleio_scanner_obj_t *self, ble_drv_adv_data_t *data); -typedef void (*ble_drv_disc_add_service_callback_t)(mp_obj_t self, ble_drv_service_data_t * p_service_data); -typedef void (*ble_drv_disc_add_char_callback_t)(mp_obj_t self, ble_drv_char_data_t * p_desc_data); +typedef void (*ble_drv_gap_evt_callback_t)(bleio_device_obj_t *device, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data); +typedef void (*ble_drv_gatts_evt_callback_t)(bleio_device_obj_t *device, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); +typedef void (*ble_drv_gattc_evt_callback_t)(bleio_device_obj_t *device, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); +typedef void (*ble_drv_adv_evt_callback_t)(bleio_scanner_obj_t *scanner, ble_drv_adv_data_t *data); +typedef void (*ble_drv_disc_add_service_callback_t)(bleio_device_obj_t *device, ble_drv_service_data_t * p_service_data); +typedef void (*ble_drv_disc_add_char_callback_t)(bleio_service_obj_t *service, ble_drv_char_data_t * p_desc_data); typedef void (*ble_drv_gattc_char_data_callback_t)(bleio_characteristic_obj_t *self, uint16_t length, uint8_t * p_data); uint32_t ble_drv_stack_enable(void); @@ -97,15 +97,15 @@ void ble_drv_service_add(bleio_service_obj_t *service); bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic); -bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params); +bool ble_drv_advertise_data(bleio_advertisement_data_t *p_adv_params); void ble_drv_advertise_stop(void); -void ble_drv_gap_event_handler_set(mp_obj_t obs, ble_drv_gap_evt_callback_t evt_handler); +void ble_drv_gap_event_handler_set(bleio_device_obj_t *device, ble_drv_gap_evt_callback_t evt_handler); -void ble_drv_gatts_event_handler_set(mp_obj_t obj, ble_drv_gatts_evt_callback_t evt_handler); +void ble_drv_gatts_event_handler_set(bleio_device_obj_t *device, ble_drv_gatts_evt_callback_t evt_handler); -void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t evt_handler); +void ble_drv_gattc_event_handler_set(bleio_device_obj_t *device, ble_drv_gattc_evt_callback_t evt_handler); void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); @@ -125,15 +125,13 @@ void ble_drv_scan_stop(void); void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *self, ble_drv_adv_evt_callback_t evt_handler); -void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type); +void ble_drv_connect(bleio_device_obj_t *device); + +void ble_drv_disconnect(bleio_device_obj_t *device); -bool ble_drv_discover_services(mp_obj_t obj, uint16_t conn_handle, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb); +bool ble_drv_discover_services(bleio_device_obj_t *device, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb); -bool ble_drv_discover_characteristic(mp_obj_t obj, - uint16_t conn_handle, - uint16_t start_handle, - uint16_t end_handle, - ble_drv_disc_add_char_callback_t cb); +bool ble_drv_discover_characteristic(bleio_device_obj_t *device, bleio_service_obj_t *service, uint16_t start_handle, ble_drv_disc_add_char_callback_t cb); void ble_drv_discover_descriptors(void); diff --git a/ports/nrf/drivers/bluetooth/ble_uart.h b/ports/nrf/drivers/bluetooth/ble_uart.h index e67176a26..336624cd3 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.h +++ b/ports/nrf/drivers/bluetooth/ble_uart.h @@ -29,7 +29,6 @@ #if BLUETOOTH_SD -#include "modubluepy.h" #include "ble_drv.h" void ble_uart_init0(void); diff --git a/ports/nrf/modules/ubluepy/modubluepy.c b/ports/nrf/modules/ubluepy/modubluepy.c deleted file mode 100644 index f8266a82d..000000000 --- a/ports/nrf/modules/ubluepy/modubluepy.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 "py/obj.h" - -#if MICROPY_PY_UBLUEPY - -extern const mp_obj_type_t ubluepy_peripheral_type; -extern const mp_obj_type_t ubluepy_service_type; - -STATIC const mp_rom_map_elem_t mp_module_ubluepy_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubluepy) }, -#if MICROPY_PY_UBLUEPY_PERIPHERAL - { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&ubluepy_peripheral_type) }, -#endif -}; - -STATIC MP_DEFINE_CONST_DICT(mp_module_ubluepy_globals, mp_module_ubluepy_globals_table); - -const mp_obj_module_t mp_module_ubluepy = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_ubluepy_globals, -}; - -#endif // MICROPY_PY_UBLUEPY diff --git a/ports/nrf/modules/ubluepy/modubluepy.h b/ports/nrf/modules/ubluepy/modubluepy.h deleted file mode 100644 index e301f0476..000000000 --- a/ports/nrf/modules/ubluepy/modubluepy.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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. - */ - -#ifndef UBLUEPY_H__ -#define UBLUEPY_H__ - -/* Examples: - -Advertisment: - -from ubluepy import Peripheral -p = Peripheral() -p.advertise(device_name="MicroPython") - -DB setup: - -from ubluepy import Service, Characteristic, UUID, Peripheral, constants -from pyb import LED - -def event_handler(id, handle, data): - print("BLE event:", id, "handle:", handle) - print(data) - - if id == constants.EVT_GAP_CONNECTED: - # connected - LED(2).on() - elif id == constants.EVT_GAP_DISCONNECTED: - # disconnect - LED(2).off() - elif id == 80: - print("id 80, data:", data) - -# u0 = UUID("0x180D") # HRM service -# u1 = UUID("0x2A37") # HRM measurement - -u0 = UUID("6e400001-b5a3-f393-e0a9-e50e24dcca9e") -u1 = UUID("6e400002-b5a3-f393-e0a9-e50e24dcca9e") -u2 = UUID("6e400003-b5a3-f393-e0a9-e50e24dcca9e") -s = Service(u0) -c0 = Characteristic(u1, props = Characteristic.PROP_WRITE | Characteristic.PROP_WRITE_WO_RESP) -c1 = Characteristic(u2, props = Characteristic.PROP_NOTIFY, attrs = Characteristic.ATTR_CCCD) -s.addCharacteristic(c0) -s.addCharacteristic(c1) -p = Peripheral() -p.addService(s) -p.setConnectionHandler(event_handler) -p.advertise(device_name="micr", services=[s]) - -*/ - -#include "common-hal/bleio/UUID.h" -#include "py/obj.h" - -extern const mp_obj_type_t ubluepy_peripheral_type; - -typedef enum { - UBLUEPY_ADDR_TYPE_PUBLIC = 0, - UBLUEPY_ADDR_TYPE_RANDOM_STATIC = 1, - UBLUEPY_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE = 2, - UBLUEPY_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE = 3, -} ubluepy_addr_type_t; - -typedef enum { - UBLUEPY_ROLE_PERIPHERAL, - UBLUEPY_ROLE_CENTRAL -} ubluepy_role_type_t; - -typedef struct _ubluepy_peripheral_obj_t { - mp_obj_base_t base; - ubluepy_role_type_t role; - volatile uint16_t conn_handle; - mp_obj_t delegate; - mp_obj_t notif_handler; - mp_obj_t conn_handler; - mp_obj_t service_list; -} ubluepy_peripheral_obj_t; - -typedef struct _ubluepy_advertise_data_t { - uint8_t * p_device_name; - uint8_t device_name_len; - mp_obj_t * p_services; - uint8_t num_of_services; - uint8_t * p_data; - uint8_t data_len; - bool connectable; -} ubluepy_advertise_data_t; - -typedef enum _ubluepy_prop_t { - UBLUEPY_PROP_BROADCAST = 0x01, - UBLUEPY_PROP_READ = 0x02, - UBLUEPY_PROP_WRITE_WO_RESP = 0x04, - UBLUEPY_PROP_WRITE = 0x08, - UBLUEPY_PROP_NOTIFY = 0x10, - UBLUEPY_PROP_INDICATE = 0x20, - UBLUEPY_PROP_AUTH_SIGNED_WR = 0x40, -} ubluepy_prop_t; - -typedef enum _ubluepy_attr_t { - UBLUEPY_ATTR_CCCD = 0x01, - UBLUEPY_ATTR_SCCD = 0x02, -} ubluepy_attr_t; - -#endif // UBLUEPY_H__ diff --git a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c b/ports/nrf/modules/ubluepy/ubluepy_peripheral.c deleted file mode 100644 index 3b1a0d279..000000000 --- a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 -#include "py/obj.h" -#include "py/runtime.h" -#include "py/objstr.h" -#include "py/objlist.h" - -#if MICROPY_PY_UBLUEPY - -#include "ble_drv.h" -#include "common-hal/bleio/UUID.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -STATIC void ubluepy_peripheral_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) { - ubluepy_peripheral_obj_t * self = (ubluepy_peripheral_obj_t *)o; - (void)self; - mp_printf(print, "Peripheral(conn_handle: " HEX2_FMT ")", - self->conn_handle); -} - -STATIC void gap_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - if (event_id == 16) { // connect event - self->conn_handle = conn_handle; - } else if (event_id == 17) { // disconnect event - self->conn_handle = 0xFFFF; // invalid connection handle - } - - if (self->conn_handler != mp_const_none) { - mp_obj_t args[3]; - mp_uint_t num_of_args = 3; - args[0] = MP_OBJ_NEW_SMALL_INT(event_id); - args[1] = MP_OBJ_NEW_SMALL_INT(conn_handle); - if (data != NULL) { - args[2] = mp_obj_new_bytearray_by_ref(length, data); - } else { - args[2] = mp_const_none; - } - - // for now hard-code all events to conn_handler - mp_call_function_n_kw(self->conn_handler, num_of_args, 0, args); - } - - (void)self; -} - -STATIC void gatts_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - if (self->conn_handler != mp_const_none) { - mp_obj_t args[3]; - mp_uint_t num_of_args = 3; - args[0] = MP_OBJ_NEW_SMALL_INT(event_id); - args[1] = MP_OBJ_NEW_SMALL_INT(attr_handle); - if (data != NULL) { - args[2] = mp_obj_new_bytearray_by_ref(length, data); - } else { - args[2] = mp_const_none; - } - - // for now hard-code all events to conn_handler - mp_call_function_n_kw(self->conn_handler, num_of_args, 0, args); - } - -} - -#if MICROPY_PY_UBLUEPY_CENTRAL - -static volatile bool m_disc_evt_received; - -STATIC void gattc_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - (void)self; - m_disc_evt_received = true; -} -#endif - -STATIC mp_obj_t ubluepy_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { - ARG_NEW_DEVICE_ADDR, - ARG_NEW_ADDR_TYPE - }; - - static const mp_arg_t allowed_args[] = { - { ARG_NEW_DEVICE_ADDR, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { ARG_NEW_ADDR_TYPE, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - ubluepy_peripheral_obj_t *s = m_new_obj(ubluepy_peripheral_obj_t); - s->base.type = type; - - s->delegate = mp_const_none; - s->conn_handler = mp_const_none; - s->notif_handler = mp_const_none; - s->conn_handle = 0xFFFF; - - s->service_list = mp_obj_new_list(0, NULL); - - return MP_OBJ_FROM_PTR(s); -} - -/// \method withDelegate(DefaultDelegate) -/// Set delegate instance for handling Bluetooth LE events. -/// -STATIC mp_obj_t peripheral_with_delegate(mp_obj_t self_in, mp_obj_t delegate) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - self->delegate = delegate; - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(ubluepy_peripheral_with_delegate_obj, peripheral_with_delegate); - -/// \method setNotificationHandler(func) -/// Set handler for Bluetooth LE notification events. -/// -STATIC mp_obj_t peripheral_set_notif_handler(mp_obj_t self_in, mp_obj_t func) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - self->notif_handler = func; - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(ubluepy_peripheral_set_notif_handler_obj, peripheral_set_notif_handler); - -/// \method setConnectionHandler(func) -/// Set handler for Bluetooth LE connection events. -/// -STATIC mp_obj_t peripheral_set_conn_handler(mp_obj_t self_in, mp_obj_t func) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - self->conn_handler = func; - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(ubluepy_peripheral_set_conn_handler_obj, peripheral_set_conn_handler); - -#if MICROPY_PY_UBLUEPY_PERIPHERAL - -/// \method advertise(device_name, [service=[service1, service2, ...]], [data=bytearray], [connectable=True]) -/// Start advertising. Connectable advertisement type by default. -/// -STATIC mp_obj_t peripheral_advertise(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - static const mp_arg_t allowed_args[] = { - { MP_QSTR_device_name, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_services, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - self->role = UBLUEPY_ROLE_PERIPHERAL; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - mp_obj_t device_name_obj = args[0].u_obj; - mp_obj_t service_obj = args[1].u_obj; - mp_obj_t data_obj = args[2].u_obj; - mp_obj_t connectable_obj = args[3].u_obj; - - ubluepy_advertise_data_t adv_data; - memset(&adv_data, 0, sizeof(ubluepy_advertise_data_t)); - - if (device_name_obj != mp_const_none && MP_OBJ_IS_STR(device_name_obj)) { - GET_STR_DATA_LEN(device_name_obj, str_data, str_len); - - adv_data.p_device_name = (uint8_t *)str_data; - adv_data.device_name_len = str_len; - } - - if (service_obj != mp_const_none) { - mp_obj_t * services = NULL; - mp_uint_t num_services; - mp_obj_get_array(service_obj, &num_services, &services); - - if (num_services > 0) { - adv_data.p_services = services; - adv_data.num_of_services = num_services; - } - } - - if (data_obj != mp_const_none) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); - - if (bufinfo.len > 0) { - adv_data.p_data = bufinfo.buf; - adv_data.data_len = bufinfo.len; - } - } - - adv_data.connectable = true; - if (connectable_obj != mp_const_none && !(mp_obj_is_true(connectable_obj))) { - adv_data.connectable = false; - } else { - ble_drv_gap_event_handler_set(MP_OBJ_FROM_PTR(self), gap_event_handler); - ble_drv_gatts_event_handler_set(MP_OBJ_FROM_PTR(self), gatts_event_handler); - } - - (void)ble_drv_advertise_data(&adv_data); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ubluepy_peripheral_advertise_obj, 0, peripheral_advertise); - -/// \method advertise_stop() -/// Stop advertisement if any onging advertisement. -/// -STATIC mp_obj_t peripheral_advertise_stop(mp_obj_t self_in) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - (void)self; - - ble_drv_advertise_stop(); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_peripheral_advertise_stop_obj, peripheral_advertise_stop); - -#endif // MICROPY_PY_UBLUEPY_PERIPHERAL - -/// \method disconnect() -/// disconnect connection. -/// -STATIC mp_obj_t peripheral_disconnect(mp_obj_t self_in) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - (void)self; - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_peripheral_disconnect_obj, peripheral_disconnect); - -/// \method addService(Service) -/// Add service to the Peripheral. -/// -STATIC mp_obj_t peripheral_add_service(mp_obj_t self_in, mp_obj_t service) { - ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); - bleio_service_obj_t * p_service = MP_OBJ_TO_PTR(service); - - p_service->periph = self_in; - - mp_obj_list_append(self->service_list, service); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(ubluepy_peripheral_add_service_obj, peripheral_add_service); - -/// \method getServices() -/// Return list with all service registered in the Peripheral. -/// -STATIC mp_obj_t peripheral_get_services(mp_obj_t self_in) { - ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); - - return self->service_list; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(ubluepy_peripheral_get_services_obj, peripheral_get_services); - -#if MICROPY_PY_UBLUEPY_CENTRAL - -void static disc_add_service(mp_obj_t self, ble_drv_service_data_t * p_service_data) { - bleio_service_obj_t * p_service = m_new_obj(bleio_service_obj_t); - p_service->base.type = &bleio_service_type; - - bleio_uuid_obj_t * p_uuid = m_new_obj(bleio_uuid_obj_t); - p_uuid->base.type = &bleio_uuid_type; - - p_service->uuid = p_uuid; - - p_uuid->type = p_service_data->uuid_type; - p_uuid->value[0] = p_service_data->uuid & 0xFF; - p_uuid->value[1] = p_service_data->uuid >> 8; - - p_service->handle = p_service_data->start_handle; - p_service->start_handle = p_service_data->start_handle; - p_service->end_handle = p_service_data->end_handle; - - p_service->char_list = mp_obj_new_list(0, NULL); - - peripheral_add_service(self, MP_OBJ_FROM_PTR(p_service)); -} - -void static disc_add_char(mp_obj_t service_in, ble_drv_char_data_t * p_desc_data) { - bleio_service_obj_t * p_service = MP_OBJ_TO_PTR(service_in); - bleio_characteristic_obj_t * p_char = m_new_obj(bleio_characteristic_obj_t); - p_char->base.type = &bleio_characteristic_type; - - bleio_uuid_obj_t * p_uuid = m_new_obj(bleio_uuid_obj_t); - p_uuid->base.type = &bleio_uuid_type; - - p_char->uuid = p_uuid; - - p_uuid->type = p_desc_data->uuid_type; - p_uuid->value[0] = p_desc_data->uuid & 0xFF; - p_uuid->value[1] = p_desc_data->uuid >> 8; - - // add characteristic specific data from discovery - p_char->props.broadcast = p_desc_data->props.broadcast; - p_char->props.indicate = p_desc_data->props.indicate; - p_char->props.notify = p_desc_data->props.notify; - p_char->props.read = p_desc_data->props.read; - p_char->props.write = p_desc_data->props.write; - p_char->props.write_wo_resp = p_desc_data->props.write_wo_resp; - p_char->handle = p_desc_data->value_handle; - - // equivalent to ubluepy_service.c - service_add_characteristic() - // except the registration of the characteristic towards the bluetooth stack - p_char->service_handle = p_service->handle; - p_char->service = p_service; - - mp_obj_list_append(p_service->char_list, MP_OBJ_FROM_PTR(p_char)); -} - -/// \method connect(device_address [, addr_type=ADDR_TYPE_PUBLIC]) -/// Connect to device peripheral with the given device address. -/// addr_type can be either ADDR_TYPE_PUBLIC (default) or -/// ADDR_TYPE_RANDOM_STATIC. -/// -STATIC mp_obj_t peripheral_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - ubluepy_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - mp_obj_t dev_addr = pos_args[1]; - - self->role = UBLUEPY_ROLE_CENTRAL; - - static const mp_arg_t allowed_args[] = { - { MP_QSTR_addr_type, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UBLUEPY_ADDR_TYPE_PUBLIC } }, - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - uint8_t addr_type = args[0].u_int; - - ble_drv_gap_event_handler_set(MP_OBJ_FROM_PTR(self), gap_event_handler); - - if (MP_OBJ_IS_STR(dev_addr)) { - GET_STR_DATA_LEN(dev_addr, str_data, str_len); - if (str_len == 17) { // Example "11:22:33:aa:bb:cc" - - uint8_t * p_addr = m_new(uint8_t, 6); - - p_addr[0] = unichar_xdigit_value(str_data[16]); - p_addr[0] += unichar_xdigit_value(str_data[15]) << 4; - p_addr[1] = unichar_xdigit_value(str_data[13]); - p_addr[1] += unichar_xdigit_value(str_data[12]) << 4; - p_addr[2] = unichar_xdigit_value(str_data[10]); - p_addr[2] += unichar_xdigit_value(str_data[9]) << 4; - p_addr[3] = unichar_xdigit_value(str_data[7]); - p_addr[3] += unichar_xdigit_value(str_data[6]) << 4; - p_addr[4] = unichar_xdigit_value(str_data[4]); - p_addr[4] += unichar_xdigit_value(str_data[3]) << 4; - p_addr[5] = unichar_xdigit_value(str_data[1]); - p_addr[5] += unichar_xdigit_value(str_data[0]) << 4; - - ble_drv_connect(p_addr, addr_type); - - m_del(uint8_t, p_addr, 6); - } - } - - // block until connected - while (self->conn_handle == 0xFFFF) { - ; - } - - ble_drv_gattc_event_handler_set(MP_OBJ_FROM_PTR(self), gattc_event_handler); - - bool service_disc_retval = ble_drv_discover_services(self, self->conn_handle, 0x0001, disc_add_service); - - // continue discovery of primary services ... - while (service_disc_retval) { - // locate the last added service - mp_obj_t * services = NULL; - mp_uint_t num_services; - mp_obj_get_array(self->service_list, &num_services, &services); - - bleio_service_obj_t * p_service = (bleio_service_obj_t *)services[num_services - 1]; - - service_disc_retval = ble_drv_discover_services(self, - self->conn_handle, - p_service->end_handle + 1, - disc_add_service); - } - - // For each service perform a characteristic discovery - mp_obj_t * services = NULL; - mp_uint_t num_services; - mp_obj_get_array(self->service_list, &num_services, &services); - - for (uint16_t s = 0; s < num_services; s++) { - bleio_service_obj_t * p_service = (bleio_service_obj_t *)services[s]; - bool char_disc_retval = ble_drv_discover_characteristic(p_service, - self->conn_handle, - p_service->start_handle, - p_service->end_handle, - disc_add_char); - // continue discovery of characteristics ... - while (char_disc_retval) { - mp_obj_t * characteristics = NULL; - mp_uint_t num_chars; - mp_obj_get_array(p_service->char_list, &num_chars, &characteristics); - - bleio_characteristic_obj_t * p_char = (bleio_characteristic_obj_t *)characteristics[num_chars - 1]; - uint16_t next_handle = p_char->handle + 1; - if ((next_handle) < p_service->end_handle) { - char_disc_retval = ble_drv_discover_characteristic(p_service, - self->conn_handle, - next_handle, - p_service->end_handle, - disc_add_char); - } else { - break; - } - } - } - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ubluepy_peripheral_connect_obj, 2, peripheral_connect); - -#endif - -STATIC const mp_rom_map_elem_t ubluepy_peripheral_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_withDelegate), MP_ROM_PTR(&ubluepy_peripheral_with_delegate_obj) }, - { MP_ROM_QSTR(MP_QSTR_setNotificationHandler), MP_ROM_PTR(&ubluepy_peripheral_set_notif_handler_obj) }, - { MP_ROM_QSTR(MP_QSTR_setConnectionHandler), MP_ROM_PTR(&ubluepy_peripheral_set_conn_handler_obj) }, - { MP_ROM_QSTR(MP_QSTR_getServices), MP_ROM_PTR(&ubluepy_peripheral_get_services_obj) }, -#if MICROPY_PY_UBLUEPY_CENTRAL - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ubluepy_peripheral_connect_obj) }, -#if 0 - { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&ubluepy_peripheral_disconnect_obj) }, - { MP_ROM_QSTR(MP_QSTR_getServiceByUUID), MP_ROM_PTR(&ubluepy_peripheral_get_service_by_uuid_obj) }, - { MP_ROM_QSTR(MP_QSTR_getCharacteristics), MP_ROM_PTR(&ubluepy_peripheral_get_chars_obj) }, - { MP_ROM_QSTR(MP_QSTR_getDescriptors), MP_ROM_PTR(&ubluepy_peripheral_get_descs_obj) }, - { MP_ROM_QSTR(MP_QSTR_waitForNotifications), MP_ROM_PTR(&ubluepy_peripheral_wait_for_notif_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeCharacteristic), MP_ROM_PTR(&ubluepy_peripheral_write_char_obj) }, - { MP_ROM_QSTR(MP_QSTR_readCharacteristic), MP_ROM_PTR(&ubluepy_peripheral_read_char_obj) }, -#endif // 0 -#endif // MICROPY_PY_UBLUEPY_CENTRAL -#if MICROPY_PY_UBLUEPY_PERIPHERAL - { MP_ROM_QSTR(MP_QSTR_advertise), MP_ROM_PTR(&ubluepy_peripheral_advertise_obj) }, - { MP_ROM_QSTR(MP_QSTR_advertise_stop), MP_ROM_PTR(&ubluepy_peripheral_advertise_stop_obj) }, - { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&ubluepy_peripheral_disconnect_obj) }, - { MP_ROM_QSTR(MP_QSTR_addService), MP_ROM_PTR(&ubluepy_peripheral_add_service_obj) }, -#if 0 - { MP_ROM_QSTR(MP_QSTR_addCharacteristic), MP_ROM_PTR(&ubluepy_peripheral_add_char_obj) }, - { MP_ROM_QSTR(MP_QSTR_addDescriptor), MP_ROM_PTR(&ubluepy_peripheral_add_desc_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeCharacteristic), MP_ROM_PTR(&ubluepy_peripheral_write_char_obj) }, - { MP_ROM_QSTR(MP_QSTR_readCharacteristic), MP_ROM_PTR(&ubluepy_peripheral_read_char_obj) }, -#endif -#endif -#if MICROPY_PY_UBLUEPY_BROADCASTER - { MP_ROM_QSTR(MP_QSTR_advertise), MP_ROM_PTR(&ubluepy_peripheral_advertise_obj) }, -#endif -#if MICROPY_PY_UBLUEPY_OBSERVER - // Nothing yet. -#endif -}; - -STATIC MP_DEFINE_CONST_DICT(ubluepy_peripheral_locals_dict, ubluepy_peripheral_locals_dict_table); - -const mp_obj_type_t ubluepy_peripheral_type = { - { &mp_type_type }, - .name = MP_QSTR_Peripheral, - .print = ubluepy_peripheral_print, - .make_new = ubluepy_peripheral_make_new, - .locals_dict = (mp_obj_dict_t*)&ubluepy_peripheral_locals_dict -}; - -#endif // MICROPY_PY_UBLUEPY diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index b710808b9..62a041ae9 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -129,21 +129,15 @@ #define CIRCUITPY_GAMEPAD_TICKS 0x1f #if BLUETOOTH_SD -#define MICROPY_PY_BLEIO (1) -#define MICROPY_PY_BLE_NUS (0) -#define MICROPY_PY_UBLUEPY (1) -#define MICROPY_PY_UBLUEPY_PERIPHERAL (1) -#define MICROPY_PY_UBLUEPY_CENTRAL (1) -#define BLUETOOTH_WEBBLUETOOTH_REPL (0) -#endif - -#ifndef MICROPY_PY_BLEIO -#define MICROPY_PY_BLEIO (0) + #define MICROPY_PY_BLEIO (1) + #define MICROPY_PY_BLE_NUS (0) + #define BLUETOOTH_WEBBLUETOOTH_REPL (0) +#else + #ifndef MICROPY_PY_BLEIO + #define MICROPY_PY_BLEIO (0) + #endif #endif -#ifndef MICROPY_PY_UBLUEPY -#define MICROPY_PY_UBLUEPY (0) -#endif // type definitions for the specific machine @@ -184,16 +178,8 @@ extern const struct _mp_obj_module_t neopixel_write_module; extern const struct _mp_obj_module_t usb_hid_module; extern const struct _mp_obj_module_t bleio_module; -extern const struct _mp_obj_module_t mp_module_ubluepy; - -#if MICROPY_PY_UBLUEPY -#define UBLUEPY_MODULE { MP_ROM_QSTR(MP_QSTR_ubluepy), MP_ROM_PTR(&mp_module_ubluepy) }, -#else -#define UBLUEPY_MODULE -#endif - #if MICROPY_PY_BLEIO -#define BLEIO_MODULE { MP_ROM_QSTR(MP_QSTR_bleio), MP_ROM_PTR(&bleio_module) }, +#define BLEIO_MODULE { MP_ROM_QSTR(MP_QSTR_bleio), MP_ROM_PTR(&bleio_module) }, #else #define BLEIO_MODULE #endif @@ -221,8 +207,7 @@ extern const struct _mp_obj_module_t mp_module_ubluepy; { MP_OBJ_NEW_QSTR (MP_QSTR_gamepad ), (mp_obj_t)&gamepad_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_time ), (mp_obj_t)&time_module }, \ USBHID_MODULE \ - BLEIO_MODULE \ - UBLUEPY_MODULE \ + BLEIO_MODULE // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ diff --git a/shared-bindings/bleio/Device.c b/shared-bindings/bleio/Device.c new file mode 100644 index 000000000..15ebd51ed --- /dev/null +++ b/shared-bindings/bleio/Device.c @@ -0,0 +1,348 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec + * + * 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 +#include + +#include "ble_drv.h" +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/AddressType.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Device.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" +#include "shared-module/bleio/AdvertisementData.h" +#include "shared-module/bleio/Device.h" + +//| .. currentmodule:: bleio +//| +//| :class:`Device` -- BLE device +//| ========================================================= +//| +//| Provides access a to BLE device, either in a Peripheral or Central role. +//| When a device is created without any parameter passed to the constructor, +//| it will be set to the Peripheral role. If a address is passed, the device +//| will be a Central. For a Peripheral you can set the `name`, add services +//| via `add_service` and then start and stop advertising via `start_advertising` +//| and `stop_advertising`. For the Central, you can `bleio.Device.connect` and `bleio.Device.disconnect` +//| to the device, once a connection is established, the device's services can +//| be accessed using `services`. +//| +//| Usage:: +//| +//| import bleio +//| +//| # Peripheral +//| periph = bleio.Device() +//| +//| serv = bleio.Service(bleio.UUID(0x180f)) +//| p.add_service(serv) +//| +//| chara = bleio.Characteristic(bleio.UUID(0x2919)) +//| chara.read = True +//| chara.notify = True +//| serv.add_characteristic(chara) +//| +//| periph.start_advertising() +//| +//| # Central +//| scanner = bleio.Scanner() +//| entries = scanner.scan(2500) +//| +//| my_entry = None +//| for entry in entries: +//| if entry.name is not None and entry.name == 'MyDevice': +//| my_entry = entry +//| break +//| +//| central = bleio.Device(my_entry.address) +//| central.connect() +//| + +//| .. class:: Device(address=None) +//| +//| Create a new Device object. If the `address` parameter is not `None`, +//| the role is set to Central, otherwise it's set to Peripheral. +//| +//| :param bleio.Address address: The address of the device to connect to +//| + +//| .. attribute:: name +//| +//| For the Peripheral role, this property can be used to read and write the device's name. +//| For the Central role, this property will equal the name of the remote device, if one was +//| advertised by the device. In the Central role this property is read-only. +//| + +//| .. attribute:: services +//| +//| A `list` of `bleio.Service` that are offered by this device. (read-only) +//| For a Peripheral device, this list will contain services added using `add_service`, +//| for a Central, this list will be empty until a connection is established, at which point +//| it will be filled with the remote device's services. +//| + +//| .. method:: add_service(service) +//| +//| Appends the :py:data:`service` to the list of this devices's services. +//| This method can only be called for Peripheral devices. +//| +//| :param bleio.Service service: the service to append +//| + +//| .. method:: connect() +//| +//| Attempts a connection to the remote device. If the connection is successful, +//| the device's services are available via `services`. +//| This method can only be called for Central devices. +//| + +//| .. method:: disconnect() +//| +//| Disconnects from the remote device. +//| This method can only be called for Central devices. +//| + +//| .. method:: start_advertising(connectable=True) +//| +//| Starts advertising the device. The device's name and +//| services are put into the advertisement packets. +//| If :py:data:`connectable` is `True` then other devices are allowed to conncet to this device. +//| This method can only be called for Peripheral devices. +//| + +//| .. method:: stop_advertising() +//| +//| Disconnects from the remote device. +//| This method can only be called for Peripheral devices. +//| +static const char default_name[] = "CIRCUITPY"; + +STATIC void bleio_device_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_printf(print, "Device(role: %s)", self->is_peripheral ? "Peripheral" : "Central"); +} + +STATIC mp_obj_t bleio_device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 0, 1, true); + bleio_device_obj_t *self = m_new_obj(bleio_device_obj_t); + self->base.type = &bleio_device_type; + self->service_list = mp_obj_new_list(0, NULL); + self->notif_handler = mp_const_none; + self->conn_handler = mp_const_none; + self->conn_handle = 0xFFFF; + self->is_peripheral = true; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + //TODO: Add ScanEntry + enum { ARG_address }; + static const mp_arg_t allowed_args[] = { + { ARG_address, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_t address_obj = args[ARG_address].u_obj; + + if (address_obj != mp_const_none) { + bleio_address_obj_t *address = MP_OBJ_TO_PTR(address_obj); + + self->is_peripheral = false; + self->address.type = address->type; + memcpy(self->address.value, address->value, BLEIO_ADDRESS_BYTES); + } else { + self->name = mp_obj_new_str(default_name, strlen(default_name), false); + common_hal_bleio_adapter_get_address(&self->address); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t bleio_device_add_service(mp_obj_t self_in, mp_obj_t service_in) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_in); + + if (!self->is_peripheral) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Can't add services in Central mode")); + } + + service->device = self_in; + + mp_obj_list_append(self->service_list, service); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_device_add_service_obj, bleio_device_add_service); + +STATIC mp_obj_t bleio_device_connect(mp_obj_t self_in) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->is_peripheral) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Can't connect in Peripheral mode")); + } + + common_hal_bleio_device_connect(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_device_connect_obj, bleio_device_connect); + +STATIC mp_obj_t bleio_device_disconnect(mp_obj_t self_in) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_device_disconnect(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_device_disconnect_obj, bleio_device_disconnect); + +STATIC mp_obj_t bleio_device_get_name(mp_obj_t self_in) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return self->name; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_device_get_name_obj, bleio_device_get_name); + +static mp_obj_t bleio_device_set_name(mp_obj_t self_in, mp_obj_t value) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (!self->is_peripheral) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Can't change the name in Central mode")); + } + + self->name = value; + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_device_set_name_obj, bleio_device_set_name); + +const mp_obj_property_t bleio_device_name_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_device_get_name_obj, + (mp_obj_t)&bleio_device_set_name_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC mp_obj_t bleio_device_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + if (!self->is_peripheral) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Can't advertise in Central mode")); + } + + enum { ARG_connectable }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // TODO: data + bleio_advertisement_data_t adv_data = { + .device_name = self->name, + .services = mp_obj_new_list(0, NULL), + .data = mp_obj_new_bytearray(0, NULL), + .connectable = args[ARG_connectable].u_bool + }; + + mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); + for (size_t i = 0; i < service_list->len; ++i) { + bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + if (!service->is_secondary) { + mp_obj_list_append(adv_data.services, service_list->items[i]); + } + } + + common_hal_bleio_device_start_advertising(self, &adv_data); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_device_start_advertising_obj, 0, bleio_device_start_advertising); + +STATIC mp_obj_t bleio_device_stop_advertising(mp_obj_t self_in) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->is_peripheral) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Can't advertise in Central mode")); + } + + common_hal_bleio_device_stop_advertising(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_device_stop_advertising_obj, bleio_device_stop_advertising); + +STATIC mp_obj_t bleio_device_get_services(mp_obj_t self_in) { + bleio_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return self->service_list; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_device_get_services_obj, bleio_device_get_services); + +const mp_obj_property_t bleio_device_services_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_device_get_services_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_device_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_add_service), MP_ROM_PTR(&bleio_device_add_service_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_device_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_device_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_device_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_device_stop_advertising_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_device_name_obj) }, + { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_device_services_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_device_locals_dict, bleio_device_locals_dict_table); + +const mp_obj_type_t bleio_device_type = { + { &mp_type_type }, + .name = MP_QSTR_Device, + .print = bleio_device_print, + .make_new = bleio_device_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_device_locals_dict +}; diff --git a/shared-bindings/bleio/Device.h b/shared-bindings/bleio/Device.h new file mode 100644 index 000000000..d099e5c43 --- /dev/null +++ b/shared-bindings/bleio/Device.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DEVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DEVICE_H + +#include "shared-module/bleio/AdvertisementData.h" +#include "shared-module/bleio/Device.h" + +extern const mp_obj_type_t bleio_device_type; + +extern void common_hal_bleio_device_start_advertising(bleio_device_obj_t *device, bleio_advertisement_data_t *adv_data); +extern void common_hal_bleio_device_stop_advertising(bleio_device_obj_t *device); +extern void common_hal_bleio_device_connect(bleio_device_obj_t *device); +extern void common_hal_bleio_device_disconnect(bleio_device_obj_t *device); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DEVICE_H diff --git a/shared-bindings/bleio/ScanEntry.c b/shared-bindings/bleio/ScanEntry.c index e38a08909..2eed24cd8 100644 --- a/shared-bindings/bleio/ScanEntry.c +++ b/shared-bindings/bleio/ScanEntry.c @@ -32,9 +32,10 @@ #include "py/objstr.h" #include "py/objtuple.h" #include "shared-bindings/bleio/Address.h" -#include "shared-module/bleio/AdvertisementData.h" -#include "shared-bindings/bleio/UUID.h" #include "shared-bindings/bleio/ScanEntry.h" +#include "shared-bindings/bleio/UUID.h" +#include "shared-module/bleio/AdvertisementData.h" +#include "shared-module/bleio/ScanEntry.h" //| .. currentmodule:: bleio //| diff --git a/shared-bindings/bleio/ScanEntry.h b/shared-bindings/bleio/ScanEntry.h index 4a201124e..2b44ba3f4 100644 --- a/shared-bindings/bleio/ScanEntry.h +++ b/shared-bindings/bleio/ScanEntry.h @@ -28,16 +28,7 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H -#include "shared-module/bleio/Address.h" -#include "py/objtype.h" - -typedef struct { - mp_obj_base_t base; - bleio_address_obj_t address; - bool connectable; - int8_t rssi; - mp_obj_t data; -} bleio_scanentry_obj_t; +#include "py/obj.h" extern const mp_obj_type_t bleio_scanentry_type; diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c index 6451991e9..5b61a2966 100644 --- a/shared-bindings/bleio/Service.c +++ b/shared-bindings/bleio/Service.c @@ -73,7 +73,7 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_check_num(n_args, n_kw, 1, 1, true); bleio_service_obj_t *self = m_new_obj(bleio_service_obj_t); self->base.type = &bleio_service_type; - self->periph = mp_const_none; + self->device = mp_const_none; self->char_list = mp_obj_new_list(0, NULL); mp_map_t kw_args; @@ -81,7 +81,7 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, enum { ARG_uuid, ARG_secondary }; static const mp_arg_t allowed_args[] = { - { ARG_uuid, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { ARG_uuid, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; @@ -92,7 +92,7 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t uuid = args[ARG_uuid].u_obj; - if (uuid == MP_OBJ_NULL) { + if (uuid == mp_const_none) { return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index 9f5a8f8f2..98099422c 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -31,6 +31,7 @@ #include "shared-bindings/bleio/AdvertisementData.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" +#include "shared-bindings/bleio/Device.h" #include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/Scanner.h" #include "shared-bindings/bleio/Service.h" @@ -57,6 +58,7 @@ //| Adapter //| Characteristic //| Descriptor +//| Device //| ScanEntry //| Scanner //| Service @@ -76,6 +78,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) }, { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, + { MP_ROM_QSTR(MP_QSTR_Device), MP_ROM_PTR(&bleio_device_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, diff --git a/shared-module/bleio/AdvertisementData.h b/shared-module/bleio/AdvertisementData.h index 2a9addf57..738d53b23 100644 --- a/shared-module/bleio/AdvertisementData.h +++ b/shared-module/bleio/AdvertisementData.h @@ -27,6 +27,8 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H +#include "py/obj.h" + // Taken from https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile enum { AdFlags = 0x01, @@ -73,4 +75,11 @@ enum { AdManufacturerSpecificData = 0xFF, }; +typedef struct { + mp_obj_t device_name; + mp_obj_t services; + mp_obj_t data; + bool connectable; +} bleio_advertisement_data_t; + #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADVERTISEMENTDATA_H diff --git a/shared-module/bleio/Device.h b/shared-module/bleio/Device.h new file mode 100644 index 000000000..afbd8f063 --- /dev/null +++ b/shared-module/bleio/Device.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_DEVICE_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_DEVICE_H + +#include + +#include "shared-module/bleio/Address.h" + +typedef struct { + mp_obj_base_t base; + bool is_peripheral; + mp_obj_t name; + bleio_address_obj_t address; + uint16_t conn_handle; + mp_obj_t service_list; + mp_obj_t notif_handler; + mp_obj_t conn_handler; +} bleio_device_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_DEVICE_H diff --git a/shared-module/bleio/ScanEntry.h b/shared-module/bleio/ScanEntry.h new file mode 100644 index 000000000..2f01669e2 --- /dev/null +++ b/shared-module/bleio/ScanEntry.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H + +#include "shared-module/bleio/Address.h" + +typedef struct { + mp_obj_base_t base; + bleio_address_obj_t address; + bool connectable; + int8_t rssi; + mp_obj_t data; +} bleio_scanentry_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H diff --git a/shared-module/bleio/Service.h b/shared-module/bleio/Service.h index bd359a41d..1e3f09119 100644 --- a/shared-module/bleio/Service.h +++ b/shared-module/bleio/Service.h @@ -27,7 +27,6 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SERVICE_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SERVICE_H -#include "modubluepy.h" #include "common-hal/bleio/UUID.h" typedef struct { @@ -35,7 +34,7 @@ typedef struct { uint16_t handle; bool is_secondary; bleio_uuid_obj_t *uuid; - mp_obj_t periph; + mp_obj_t device; mp_obj_t char_list; uint16_t start_handle; uint16_t end_handle; -- cgit v1.2.3 From 17f13ecc2cf10975bf5ff0631ce4326b9ffbbcf5 Mon Sep 17 00:00:00 2001 From: arturo182 Date: Sun, 22 Jul 2018 16:06:19 +0200 Subject: nrf: Cleanup of the ble driver Moved the functions to classes that they belong to. --- ports/nrf/common-hal/bleio/Adapter.c | 117 ++- ports/nrf/common-hal/bleio/Characteristic.c | 159 +++- ports/nrf/common-hal/bleio/Device.c | 535 +++++++++++++- ports/nrf/common-hal/bleio/Scanner.c | 72 +- ports/nrf/common-hal/bleio/Service.c | 85 ++- ports/nrf/common-hal/bleio/UUID.c | 48 +- ports/nrf/drivers/bluetooth/ble_drv.c | 1038 ++------------------------- ports/nrf/drivers/bluetooth/ble_drv.h | 68 +- ports/nrf/drivers/bluetooth/ble_uart.h | 4 +- shared-bindings/bleio/Characteristic.c | 2 + shared-bindings/bleio/Characteristic.h | 1 + shared-bindings/bleio/Device.c | 24 +- shared-bindings/bleio/Device.h | 2 +- shared-bindings/bleio/Scanner.h | 1 + shared-bindings/bleio/Service.c | 2 +- shared-module/bleio/Characteristic.h | 2 +- shared-module/bleio/Device.h | 2 +- shared-module/bleio/Service.h | 3 +- 18 files changed, 1024 insertions(+), 1141 deletions(-) (limited to 'shared-module') diff --git a/ports/nrf/common-hal/bleio/Adapter.c b/ports/nrf/common-hal/bleio/Adapter.c index bee9bd395..dd2fd00dd 100644 --- a/ports/nrf/common-hal/bleio/Adapter.c +++ b/ports/nrf/common-hal/bleio/Adapter.c @@ -28,32 +28,133 @@ #include #include +#include "ble.h" #include "ble_drv.h" -#include "nrfx.h" -#include "nrf_error.h" +#include "nrfx_power.h" +#include "nrf_nvic.h" #include "nrf_sdm.h" #include "py/nlr.h" -#include "shared-module/bleio/Address.h" +#include "shared-bindings/bleio/Adapter.h" + +STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_AssertionError, + "Soft device assert, id: 0x%08lX, pc: 0x%08lX", id, pc)); +} + +STATIC uint32_t ble_stack_enable(void) { + nrf_clock_lf_cfg_t clock_config = { + .source = NRF_CLOCK_LF_SRC_XTAL, +#if (BLE_API_VERSION == 4) + .accuracy = NRF_CLOCK_LF_ACCURACY_20_PPM +#else + .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM +#endif + }; + +#if (BLUETOOTH_SD == 140) + // The SD takes over the POWER IRQ and will fail if the IRQ is already in use + nrfx_power_uninit(); +#endif + + uint32_t err_code = sd_softdevice_enable(&clock_config, softdevice_assert_handler); + if (err_code != NRF_SUCCESS) + return err_code; + + err_code = sd_nvic_EnableIRQ(SD_EVT_IRQn); + if (err_code != NRF_SUCCESS) + return err_code; + + uint32_t app_ram_start; +#if (BLE_API_VERSION == 2) + ble_enable_params_t ble_enable_params = { + .gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT, + .gap_enable_params.central_conn_count = 1, + .gap_enable_params.periph_conn_count = 1, + }; + + app_ram_start = 0x200039c0; + err_code = sd_ble_enable(&ble_enable_params, &app_ram_start); +#else + app_ram_start = 0x20004000; + + ble_cfg_t ble_conf; + ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM; + ble_conf.conn_cfg.params.gap_conn_cfg.conn_count = BLE_GAP_CONN_COUNT_DEFAULT; + ble_conf.conn_cfg.params.gap_conn_cfg.event_length = BLE_GAP_EVENT_LENGTH_DEFAULT; + err_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_conf, app_ram_start); + if (err_code != NRF_SUCCESS) + return err_code; + + memset(&ble_conf, 0, sizeof(ble_conf)); + ble_conf.gap_cfg.role_count_cfg.periph_role_count = 1; + ble_conf.gap_cfg.role_count_cfg.central_role_count = 1; + err_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_conf, app_ram_start); + if (err_code != NRF_SUCCESS) + return err_code; + + memset(&ble_conf, 0, sizeof(ble_conf)); + ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM; + ble_conf.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = MAX_TX_IN_PROGRESS; + err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &ble_conf, app_ram_start); + if (err_code != NRF_SUCCESS) + return err_code; + + err_code = sd_ble_enable(&app_ram_start); +#endif + + return err_code; +} void common_hal_bleio_adapter_set_enabled(bool enabled) { - uint32_t err_code; + const bool is_enabled = common_hal_bleio_adapter_get_enabled(); + // Don't enable or disable twice + if ((is_enabled && enabled) || (!is_enabled && !enabled)) { + return; + } + + uint32_t err_code; if (enabled) { - err_code = ble_drv_stack_enable(); + err_code = ble_stack_enable(); } else { err_code = sd_softdevice_disable(); } if (err_code != NRF_SUCCESS) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - "Failed to change softdevice status, error: 0x%08lX", err_code)); + "Failed to change softdevice state, error: 0x%08lX", err_code)); } } bool common_hal_bleio_adapter_get_enabled(void) { - return ble_drv_stack_enabled(); + uint8_t is_enabled; + + const uint32_t err_code = sd_softdevice_is_enabled(&is_enabled); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to get softdevice state, error: 0x%08lX", err_code)); + } + + return is_enabled; } void common_hal_bleio_adapter_get_address(bleio_address_obj_t *address) { - ble_drv_address_get(address); + ble_gap_addr_t local_address; + uint32_t err_code; + + common_hal_bleio_adapter_set_enabled(true); + +#if (BLE_API_VERSION == 2) + err_code = sd_ble_gap_address_get(&local_address); +#else + err_code = sd_ble_gap_addr_get(&local_address); +#endif + + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to get local address, error: 0x%08lX", err_code)); + } + + address->type = local_address.addr_type; + memcpy(address->value, local_address.addr, BLEIO_ADDRESS_BYTES); } diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index 35b4f5e24..040cacc3f 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -24,26 +24,173 @@ * THE SOFTWARE. */ +#include +#include + #include "ble_drv.h" +#include "ble_gatts.h" +#include "nrf_soc.h" +#include "py/nlr.h" #include "shared-module/bleio/Characteristic.h" -#include "shared-module/bleio/Device.h" + +static volatile bleio_characteristic_obj_t *m_read_characteristic; +static volatile uint8_t m_tx_in_progress; +static nrf_mutex_t *m_write_mutex; +//static volatile bool m_write_done; + +STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { + bleio_device_obj_t *device = characteristic->service->device; + const uint16_t conn_handle = device->conn_handle; + + ble_gatts_value_t gatts_value = { + .p_value = bufinfo->buf, + .len = bufinfo->len, + }; + + const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, characteristic->handle, &gatts_value); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to write gatts value, status: 0x%08lX", err_code)); + } +} + +STATIC void gatts_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { + bleio_device_obj_t *device = characteristic->service->device; + uint16_t hvx_len = bufinfo->len; + + ble_gatts_hvx_params_t hvx_params = { + .handle = characteristic->handle, + .type = BLE_GATT_HVX_NOTIFICATION, + .p_len = &hvx_len, + .p_data = bufinfo->buf, + }; + + while (m_tx_in_progress > MAX_TX_IN_PROGRESS) { +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif + } + + const uint32_t err_code = sd_ble_gatts_hvx(device->conn_handle, &hvx_params); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to notify attribute value, status: 0x%08lX", err_code)); + } + + m_tx_in_progress += 1; +} + +STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) { + bleio_service_obj_t *service = characteristic->service; + bleio_device_obj_t *device = service->device; + + m_read_characteristic = characteristic; + + const uint32_t err_code = sd_ble_gattc_read(device->conn_handle, characteristic->handle, 0); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to read attribute value, status: 0x%08lX", err_code)); + } + + while (m_read_characteristic != NULL) { +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif + } +} + +STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { + bleio_device_obj_t *device = characteristic->service->device; + uint16_t conn_handle = device->conn_handle; + uint32_t err_code; + + ble_gattc_write_params_t write_params; + write_params.write_op = BLE_GATT_OP_WRITE_REQ; + + if (characteristic->props.write_wo_resp) { + write_params.write_op = BLE_GATT_OP_WRITE_CMD; + } + + write_params.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_CANCEL; + write_params.handle = characteristic->handle; + write_params.offset = 0; + write_params.len = bufinfo->len; + write_params.p_value = bufinfo->buf; + + if (write_params.write_op == BLE_GATT_OP_WRITE_CMD) { + err_code = sd_mutex_acquire(m_write_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to acquire mutex, status: 0x%08lX", err_code)); + } + } + + err_code = sd_ble_gattc_write(conn_handle, &write_params); + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to write attribute value, status: 0x%08lX", err_code)); + } + + while (sd_mutex_acquire(m_write_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif + } + + err_code = sd_mutex_release(m_write_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to release mutex, status: 0x%08lX", err_code)); + } +} + +STATIC void on_ble_evt(ble_evt_t *ble_evt, void *param) { + switch (ble_evt->header.evt_id) { +#if (BLE_API_VERSION == 4) + case BLE_GATTS_EVT_HVN_TX_COMPLETE: + m_tx_in_progress -= ble_evt->evt.gatts_evt.params.hvn_tx_complete.count; + break; +#else + case BLE_EVT_TX_COMPLETE: + m_tx_in_progress -= ble_evt->evt.common_evt.params.tx_complete.count; + break; +#endif + + case BLE_GATTC_EVT_READ_RSP: + { + ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; + m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data); + m_read_characteristic = NULL; + break; + } + + case BLE_GATTC_EVT_WRITE_RSP: + sd_mutex_release(m_write_mutex); +// m_write_done = true; + break; + } +} + +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self) { + ble_drv_add_event_handler(on_ble_evt, NULL); +} void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self) { - ble_drv_attr_c_read(self); + gattc_read(self); } void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - const bleio_device_obj_t *device = MP_OBJ_TO_PTR(self->service->device); + const bleio_device_obj_t *device = self->service->device; if (device->is_peripheral) { // TODO: Add indications if (self->props.notify) { - ble_drv_attr_s_notify(self, bufinfo); + gatts_notify(self, bufinfo); } else { - ble_drv_attr_s_write(self, bufinfo); + gatts_write(self, bufinfo); } } else { - ble_drv_attr_c_write(self, bufinfo); + gattc_write(self, bufinfo); } } diff --git a/ports/nrf/common-hal/bleio/Device.c b/ports/nrf/common-hal/bleio/Device.c index 9e7b382dd..195b6cd32 100644 --- a/ports/nrf/common-hal/bleio/Device.c +++ b/ports/nrf/common-hal/bleio/Device.c @@ -24,49 +24,528 @@ * THE SOFTWARE. */ +#include #include +#include "ble.h" #include "ble_drv.h" -#include "ble_gap.h" -#include "ble_gatt.h" -#include "ble_types.h" +#include "ble_hci.h" +#include "nrf_soc.h" +#include "py/objstr.h" #include "py/runtime.h" +#include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Device.h" #include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/UUID.h" -STATIC void gap_event_handler(bleio_device_obj_t *device, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data) { - if (event_id == BLE_GAP_EVT_CONNECTED) { - device->conn_handle = conn_handle; - } else if (event_id == BLE_GAP_EVT_DISCONNECTED) { - device->conn_handle = BLE_CONN_HANDLE_INVALID; +#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) +#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS) +#define BLE_SLAVE_LATENCY 0 +#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) + +#define BLE_ADV_LENGTH_FIELD_SIZE 1 +#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 +#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 + +#ifndef BLE_GAP_ADV_MAX_SIZE +#define BLE_GAP_ADV_MAX_SIZE 31 +#endif + +static bleio_service_obj_t *m_char_discovery_service; +static volatile bool m_discovery_successful; +static nrf_mutex_t *m_discovery_mutex; + +#if (BLUETOOTH_SD == 140) +static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; + +static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN]; + +static ble_data_t m_scan_buffer = { + .p_data = m_scan_buffer_data, + .len = BLE_GAP_SCAN_BUFFER_MIN +}; +#endif + +STATIC uint32_t set_advertisement_data(bleio_device_obj_t *device, bool connectable, mp_buffer_info_t *raw_data) { + common_hal_bleio_adapter_set_enabled(true); + + uint8_t adv_data[BLE_GAP_ADV_MAX_SIZE]; + uint8_t byte_pos = 0; + uint32_t err_code; + +#define ADD_FIELD(field, len) \ + do { \ + if (byte_pos + (len) > BLE_GAP_ADV_MAX_SIZE) { \ + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, \ + "Can not fit data into the advertisment packet")); \ + } \ + adv_data[byte_pos] = (field); \ + byte_pos += (len); \ + } while (0) + + GET_STR_DATA_LEN(device->name, name_data, name_len); + if (name_len > 0) { + ble_gap_conn_sec_mode_t sec_mode; + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); + + err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + // TODO: Shorten if too long + + ADD_FIELD(BLE_ADV_AD_TYPE_FIELD_SIZE + name_len, BLE_ADV_LENGTH_FIELD_SIZE); + ADD_FIELD(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, BLE_ADV_AD_TYPE_FIELD_SIZE); + + memcpy(&adv_data[byte_pos], name_data, name_len); + byte_pos += name_len; + } + + // set flags, default to disc mode + if (raw_data->len == 0) { + ADD_FIELD(BLE_ADV_AD_TYPE_FIELD_SIZE + BLE_AD_TYPE_FLAGS_DATA_SIZE, BLE_ADV_LENGTH_FIELD_SIZE); + ADD_FIELD(BLE_GAP_AD_TYPE_FLAGS, BLE_AD_TYPE_FLAGS_DATA_SIZE); + ADD_FIELD(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE, BLE_AD_TYPE_FLAGS_DATA_SIZE); + } else { + if (byte_pos + raw_data->len > BLE_GAP_ADV_MAX_SIZE) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not fit data into the advertisment packet")); + } + + memcpy(&adv_data[byte_pos], raw_data->buf, raw_data->len); + byte_pos += raw_data->len; + } + + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(device->service_list); + if (service_list->len > 0) { + bool has_128bit_services = false; + bool has_16bit_services = false; + + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + + if (service->is_secondary) { + continue; + } + + if (service->uuid->type == UUID_TYPE_16BIT) { + has_16bit_services = true; + } + + if (service->uuid->type == UUID_TYPE_128BIT) { + has_128bit_services = true; + } + } + + if (has_16bit_services) { + const uint8_t size_byte_pos = byte_pos; + uint8_t uuid_total_size = 0; + + // skip length byte for now, apply total length post calculation + byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + + ADD_FIELD(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, BLE_ADV_AD_TYPE_FIELD_SIZE); + + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + uint8_t encoded_size = 0; + + if ((service->uuid->type != UUID_TYPE_16BIT) || service->is_secondary) { + continue; + } + + ble_uuid_t uuid; + uuid.type = BLE_UUID_TYPE_BLE; + uuid.uuid = service->uuid->value[0] | (service->uuid->value[1] << 8); + + err_code = sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + uuid_total_size += encoded_size; + byte_pos += encoded_size; + } + + adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); + } + + if (has_128bit_services) { + const uint8_t size_byte_pos = byte_pos; + uint8_t uuid_total_size = 0; + + // skip length byte for now, apply total length post calculation + byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + + ADD_FIELD(BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE, BLE_ADV_AD_TYPE_FIELD_SIZE); + + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + uint8_t encoded_size = 0; + + if ((service->uuid->type != UUID_TYPE_128BIT) || service->is_secondary) { + continue; + } + + ble_uuid_t uuid; + uuid.type = service->uuid->uuid_vs_idx; + uuid.uuid = service->uuid->value[0] | (service->uuid->value[1] << 8); + + err_code = sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + uuid_total_size += encoded_size; + byte_pos += encoded_size; + } + + adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); + } + } + +#if (BLUETOOTH_SD == 132) + err_code = sd_ble_gap_adv_data_set(adv_data, byte_pos, NULL, 0); + if (err_code != NRF_SUCCESS) { + return err_code; + } +#endif + + static ble_gap_adv_params_t m_adv_params = { + .interval = MSEC_TO_UNITS(100, UNIT_0_625_MS), +#if (BLUETOOTH_SD == 140) + .properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED, + .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED, + .filter_policy = BLE_GAP_ADV_FP_ANY, + .primary_phy = BLE_GAP_PHY_1MBPS, +#else + .type = BLE_GAP_ADV_TYPE_ADV_IND, + .fp = BLE_GAP_ADV_FP_ANY, +#endif + }; + + if (!connectable) { +#if (BLUETOOTH_SD == 140) + m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED; +#else + m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_NONCONN_IND; +#endif } + + common_hal_bleio_device_stop_advertising(device); + +#if (BLUETOOTH_SD == 140) + const ble_gap_adv_data_t ble_gap_adv_data = { + .adv_data.p_data = adv_data, + .adv_data.len = byte_pos, + }; + + err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_CUSTOM); +#elif (BLUETOOTH_SD == 132 && BLE_API_VERSION == 4) + err_code = sd_ble_gap_adv_start(&m_adv_params, BLE_CONN_CFG_TAG_CUSTOM); +#else + err_code = sd_ble_gap_adv_start(&m_adv_params); +#endif + + return err_code; } -STATIC void gatts_event_handler(bleio_device_obj_t *device, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) { +STATIC bool discover_services(bleio_device_obj_t *device, uint16_t start_handle) { + m_discovery_successful = false; + + uint32_t err_code = sd_ble_gattc_primary_services_discover(device->conn_handle, start_handle, NULL); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to discover serivices, status: 0x%08lX", err_code)); + } + + err_code = sd_mutex_acquire(m_discovery_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to acquire mutex, status: 0x%08lX", err_code)); + } + + while (sd_mutex_acquire(m_discovery_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif + } + + err_code = sd_mutex_release(m_discovery_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to release mutex, status: 0x%08lX", err_code)); + } + return m_discovery_successful; } -void common_hal_bleio_device_start_advertising(bleio_device_obj_t *device, bleio_advertisement_data_t *adv_data) { - if (adv_data->connectable) { - ble_drv_gap_event_handler_set(device, gap_event_handler); - ble_drv_gatts_event_handler_set(device, gatts_event_handler); +STATIC bool discover_characteristics(bleio_device_obj_t *device, bleio_service_obj_t *service, uint16_t start_handle) { + m_char_discovery_service = service; + + ble_gattc_handle_range_t handle_range; + handle_range.start_handle = start_handle; + handle_range.end_handle = service->end_handle; + + m_discovery_successful = false; + + uint32_t err_code = sd_ble_gattc_characteristics_discover(device->conn_handle, &handle_range); + if (err_code != 0) { + return false; + } + + err_code = sd_mutex_acquire(m_discovery_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to acquire mutex, status: 0x%08lX", err_code)); } - ble_drv_advertise_data(adv_data); + while (sd_mutex_acquire(m_discovery_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif + } + + err_code = sd_mutex_release(m_discovery_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to release mutex, status: 0x%08lX", err_code)); + } + + return m_discovery_successful; +} + +STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, bleio_device_obj_t *device) { + for (size_t i = 0; i < response->count; ++i) { + const ble_gattc_service_t *gattc_service = &response->services[i]; + + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + service->device = device; + service->char_list = mp_obj_new_list(0, NULL); + service->start_handle = gattc_service->handle_range.start_handle; + service->end_handle = gattc_service->handle_range.end_handle; + service->handle = gattc_service->handle_range.start_handle; + + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + uuid->type = (gattc_service->uuid.type == BLE_UUID_TYPE_BLE) ? UUID_TYPE_16BIT : UUID_TYPE_128BIT; + uuid->value[0] = gattc_service->uuid.uuid & 0xFF; + uuid->value[1] = gattc_service->uuid.uuid >> 8; + service->uuid = uuid; + + mp_obj_list_append(device->service_list, service); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + + const uint32_t err_code = sd_mutex_release(m_discovery_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to release mutex, status: 0x%08lX", err_code)); + } +} + +STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio_device_obj_t *device) { + for (size_t i = 0; i < response->count; ++i) { + const ble_gattc_char_t *gattc_char = &response->chars[i]; + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + characteristic->base.type = &bleio_characteristic_type; + + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + uuid->type = (gattc_char->uuid.type == BLE_UUID_TYPE_BLE) ? UUID_TYPE_16BIT : UUID_TYPE_128BIT; + uuid->value[0] = gattc_char->uuid.uuid & 0xFF; + uuid->value[1] = gattc_char->uuid.uuid >> 8; + characteristic->uuid = uuid; + + characteristic->props.broadcast = gattc_char->char_props.broadcast; + characteristic->props.indicate = gattc_char->char_props.indicate; + characteristic->props.notify = gattc_char->char_props.notify; + characteristic->props.read = gattc_char->char_props.read; + characteristic->props.write = gattc_char->char_props.write; + characteristic->props.write_wo_resp = gattc_char->char_props.write_wo_resp; + characteristic->handle = gattc_char->handle_value; + + characteristic->service_handle = m_char_discovery_service->handle; + characteristic->service = m_char_discovery_service; + + mp_obj_list_append(m_char_discovery_service->char_list, MP_OBJ_FROM_PTR(characteristic)); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + + const uint32_t err_code = sd_mutex_release(m_discovery_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to release mutex, status: 0x%08lX", err_code)); + } +} + +STATIC void on_adv_report(ble_gap_evt_adv_report_t *report, bleio_device_obj_t *device) { + uint32_t err_code; + + if (memcmp(report->peer_addr.addr, device->address.value, BLEIO_ADDRESS_BYTES) != 0) { +#if (BLUETOOTH_SD == 140) + err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to continue scanning, status: 0x%0xlX", err_code)); + } +#endif + return; + } + + ble_gap_scan_params_t scan_params = { + .active = 1, + .interval = MSEC_TO_UNITS(100, UNIT_0_625_MS), + .window = MSEC_TO_UNITS(100, UNIT_0_625_MS), + }; + + ble_gap_addr_t addr; + memset(&addr, 0, sizeof(addr)); + + addr.addr_type = report->peer_addr.addr_type; + memcpy(addr.addr, report->peer_addr.addr, BLEIO_ADDRESS_BYTES); + + ble_gap_conn_params_t conn_params = { + .min_conn_interval = BLE_MIN_CONN_INTERVAL, + .max_conn_interval = BLE_MAX_CONN_INTERVAL, + .conn_sup_timeout = BLE_CONN_SUP_TIMEOUT, + .slave_latency = BLE_SLAVE_LATENCY, + }; + +#if (BLE_API_VERSION == 2) + err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params); +#else + err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_CUSTOM); +#endif + + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to connect, status: 0x%08lX", err_code)); + } +} + +STATIC void on_ble_evt(ble_evt_t *ble_evt, void *device_in) { + bleio_device_obj_t *device = (bleio_device_obj_t*)device_in; + + switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_CONNECTED: + { + ble_gap_conn_params_t conn_params; + device->conn_handle = ble_evt->evt.gap_evt.conn_handle; + + sd_ble_gap_ppcp_get(&conn_params); + sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params); + break; + } + + case BLE_GAP_EVT_DISCONNECTED: + device->conn_handle = BLE_CONN_HANDLE_INVALID; + break; + + case BLE_GAP_EVT_ADV_REPORT: + on_adv_report(&ble_evt->evt.gap_evt.params.adv_report, device); + break; + + case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: + on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, device); + break; + + case BLE_GATTC_EVT_CHAR_DISC_RSP: + on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, device); + break; + + case BLE_GATTS_EVT_SYS_ATTR_MISSING: + sd_ble_gatts_sys_attr_set(ble_evt->evt.gatts_evt.conn_handle, NULL, 0, 0); + break; + +#if (BLE_API_VERSION == 4) + case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: + sd_ble_gatts_exchange_mtu_reply(device->conn_handle, BLE_GATT_ATT_MTU_DEFAULT); + break; +#endif + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + sd_ble_gap_sec_params_reply(device->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: + { + ble_gap_evt_conn_param_update_request_t *request = &ble_evt->evt.gap_evt.params.conn_param_update_request; + sd_ble_gap_conn_param_update(device->conn_handle, &request->conn_params); + break; + } + } +} + +void common_hal_bleio_device_start_advertising(bleio_device_obj_t *device, bool connectable, mp_buffer_info_t *raw_data) { + if (connectable) { + ble_drv_add_event_handler(on_ble_evt, device); + } + + const uint32_t err_code = set_advertisement_data(device, connectable, raw_data); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to start advertisment, status: 0x%08lX", err_code)); + } } void common_hal_bleio_device_stop_advertising(bleio_device_obj_t *device) { - (void)device; + uint32_t err_code; + +#if (BLUETOOTH_SD == 140) + if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) + return; - ble_drv_advertise_stop(); + err_code = sd_ble_gap_adv_stop(m_adv_handle); +#else + err_code = sd_ble_gap_adv_stop(); +#endif + + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to stop advertisment, status: 0x%08lX", err_code)); + } } void common_hal_bleio_device_connect(bleio_device_obj_t *device) { - ble_drv_gap_event_handler_set(device, gap_event_handler); + ble_drv_add_event_handler(on_ble_evt, device); - ble_drv_connect(device); + ble_gap_scan_params_t scan_params = { + .interval = MSEC_TO_UNITS(100, UNIT_0_625_MS), + .window = MSEC_TO_UNITS(100, UNIT_0_625_MS), +#if (BLUETOOTH_SD == 140) + .scan_phys = BLE_GAP_PHY_1MBPS, +#endif + }; + + common_hal_bleio_adapter_set_enabled(true); + + uint32_t err_code; +#if (BLUETOOTH_SD == 140) + err_code = sd_ble_gap_scan_start(&scan_params, &m_scan_buffer); +#else + err_code = sd_ble_gap_scan_start(&scan_params); +#endif + + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to start scanning, status: 0x%0xlX", err_code)); + } while (device->conn_handle == BLE_CONN_HANDLE_INVALID) { #ifdef MICROPY_VM_HOOK_LOOP @@ -76,13 +555,23 @@ void common_hal_bleio_device_connect(bleio_device_obj_t *device) { // TODO: read name + if (m_discovery_mutex == NULL) { + m_discovery_mutex = m_new_ll(nrf_mutex_t, 1); + + err_code = sd_mutex_new(m_discovery_mutex); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to create mutex, status: 0x%0xlX", err_code)); + } + } + // find services - bool found_service = ble_drv_discover_services(device, BLE_GATT_HANDLE_START); + bool found_service = discover_services(device, BLE_GATT_HANDLE_START); while (found_service) { const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(device->service_list); const bleio_service_obj_t *service = service_list->items[service_list->len - 1]; - found_service = ble_drv_discover_services(device, service->end_handle + 1); + found_service = discover_services(device, service->end_handle + 1); } // find characteristics in each service @@ -90,7 +579,7 @@ void common_hal_bleio_device_connect(bleio_device_obj_t *device) { for (size_t i = 0; i < service_list->len; ++i) { bleio_service_obj_t *service = service_list->items[i]; - bool found_char = ble_drv_discover_characteristic(device, service, service->start_handle); + bool found_char = discover_characteristics(device, service, service->start_handle); while (found_char) { const mp_obj_list_t *char_list = MP_OBJ_TO_PTR(service->char_list); const bleio_characteristic_obj_t *characteristic = char_list->items[char_list->len - 1]; @@ -100,11 +589,11 @@ void common_hal_bleio_device_connect(bleio_device_obj_t *device) { break; } - found_char = ble_drv_discover_characteristic(device, service, next_handle); + found_char = discover_characteristics(device, service, next_handle); } } } void common_hal_bleio_device_disconnect(bleio_device_obj_t *device) { - ble_drv_disconnect(device); + sd_ble_gap_disconnect(device->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); } diff --git a/ports/nrf/common-hal/bleio/Scanner.c b/ports/nrf/common-hal/bleio/Scanner.c index 150c417a3..17af49621 100644 --- a/ports/nrf/common-hal/bleio/Scanner.c +++ b/ports/nrf/common-hal/bleio/Scanner.c @@ -28,25 +28,83 @@ #include #include "ble_drv.h" +#include "ble_gap.h" #include "py/mphal.h" +#include "py/nlr.h" +#include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/Scanner.h" #include "shared-module/bleio/ScanEntry.h" -STATIC void adv_event_handler(bleio_scanner_obj_t *self, bleio_scanentry_obj_t *entry) { +#if (BLUETOOTH_SD == 140) +static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN]; + +static ble_data_t m_scan_buffer = { + m_scan_buffer_data, + BLE_GAP_SCAN_BUFFER_MIN +}; +#endif + +STATIC void on_ble_evt(ble_evt_t *ble_evt, void *scanner_in) { + bleio_scanner_obj_t *scanner = (bleio_scanner_obj_t*)scanner_in; + ble_gap_evt_adv_report_t *report = &ble_evt->evt.gap_evt.params.adv_report; + + if (ble_evt->header.evt_id != BLE_GAP_EVT_ADV_REPORT) { + return; + } + // TODO: Don't add new entry for each item, group by address and update + bleio_scanentry_obj_t *entry = m_new_obj(bleio_scanentry_obj_t); + entry->base.type = &bleio_scanentry_type; + entry->rssi = report->rssi; - mp_obj_list_append(self->adv_reports, entry); + entry->address.type = report->peer_addr.addr_type; + memcpy(entry->address.value, report->peer_addr.addr, BLEIO_ADDRESS_BYTES); - ble_drv_scan_continue(); +#if (BLUETOOTH_SD == 140) + entry->data = mp_obj_new_bytearray(report->data.len, report->data.p_data); +#else + entry->data = mp_obj_new_bytearray(report->dlen, report->data); +#endif + + mp_obj_list_append(scanner->adv_reports, entry); + +#if (BLUETOOTH_SD == 140) + const uint32_t err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to continue scanning, status: 0x%0xlX", err_code)); + } +#endif } void common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_int_t timeout) { - ble_drv_adv_report_handler_set(self, adv_event_handler); + ble_drv_add_event_handler(on_ble_evt, self); + + ble_gap_scan_params_t scan_params = { + .interval = MSEC_TO_UNITS(self->interval, UNIT_0_625_MS), + .window = MSEC_TO_UNITS(self->window, UNIT_0_625_MS), +#if (BLUETOOTH_SD == 140) + .scan_phys = BLE_GAP_PHY_1MBPS, +#endif + }; + + common_hal_bleio_adapter_set_enabled(true); - ble_drv_scan_start(self->interval, self->window); + uint32_t err_code; +#if (BLUETOOTH_SD == 140) + err_code = sd_ble_gap_scan_start(&scan_params, &m_scan_buffer); +#else + err_code = sd_ble_gap_scan_start(&scan_params); +#endif - mp_hal_delay_ms(timeout); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to start scanning, status: 0x%0xlX", err_code)); + } - ble_drv_scan_stop(); + if (timeout > 0) { + mp_hal_delay_ms(timeout); + sd_ble_gap_scan_stop(); + } } diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c index b6e0595b1..9e376b333 100644 --- a/ports/nrf/common-hal/bleio/Service.c +++ b/ports/nrf/common-hal/bleio/Service.c @@ -25,14 +25,91 @@ */ #include "ble_drv.h" -#include "shared-module/bleio/Service.h" +#include "ble.h" +#include "py/nlr.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/Adapter.h" void common_hal_bleio_service_construct(bleio_service_obj_t *self) { - ble_drv_service_add(self); + ble_uuid_t uuid = { + .type = BLE_UUID_TYPE_BLE, + .uuid = self->uuid->value[0] | (self->uuid->value[1] << 8) + }; + + if (self->uuid->type == UUID_TYPE_128BIT) { + uuid.type = self->uuid->uuid_vs_idx; + } + + uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; + if (self->is_secondary) { + service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; + } + + common_hal_bleio_adapter_set_enabled(true); + + const uint32_t err_code = sd_ble_gatts_service_add(service_type, &uuid, &self->handle); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to add service, status: 0x%08lX", err_code)); + } } void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic) { - if (ble_drv_characteristic_add(characteristic)) { - characteristic->service = self; + ble_gatts_char_md_t char_md = { + .char_props.broadcast = characteristic->props.broadcast, + .char_props.read = characteristic->props.read, + .char_props.write_wo_resp = characteristic->props.write_wo_resp, + .char_props.write = characteristic->props.write, + .char_props.notify = characteristic->props.notify, + .char_props.indicate = characteristic->props.indicate, + }; + + ble_gatts_attr_md_t cccd_md = { + .vloc = BLE_GATTS_VLOC_STACK, + }; + + if (char_md.char_props.notify || char_md.char_props.indicate) { + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm); + + char_md.p_cccd_md = &cccd_md; } + + ble_uuid_t uuid = { + .type = BLE_UUID_TYPE_BLE, + .uuid = characteristic->uuid->value[0] | (characteristic->uuid->value[1] << 8), + }; + + if (characteristic->uuid->type == UUID_TYPE_128BIT) + uuid.type = characteristic->uuid->uuid_vs_idx; + + ble_gatts_attr_md_t attr_md = { + .vloc = BLE_GATTS_VLOC_STACK, + .vlen = 1, + }; + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + + ble_gatts_attr_t attr_char_value = { + .p_uuid = &uuid, + .p_attr_md = &attr_md, + .init_len = sizeof(uint8_t), + .max_len = (BLE_GATT_ATT_MTU_DEFAULT - 3), + }; + + ble_gatts_char_handles_t handles; + + uint32_t err_code; + err_code = sd_ble_gatts_characteristic_add(characteristic->service_handle, &char_md, &attr_char_value, &handles); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to add characteristic, status: 0x%08lX", err_code)); + } + + characteristic->user_desc_handle = handles.user_desc_handle; + characteristic->cccd_handle = handles.cccd_handle; + characteristic->sccd_handle = handles.sccd_handle; + characteristic->handle = handles.value_handle; + characteristic->service = self; } diff --git a/ports/nrf/common-hal/bleio/UUID.c b/ports/nrf/common-hal/bleio/UUID.c index e655589a6..4d4c8c9fb 100644 --- a/ports/nrf/common-hal/bleio/UUID.c +++ b/ports/nrf/common-hal/bleio/UUID.c @@ -25,10 +25,13 @@ * THE SOFTWARE. */ +#include "ble.h" #include "ble_drv.h" #include "common-hal/bleio/UUID.h" +#include "nrf_error.h" #include "py/objstr.h" #include "py/runtime.h" +#include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/UUID.h" #define UUID_STR_16BIT_LEN 6 @@ -59,37 +62,44 @@ void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, const mp_obj_t *uui } else if (str_len == UUID_STR_128BIT_LEN) { self->type = UUID_TYPE_128BIT; - uint8_t buffer[16]; - buffer[0] = xdigit_8b_value(str_data[35], str_data[34]); - buffer[1] = xdigit_8b_value(str_data[33], str_data[32]); - buffer[2] = xdigit_8b_value(str_data[31], str_data[30]); - buffer[3] = xdigit_8b_value(str_data[29], str_data[28]); - buffer[4] = xdigit_8b_value(str_data[27], str_data[26]); - buffer[5] = xdigit_8b_value(str_data[25], str_data[24]); + ble_uuid128_t vs_uuid; + vs_uuid.uuid128[0] = xdigit_8b_value(str_data[35], str_data[34]); + vs_uuid.uuid128[1] = xdigit_8b_value(str_data[33], str_data[32]); + vs_uuid.uuid128[2] = xdigit_8b_value(str_data[31], str_data[30]); + vs_uuid.uuid128[3] = xdigit_8b_value(str_data[29], str_data[28]); + vs_uuid.uuid128[4] = xdigit_8b_value(str_data[27], str_data[26]); + vs_uuid.uuid128[5] = xdigit_8b_value(str_data[25], str_data[24]); // 23 '-' - buffer[6] = xdigit_8b_value(str_data[22], str_data[21]); - buffer[7] = xdigit_8b_value(str_data[20], str_data[19]); + vs_uuid.uuid128[6] = xdigit_8b_value(str_data[22], str_data[21]); + vs_uuid.uuid128[7] = xdigit_8b_value(str_data[20], str_data[19]); // 18 '-' - buffer[8] = xdigit_8b_value(str_data[17], str_data[16]); - buffer[9] = xdigit_8b_value(str_data[15], str_data[14]); + vs_uuid.uuid128[8] = xdigit_8b_value(str_data[17], str_data[16]); + vs_uuid.uuid128[9] = xdigit_8b_value(str_data[15], str_data[14]); // 13 '-' - buffer[10] = xdigit_8b_value(str_data[12], str_data[11]); - buffer[11] = xdigit_8b_value(str_data[10], str_data[9]); + vs_uuid.uuid128[10] = xdigit_8b_value(str_data[12], str_data[11]); + vs_uuid.uuid128[11] = xdigit_8b_value(str_data[10], str_data[9]); // 8 '-' self->value[0] = xdigit_8b_value(str_data[7], str_data[6]); self->value[1] = xdigit_8b_value(str_data[5], str_data[4]); - buffer[14] = xdigit_8b_value(str_data[3], str_data[2]); - buffer[15] = xdigit_8b_value(str_data[1], str_data[0]); + vs_uuid.uuid128[14] = xdigit_8b_value(str_data[3], str_data[2]); + vs_uuid.uuid128[15] = xdigit_8b_value(str_data[1], str_data[0]); + + common_hal_bleio_adapter_set_enabled(true); + + const uint32_t err_code = sd_ble_uuid_vs_add(&vs_uuid, &self->uuid_vs_idx); + if (err_code != NRF_SUCCESS) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Failed to add Vendor Specific UUID, status: 0x%08lX", err_code)); + } - ble_drv_uuid_add_vs(buffer, &self->uuid_vs_idx); } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "Invalid UUID string length")); + "Invalid UUID string length")); } return; @@ -113,10 +123,10 @@ void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, const mp_obj_t *uui void common_hal_bleio_uuid_print(bleio_uuid_obj_t *self, const mp_print_t *print) { if (self->type == UUID_TYPE_16BIT) { mp_printf(print, "UUID(uuid: 0x" HEX2_FMT HEX2_FMT ")", - self->value[1], self->value[0]); + self->value[1], self->value[0]); } else { mp_printf(print, "UUID(uuid: 0x" HEX2_FMT HEX2_FMT ", VS idx: " HEX2_FMT ")", - self->value[1], self->value[0], self->uuid_vs_idx); + self->value[1], self->value[0], self->uuid_vs_idx); } } diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 6d32ff6f9..0b6daf711 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -25,1027 +25,73 @@ * THE SOFTWARE. */ -#if BLUETOOTH_SD - -#include -#include #include +#include -#if (BLUETOOTH_SD == 132) -#define NRF52 // Needed for SD132 v2 -#endif - -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/ScanEntry.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" -#include "shared-module/bleio/Device.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "supervisor/shared/translate.h" +#include "ble.h" #include "ble_drv.h" #include "nrf_nvic.h" #include "nrf_sdm.h" -#include "nrfx_power.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/ScanEntry.h" -#include "shared-bindings/bleio/UUID.h" - -#define BLE_DRIVER_LOG printf - -#define BLE_ADV_LENGTH_FIELD_SIZE 1 -#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 -#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 - -#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) -#define UNIT_0_625_MS (625) -#define UNIT_10_MS (10000) - -#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) -#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS) -#define BLE_SLAVE_LATENCY 0 -#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) - -#ifndef BLE_GAP_ADV_MAX_SIZE -#define BLE_GAP_ADV_MAX_SIZE 31 -#endif - -#define SD_TEST_OR_ENABLE() \ -if (ble_drv_stack_enabled() == 0) { \ - (void)ble_drv_stack_enable(); \ -} - -static ble_drv_adv_evt_callback_t adv_event_handler; -static ble_drv_gatts_evt_callback_t gatts_event_handler; -static ble_drv_gap_evt_callback_t gap_event_handler; - -static bleio_characteristic_obj_t *mp_gattc_char_data_observer; -static bleio_device_obj_t *mp_gattc_disc_service_observer; -static bleio_service_obj_t *mp_gattc_disc_char_observer; -static bleio_address_obj_t *mp_connect_address; -static bleio_device_obj_t *mp_gatts_observer; -static bleio_scanner_obj_t *mp_adv_observer; -static bleio_device_obj_t *mp_gap_observer; - -static volatile bool m_primary_service_found; -static volatile bool m_characteristic_found; -static volatile bool m_tx_in_progress; -static volatile bool m_write_done; +#include "py/misc.h" nrf_nvic_state_t nrf_nvic_state = { 0 }; -#if (BLUETOOTH_SD == 140) -static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; -static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN]; - -static ble_data_t m_scan_buffer = -{ - m_scan_buffer_data, - BLE_GAP_SCAN_BUFFER_MIN -}; -#endif - -void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { - BLE_DRIVER_LOG("ERROR: SoftDevice assert!!!"); -} - -uint32_t ble_drv_stack_enable(void) { - nrf_clock_lf_cfg_t clock_config = { - .source = NRF_CLOCK_LF_SRC_XTAL, - .rc_ctiv = 0, - .rc_temp_ctiv = 0, -#if (BLE_API_VERSION == 4) - .accuracy = NRF_CLOCK_LF_ACCURACY_20_PPM -#else - .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM -#endif - }; - -#if (BLUETOOTH_SD == 140) - // The SD takes over the POWER IRQ and will fail if the IRQ is already in use - nrfx_power_uninit(); -#endif - - uint32_t err_code = sd_softdevice_enable(&clock_config, softdevice_assert_handler); - if (err_code != NRF_SUCCESS) - BLE_DRIVER_LOG("SoftDevice enable status: " UINT_FMT "\n", (uint16_t)err_code); - - err_code = sd_nvic_EnableIRQ(SD_EVT_IRQn); - if (err_code != NRF_SUCCESS) - BLE_DRIVER_LOG("IRQ enable status: " UINT_FMT "\n", (uint16_t)err_code); - - // Enable BLE stack. - uint32_t app_ram_start; -#if (BLE_API_VERSION == 2) - ble_enable_params_t ble_enable_params; - memset(&ble_enable_params, 0x00, sizeof(ble_enable_params)); - ble_enable_params.gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT; - ble_enable_params.gatts_enable_params.service_changed = 0; - ble_enable_params.gap_enable_params.periph_conn_count = 1; - ble_enable_params.gap_enable_params.central_conn_count = 1; - - app_ram_start = 0x200039c0; - err_code = sd_ble_enable(&ble_enable_params, &app_ram_start); // 8K SD headroom from linker script. -#else - app_ram_start = 0x20004000; - err_code = sd_ble_enable(&app_ram_start); -#endif - - if (err_code != NRF_SUCCESS) { - BLE_DRIVER_LOG("BLE ram size: " UINT_FMT "\n", (uint16_t)app_ram_start); - BLE_DRIVER_LOG("BLE enable status: " UINT_FMT "\n", (uint16_t)err_code); - } - - return err_code; -} - -uint8_t ble_drv_stack_enabled(void) { - uint8_t is_enabled; - uint32_t err_code = sd_softdevice_is_enabled(&is_enabled); - - if (err_code != NRF_SUCCESS) { - BLE_DRIVER_LOG("Is enabled status: " UINT_FMT "\n", (uint16_t)err_code); - } - - return is_enabled; -} - -void ble_drv_address_get(bleio_address_obj_t *address) { - SD_TEST_OR_ENABLE(); - - ble_gap_addr_t local_ble_addr; -#if (BLE_API_VERSION == 2) - uint32_t err_code = sd_ble_gap_address_get(&local_ble_addr); -#else - uint32_t err_code = sd_ble_gap_addr_get(&local_ble_addr); -#endif - - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not query for the device address."))); - } - - BLE_DRIVER_LOG("ble address, type: " HEX2_FMT ", " \ - "address: " HEX2_FMT ":" HEX2_FMT ":" HEX2_FMT ":" \ - HEX2_FMT ":" HEX2_FMT ":" HEX2_FMT "\n", \ - local_ble_addr.addr_type, \ - local_ble_addr.addr[5], local_ble_addr.addr[4], local_ble_addr.addr[3], \ - local_ble_addr.addr[2], local_ble_addr.addr[1], local_ble_addr.addr[0]); - - address->type = local_ble_addr.addr_type; - memcpy(address->value, local_ble_addr.addr, BLEIO_ADDRESS_BYTES); -} - -bool ble_drv_uuid_add_vs(uint8_t *uuid, uint8_t *idx) { - SD_TEST_OR_ENABLE(); - - if (sd_ble_uuid_vs_add((ble_uuid128_t const *)uuid, idx) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not add Vendor Specific 128-bit UUID."))); - } - - return true; -} - -void ble_drv_service_add(bleio_service_obj_t *service) { - SD_TEST_OR_ENABLE(); - - ble_uuid_t uuid = { - .type = BLE_UUID_TYPE_BLE, - .uuid = service->uuid->value[0] | (service->uuid->value[1] << 8) - }; - - if (service->uuid->type == UUID_TYPE_128BIT) { - uuid.type = service->uuid->uuid_vs_idx; - } - - uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; - if (service->is_secondary) { - service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; - } - - if (sd_ble_gatts_service_add(service_type, &uuid, &service->handle) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not add Service."))); - } -} - -bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic) { - ble_gatts_char_md_t char_md; - ble_gatts_attr_md_t cccd_md; - ble_gatts_attr_t attr_char_value; - ble_uuid_t uuid; - ble_gatts_attr_md_t attr_md; - - memset(&char_md, 0, sizeof(char_md)); - - char_md.char_props.broadcast = characteristic->props.broadcast; - char_md.char_props.read = characteristic->props.read; - char_md.char_props.write_wo_resp = characteristic->props.write_wo_resp; - char_md.char_props.write = characteristic->props.write; - char_md.char_props.notify = characteristic->props.notify; - char_md.char_props.indicate = characteristic->props.indicate; - - char_md.p_char_user_desc = NULL; - char_md.p_char_pf = NULL; - char_md.p_user_desc_md = NULL; - char_md.p_sccd_md = NULL; - - if (characteristic->props.notify || characteristic->props.notify) { - memset(&cccd_md, 0, sizeof(cccd_md)); - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm); - cccd_md.vloc = BLE_GATTS_VLOC_STACK; - char_md.p_cccd_md = &cccd_md; - } else { - char_md.p_cccd_md = NULL; - } - - uuid.type = BLE_UUID_TYPE_BLE; - if (characteristic->uuid->type == UUID_TYPE_128BIT) - uuid.type = characteristic->uuid->uuid_vs_idx; - - uuid.uuid = characteristic->uuid->value[0]; - uuid.uuid += characteristic->uuid->value[1] << 8; - - memset(&attr_md, 0, sizeof(attr_md)); - - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); - - attr_md.vloc = BLE_GATTS_VLOC_STACK; - attr_md.rd_auth = 0; - attr_md.wr_auth = 0; - attr_md.vlen = 1; - - memset(&attr_char_value, 0, sizeof(attr_char_value)); - - attr_char_value.p_uuid = &uuid; - attr_char_value.p_attr_md = &attr_md; - attr_char_value.init_len = sizeof(uint8_t); - attr_char_value.init_offs = 0; -#if (BLE_API_VERSION == 2) - attr_char_value.max_len = (GATT_MTU_SIZE_DEFAULT - 3); -#else - attr_char_value.max_len = (BLE_GATT_ATT_MTU_DEFAULT - 3); -#endif - - ble_gatts_char_handles_t handles; - - uint32_t err_code = sd_ble_gatts_characteristic_add(characteristic->service_handle, - &char_md, - &attr_char_value, - &handles); - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not add Characteristic. status: 0x%08lX"), err_code)); - } - - // apply handles to object instance - characteristic->handle = handles.value_handle; - characteristic->user_desc_handle = handles.user_desc_handle; - characteristic->cccd_handle = handles.cccd_handle; - characteristic->sccd_handle = handles.sccd_handle; - - return true; -} - -// TODO: Replace with just bleio_device_obj_t + data -bool ble_drv_advertise_data(bleio_advertisement_data_t *adv_params) { - SD_TEST_OR_ENABLE(); - - uint8_t byte_pos = 0; - uint8_t adv_data[BLE_GAP_ADV_MAX_SIZE]; - - GET_STR_DATA_LEN(adv_params->device_name, name_data, name_len); - - if (name_len > 0) { - ble_gap_conn_sec_mode_t sec_mode; - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); - - if (sd_ble_gap_device_name_set(&sec_mode, - name_data, - name_len) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not apply device name in the stack."))); - } - - adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + name_len); - byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; - - // TODO: Shorten if too long - adv_data[byte_pos] = BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME; - byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE; - - memcpy(&adv_data[byte_pos], name_data, name_len); - - byte_pos += name_len; - } - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(adv_params->data, &bufinfo, MP_BUFFER_WRITE); - - // set flags, default to disc mode - if (bufinfo.len == 0) { - adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + BLE_AD_TYPE_FLAGS_DATA_SIZE); - byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; - - adv_data[byte_pos] = BLE_GAP_AD_TYPE_FLAGS; - byte_pos += BLE_AD_TYPE_FLAGS_DATA_SIZE; - - adv_data[byte_pos] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; - byte_pos += 1; - } - - const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(adv_params->services); - if (service_list->len > 0) { - bool type_16bit_present = false; - bool type_128bit_present = false; - - for (size_t i = 0; i < service_list->len; ++i) { - const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); - - if (service->uuid->type == UUID_TYPE_16BIT) { - type_16bit_present = true; - } - - if (service->uuid->type == UUID_TYPE_128BIT) { - type_128bit_present = true; - } - } - - if (type_16bit_present) { - uint8_t size_byte_pos = byte_pos; - - // skip length byte for now, apply total length post calculation - byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; +__attribute__((aligned(4))) +static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (BLE_GATT_ATT_MTU_DEFAULT)]; - adv_data[byte_pos] = BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE; - byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE; +typedef struct event_handler { + struct event_handler *next; + void *param; + ble_drv_evt_handler_t func; +} event_handler_t; - uint8_t uuid_total_size = 0; - uint8_t encoded_size = 0; +static event_handler_t *m_event_handlers; - for (size_t i = 0; i < service_list->len; ++i) { - const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); - - if (service->uuid->type != UUID_TYPE_16BIT) { - continue; - } - - ble_uuid_t uuid; - uuid.type = BLE_UUID_TYPE_BLE; - uuid.uuid = service->uuid->value[0] | (service->uuid->value[1] << 8); - - // calculate total size of uuids - if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not encode UUID, to check length."))); - } - - // do encoding into the adv buffer - if (sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can encode UUID into the advertisement packet."))); - } - - uuid_total_size += encoded_size; // size of entry - byte_pos += encoded_size; // relative to adv data packet - } - - adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); - } - - if (type_128bit_present) { - uint8_t size_byte_pos = byte_pos; - - // skip length byte for now, apply total length post calculation - byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; - - adv_data[byte_pos] = BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE; - byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE; - - uint8_t uuid_total_size = 0; - uint8_t encoded_size = 0; - - for (size_t i = 0; i < service_list->len; ++i) { - const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); - - if (service->uuid->type != UUID_TYPE_128BIT) { - continue; - } - - ble_uuid_t uuid; - uuid.type = service->uuid->uuid_vs_idx; - uuid.uuid = service->uuid->value[0] | (service->uuid->value[1] << 8); - - // calculate total size of uuids - if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not encode UUID, to check length."))); - } - - // do encoding into the adv buffer - if (sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can encode UUID into the advertisement packet."))); - } - - uuid_total_size += encoded_size; // size of entry - byte_pos += encoded_size; // relative to adv data packet - } +void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) { + event_handler_t *handler = m_new_ll(event_handler_t, 1); + handler->next = NULL; + handler->param = param; + handler->func = func; - adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); - } - } - - if (bufinfo.len > 0) { - if (byte_pos + bufinfo.len > BLE_GAP_ADV_MAX_SIZE) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not fit data into the advertisement packet."))); - } - - memcpy(adv_data, bufinfo.buf, bufinfo.len); - byte_pos += bufinfo.len; - } - - uint32_t err_code; -#if (BLUETOOTH_SD == 132) - if ((err_code = sd_ble_gap_adv_data_set(adv_data, byte_pos, NULL, 0)) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not apply advertisement data. status: 0x%02x"), (uint16_t)err_code)); - } -#endif - - static ble_gap_adv_params_t m_adv_params; - - // initialize advertising params - memset(&m_adv_params, 0, sizeof(m_adv_params)); - if (adv_params->connectable) { -#if (BLUETOOTH_SD == 140) - m_adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; -#else - m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND; -#endif - } else { -#if (BLUETOOTH_SD == 140) - m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED; -#else - m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_NONCONN_IND; -#endif - } - - m_adv_params.p_peer_addr = NULL; // undirected advertisement - m_adv_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); // approx 8 ms -#if (BLUETOOTH_SD == 140) - m_adv_params.duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED; - m_adv_params.filter_policy = BLE_GAP_ADV_FP_ANY; - m_adv_params.primary_phy = BLE_GAP_PHY_1MBPS; -#else - m_adv_params.fp = BLE_GAP_ADV_FP_ANY; - m_adv_params.timeout = 0; // infinite advertisement -#endif - - ble_drv_advertise_stop(); - -#if (BLUETOOTH_SD == 140) - const ble_gap_adv_data_t ble_gap_adv_data = { - .adv_data = { - .p_data = adv_data, - .len = byte_pos - } - }; - - if ((err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params)) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not apply advertisement data. status: 0x%02x"), (uint16_t)err_code)); - } - err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_DEFAULT); -#elif (BLUETOOTH_SD == 132 && BLE_API_VERSION == 4) - err_code = sd_ble_gap_adv_start(&m_adv_params, BLE_CONN_CFG_TAG_DEFAULT); -#else - err_code = sd_ble_gap_adv_start(&m_adv_params); -#endif - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not start advertisement. status: 0x%02x"), (uint16_t)err_code)); - } - - return true; -} - -void ble_drv_advertise_stop(void) { - uint32_t err_code; - -#if (BLUETOOTH_SD == 140) - if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) + if (m_event_handlers == NULL) { + m_event_handlers = handler; return; - - err_code = sd_ble_gap_adv_stop(m_adv_handle); -#else - err_code = sd_ble_gap_adv_stop(); -#endif - - if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not stop advertisement. status: 0x%02x"), (uint16_t)err_code)); - } -} - -void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) { - ble_gatts_value_t gatts_value; - memset(&gatts_value, 0, sizeof(gatts_value)); - - gatts_value.len = len; - gatts_value.offset = 0; - gatts_value.p_value = p_data; - - uint32_t err_code = sd_ble_gatts_value_get(conn_handle, - handle, - &gatts_value); - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not read attribute value. status: 0x%02x"), (uint16_t)err_code)); } -} - -void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - bleio_device_obj_t *device = MP_OBJ_TO_PTR(characteristic->service->device); - uint16_t conn_handle = device->conn_handle; - ble_gatts_value_t gatts_value; - - memset(&gatts_value, 0, sizeof(gatts_value)); - - gatts_value.len = bufinfo->len; - gatts_value.offset = 0; - gatts_value.p_value = bufinfo->buf; - - uint32_t err_code = sd_ble_gatts_value_set(conn_handle, characteristic->handle, &gatts_value); - - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not write attribute value. status: 0x%02x"), (uint16_t)err_code)); - } -} - -void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - bleio_device_obj_t *device = MP_OBJ_TO_PTR(characteristic->service->device); - uint16_t conn_handle = device->conn_handle; - ble_gatts_hvx_params_t hvx_params; - uint16_t hvx_len = bufinfo->len; - - memset(&hvx_params, 0, sizeof(hvx_params)); - - hvx_params.handle = characteristic->handle; - hvx_params.type = BLE_GATT_HVX_NOTIFICATION; - hvx_params.offset = 0; - hvx_params.p_len = &hvx_len; - hvx_params.p_data = bufinfo->buf; - - while (m_tx_in_progress) { -#ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP -#endif - } - - m_tx_in_progress = true; - uint32_t err_code; - if ((err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params)) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not notify attribute value. status: 0x%02x"), (uint16_t)err_code)); - } -} - -void ble_drv_gap_event_handler_set(bleio_device_obj_t *device, ble_drv_gap_evt_callback_t evt_handler) { - mp_gap_observer = device; - gap_event_handler = evt_handler; -} - -void ble_drv_gatts_event_handler_set(bleio_device_obj_t *device, ble_drv_gatts_evt_callback_t evt_handler) { - mp_gatts_observer = device; - gatts_event_handler = evt_handler; -} - -void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *device, ble_drv_adv_evt_callback_t evt_handler) { - mp_adv_observer = device; - adv_event_handler = evt_handler; -} - -void ble_drv_attr_c_read(bleio_characteristic_obj_t *characteristic) { - bleio_service_obj_t *service = characteristic->service; - bleio_device_obj_t *device = MP_OBJ_TO_PTR(service->device); - - mp_gattc_char_data_observer = characteristic; - - const uint32_t err_code = sd_ble_gattc_read(device->conn_handle, characteristic->handle, 0); - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not read attribute value. status: 0x%02x"), (uint16_t)err_code)); - } - - while (mp_gattc_char_data_observer != NULL) { -#ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP -#endif - } -} - -void ble_drv_attr_c_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - bleio_device_obj_t *device = MP_OBJ_TO_PTR(characteristic->service->device); - uint16_t conn_handle = device->conn_handle; - - ble_gattc_write_params_t write_params; - write_params.write_op = BLE_GATT_OP_WRITE_REQ; - - if (characteristic->props.write_wo_resp) { - write_params.write_op = BLE_GATT_OP_WRITE_CMD; - } - - write_params.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_CANCEL; - write_params.handle = characteristic->handle; - write_params.offset = 0; - write_params.len = bufinfo->len; - write_params.p_value = bufinfo->buf; - - m_write_done = (write_params.write_op == BLE_GATT_OP_WRITE_CMD); - - uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); - if (err_code != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not write attribute value. status: 0x%02x"), (uint16_t)err_code)); - } - - while (m_write_done != true) { -#ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP -#endif - } -} - -void ble_drv_scan_start(uint16_t interval, uint16_t window) { - SD_TEST_OR_ENABLE(); - - ble_gap_scan_params_t scan_params; - memset(&scan_params, 0, sizeof(ble_gap_scan_params_t)); - - scan_params.active = 1; - scan_params.interval = MSEC_TO_UNITS(interval, UNIT_0_625_MS); - scan_params.window = MSEC_TO_UNITS(window, UNIT_0_625_MS); -#if (BLUETOOTH_SD == 140) - scan_params.scan_phys = BLE_GAP_PHY_1MBPS; -#endif - scan_params.timeout = 0; // Infinite - - uint32_t err_code; -#if (BLUETOOTH_SD == 140) - if ((err_code = sd_ble_gap_scan_start(&scan_params, &m_scan_buffer)) != 0) { -#else - if ((err_code = sd_ble_gap_scan_start(&scan_params)) != 0) { -#endif - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - translate("Can not start scanning. status: 0x%02x"), (uint16_t)err_code)); - } -} - -void ble_drv_scan_continue(void) { - SD_TEST_OR_ENABLE(); - -#if (BLUETOOTH_SD == 140) - uint32_t err_code; - if ((err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer)) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - "Can not continue scanning. status: 0x" HEX2_FMT, (uint16_t)err_code)); - } -#endif -} - -void ble_drv_scan_stop(void) { - sd_ble_gap_scan_stop(); -} - -STATIC void ble_drv_connect_scan_callback(bleio_scanner_obj_t *scanner, bleio_scanentry_obj_t *entry) { - if (memcmp(entry->address.value, mp_connect_address->value, BLEIO_ADDRESS_BYTES) == 0) { - ble_drv_adv_report_handler_set(NULL, NULL); - - ble_gap_scan_params_t scan_params; - memset(&scan_params, 0, sizeof(scan_params)); - - scan_params.active = 1; - scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); - scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS); - scan_params.timeout = 0; - - ble_gap_addr_t addr; - memset(&addr, 0, sizeof(addr)); - - addr.addr_type = entry->address.type; - memcpy(addr.addr, entry->address.value, BLEIO_ADDRESS_BYTES); - - BLE_DRIVER_LOG("GAP CONNECTING: "HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT", type: %d\n", - addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2], addr.addr[1], addr.addr[0], addr.addr_type); - - ble_gap_conn_params_t conn_params = { - .min_conn_interval = BLE_MIN_CONN_INTERVAL, - .max_conn_interval = BLE_MAX_CONN_INTERVAL, - .conn_sup_timeout = BLE_CONN_SUP_TIMEOUT, - .slave_latency = BLE_SLAVE_LATENCY, - }; - - uint32_t err_code; - #if (BLE_API_VERSION == 2) - if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params)) != 0) { - #else - if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_DEFAULT)) != 0) { - #endif - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - "Can not connect. status: 0x" HEX2_FMT, (uint16_t)err_code)); + event_handler_t *it = m_event_handlers; + while (it->next != NULL) { + if ((it->func == func) && (it->param == param)) { + m_free(handler); + return; } - } -} - -void ble_drv_connect(bleio_device_obj_t *device) { - SD_TEST_OR_ENABLE(); - - mp_connect_address = &device->address; - ble_drv_adv_report_handler_set(NULL, ble_drv_connect_scan_callback); - - ble_drv_scan_start(100, 100); -} - -void ble_drv_disconnect(bleio_device_obj_t *device) { - sd_ble_gap_disconnect(device->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); -} - -bool ble_drv_discover_services(bleio_device_obj_t *device, uint16_t start_handle) { - mp_gattc_disc_service_observer = device; - - m_primary_service_found = false; - - uint32_t err_code = sd_ble_gattc_primary_services_discover(device->conn_handle, start_handle, NULL); - if (err_code != NRF_SUCCESS) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - "Failed to discover serivices. status: 0x" HEX2_FMT, (uint16_t)err_code)); - } - while (mp_gattc_disc_service_observer != NULL) { -#ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP -#endif + it = it->next; } - return m_primary_service_found; + it->next = handler; } -bool ble_drv_discover_characteristic(bleio_device_obj_t *device, bleio_service_obj_t *service, uint16_t start_handle) { - BLE_DRIVER_LOG("Discover characteristicts. Conn handle: 0x" HEX2_FMT "\n", device->conn_handle); - - mp_gattc_disc_char_observer = service; - - ble_gattc_handle_range_t handle_range; - handle_range.start_handle = start_handle; - handle_range.end_handle = service->end_handle; - - m_characteristic_found = false; - - uint32_t err_code = sd_ble_gattc_characteristics_discover(device->conn_handle, &handle_range); - if (err_code != 0) { - return false; - } - - while (mp_gattc_disc_char_observer != NULL) { -#ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP -#endif - } - - return m_characteristic_found; -} - -void ble_drv_discover_descriptors(void) { - -} - -STATIC void on_adv_report(ble_gap_evt_adv_report_t *report) { - bleio_scanentry_obj_t *entry = m_new_obj(bleio_scanentry_obj_t); - entry->base.type = &bleio_scanentry_type; - - entry->rssi = report->rssi; - - entry->address.type = report->peer_addr.addr_type; - memcpy(entry->address.value, report->peer_addr.addr, BLEIO_ADDRESS_BYTES); - -#if (BLUETOOTH_SD == 140) - entry->data = mp_obj_new_bytearray(report->data.len, report->data.p_data); -#else - entry->data = mp_obj_new_bytearray(report->dlen, report->data); -#endif - - if (adv_event_handler != NULL) { - adv_event_handler(mp_adv_observer, entry); - } -} - -STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response) { - BLE_DRIVER_LOG(">>> service count: %d\n", response->count); - - for (size_t i = 0; i < response->count; ++i) { - const ble_gattc_service_t *gattc_service = &response->services[i]; - - bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); - service->base.type = &bleio_service_type; - service->char_list = mp_obj_new_list(0, NULL); - service->start_handle = gattc_service->handle_range.start_handle; - service->end_handle = gattc_service->handle_range.end_handle; - service->handle = gattc_service->handle_range.start_handle; - service->device = mp_gattc_disc_service_observer; - - bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - uuid->type = (gattc_service->uuid.type == BLE_UUID_TYPE_BLE) ? UUID_TYPE_16BIT : UUID_TYPE_128BIT; - uuid->value[0] = gattc_service->uuid.uuid & 0xFF; - uuid->value[1] = gattc_service->uuid.uuid >> 8; - service->uuid = uuid; - - mp_obj_list_append(mp_gattc_disc_service_observer->service_list, service); - } - - if (response->count > 0) { - m_primary_service_found = true; - } - - // mark end of service discovery - mp_gattc_disc_service_observer = NULL; -} - -STATIC void on_characteristic_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response) { - BLE_DRIVER_LOG(">>> characteristic count: %d\n", response->count); - - for (size_t i = 0; i < response->count; ++i) { - const ble_gattc_char_t *gattc_char = &response->chars[i]; - - bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); - characteristic->base.type = &bleio_characteristic_type; - - bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - uuid->type = (gattc_char->uuid.type == BLE_UUID_TYPE_BLE) ? UUID_TYPE_16BIT : UUID_TYPE_128BIT; - uuid->value[0] = gattc_char->uuid.uuid & 0xFF; - uuid->value[1] = gattc_char->uuid.uuid >> 8; - characteristic->uuid = uuid; - - characteristic->props.broadcast = gattc_char->char_props.broadcast; - characteristic->props.indicate = gattc_char->char_props.indicate; - characteristic->props.notify = gattc_char->char_props.notify; - characteristic->props.read = gattc_char->char_props.read; - characteristic->props.write = gattc_char->char_props.write; - characteristic->props.write_wo_resp = gattc_char->char_props.write_wo_resp; - characteristic->handle = gattc_char->handle_value; - - characteristic->service_handle = mp_gattc_disc_char_observer->handle; - characteristic->service = mp_gattc_disc_char_observer; - - mp_obj_list_append(mp_gattc_disc_char_observer->char_list, MP_OBJ_FROM_PTR(characteristic)); - } - - if (response->count > 0) { - m_characteristic_found = true; +void SD_EVT_IRQHandler(void) { + uint32_t evt_id; + while (sd_evt_get(&evt_id) != NRF_ERROR_NOT_FOUND) { +// sd_evt_handler(evt_id); } - // mark end of characteristic discovery - mp_gattc_disc_char_observer = NULL; -} - -STATIC void on_read_rsp(ble_gattc_evt_read_rsp_t *response) { - mp_gattc_char_data_observer->value_data = mp_obj_new_bytearray(response->len, response->data); - - mp_gattc_char_data_observer = NULL; -} - -STATIC void ble_evt_handler(ble_evt_t *p_ble_evt) { - printf("%s - 0x%02X\r\n", __func__, p_ble_evt->header.evt_id); - - switch (p_ble_evt->header.evt_id) { - case BLE_GAP_EVT_CONNECTED: - BLE_DRIVER_LOG("GAP CONNECT\n"); - gap_event_handler(mp_gap_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL); - - ble_gap_conn_params_t conn_params; - (void)sd_ble_gap_ppcp_get(&conn_params); - (void)sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, &conn_params); - break; - - case BLE_GAP_EVT_DISCONNECTED: - BLE_DRIVER_LOG("GAP DISCONNECT\n"); - gap_event_handler(mp_gap_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL); - break; - - case BLE_GATTS_EVT_HVC: - gatts_event_handler(mp_gatts_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gatts_evt.params.hvc.handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL); - break; - - case BLE_GATTS_EVT_WRITE: - BLE_DRIVER_LOG("GATTS write\n"); - - uint16_t handle = p_ble_evt->evt.gatts_evt.params.write.handle; - uint16_t data_len = p_ble_evt->evt.gatts_evt.params.write.len; - uint8_t * p_data = &p_ble_evt->evt.gatts_evt.params.write.data[0]; - - gatts_event_handler(mp_gatts_observer, p_ble_evt->header.evt_id, handle, data_len, p_data); - break; - - case BLE_GAP_EVT_CONN_PARAM_UPDATE: - BLE_DRIVER_LOG("GAP CONN PARAM UPDATE\n"); - break; - - case BLE_GATTS_EVT_SYS_ATTR_MISSING: - // No system attributes have been stored. - (void)sd_ble_gatts_sys_attr_set(p_ble_evt->evt.gatts_evt.conn_handle, NULL, 0, 0); - break; - -#if (BLE_API_VERSION == 4) - case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: - BLE_DRIVER_LOG("GATTS EVT EXCHANGE MTU REQUEST\n"); - (void)sd_ble_gatts_exchange_mtu_reply(p_ble_evt->evt.gatts_evt.conn_handle, 23); // MAX MTU size - break; -#endif - -#if (BLE_API_VERSION == 4) - case BLE_GATTS_EVT_HVN_TX_COMPLETE: -#else - case BLE_EVT_TX_COMPLETE: -#endif - BLE_DRIVER_LOG("BLE EVT TX COMPLETE\n"); - m_tx_in_progress = false; - break; - - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: - BLE_DRIVER_LOG("BLE EVT SEC PARAMS REQUEST\n"); - // pairing not supported - (void)sd_ble_gap_sec_params_reply(p_ble_evt->evt.gatts_evt.conn_handle, - BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, - NULL, NULL); - break; - - case BLE_GAP_EVT_ADV_REPORT: - on_adv_report(&p_ble_evt->evt.gap_evt.params.adv_report); - break; - - case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: - BLE_DRIVER_LOG("BLE EVT CONN PARAM UPDATE REQUEST\n"); - - (void)sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, - &p_ble_evt->evt.gap_evt.params.conn_param_update_request.conn_params); - break; - - case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: - on_primary_srv_discovery_rsp(&p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp); - break; - - case BLE_GATTC_EVT_CHAR_DISC_RSP: - on_characteristic_discovery_rsp(&p_ble_evt->evt.gattc_evt.params.char_disc_rsp); - break; - - case BLE_GATTC_EVT_READ_RSP: - on_read_rsp(&p_ble_evt->evt.gattc_evt.params.read_rsp); - break; - - case BLE_GATTC_EVT_WRITE_RSP: - BLE_DRIVER_LOG("BLE EVT WRITE RESPONSE\n"); - m_write_done = true; - break; + while (1) { + uint16_t evt_len = sizeof(m_ble_evt_buf); + const uint32_t err_code = sd_ble_evt_get(m_ble_evt_buf, &evt_len); + if (err_code != NRF_SUCCESS) { + if (err_code == NRF_ERROR_DATA_SIZE) { + printf("NRF_ERROR_DATA_SIZE\n"); + } - case BLE_GATTC_EVT_HVX: - BLE_DRIVER_LOG("BLE EVT HVX RESPONSE\n"); break; + } - default: - BLE_DRIVER_LOG(">>> unhandled evt: 0x" HEX2_FMT "\n", p_ble_evt->header.evt_id); - break; + event_handler_t *it = m_event_handlers; + while (it != NULL) { + it->func((ble_evt_t *)m_ble_evt_buf, it->param); + it = it->next; + } } } - -#if (BLE_API_VERSION == 2) -static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (GATT_MTU_SIZE_DEFAULT)] __attribute__ ((aligned (4))); -#else -static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (BLE_GATT_ATT_MTU_DEFAULT)] __attribute__ ((aligned (4))); -#endif - -void SD_EVT_IRQHandler(void) { - uint32_t evt_id; - uint32_t err_code; - do { - err_code = sd_evt_get(&evt_id); - // TODO: handle non ble events - } while (err_code != NRF_ERROR_NOT_FOUND && err_code != NRF_SUCCESS); - - uint16_t evt_len = sizeof(m_ble_evt_buf); - do { - err_code = sd_ble_evt_get(m_ble_evt_buf, &evt_len); - ble_evt_handler((ble_evt_t *)m_ble_evt_buf); - } while (err_code != NRF_ERROR_NOT_FOUND && err_code != NRF_SUCCESS); -} - -#endif // BLUETOOTH_SD diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h index 3b065c7a9..426354a19 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.h +++ b/ports/nrf/drivers/bluetooth/ble_drv.h @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2016 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,65 +28,26 @@ #ifndef BLUETOOTH_LE_DRIVER_H__ #define BLUETOOTH_LE_DRIVER_H__ -#if BLUETOOTH_SD +#include "ble.h" -#include "shared-bindings/bleio/Device.h" -#include "shared-bindings/bleio/Scanner.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-module/bleio/ScanEntry.h" +#if (BLUETOOTH_SD == 132) && (BLE_API_VERSION == 2) +#define NRF52 +#endif -typedef void (*ble_drv_gap_evt_callback_t)(bleio_device_obj_t *device, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data); -typedef void (*ble_drv_gatts_evt_callback_t)(bleio_device_obj_t *device, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); -typedef void (*ble_drv_adv_evt_callback_t)(bleio_scanner_obj_t *scanner, bleio_scanentry_obj_t *entry); +#define MAX_TX_IN_PROGRESS 10 -uint32_t ble_drv_stack_enable(void); +#ifndef BLE_GATT_ATT_MTU_DEFAULT + #define BLE_GATT_ATT_MTU_DEFAULT GATT_MTU_SIZE_DEFAULT +#endif -uint8_t ble_drv_stack_enabled(void); +#define BLE_CONN_CFG_TAG_CUSTOM 1 -void ble_drv_address_get(bleio_address_obj_t *address); +#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) +#define UNIT_0_625_MS (625) +#define UNIT_10_MS (10000) -bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx); +typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*); -void ble_drv_service_add(bleio_service_obj_t *service); - -bool ble_drv_characteristic_add(bleio_characteristic_obj_t *characteristic); - -bool ble_drv_advertise_data(bleio_advertisement_data_t *p_adv_params); - -void ble_drv_advertise_stop(void); - -void ble_drv_gap_event_handler_set(bleio_device_obj_t *device, ble_drv_gap_evt_callback_t evt_handler); - -void ble_drv_gatts_event_handler_set(bleio_device_obj_t *device, ble_drv_gatts_evt_callback_t evt_handler); - -void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); - -void ble_drv_attr_c_read(bleio_characteristic_obj_t *characteristic); - -void ble_drv_attr_s_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo); - -void ble_drv_attr_s_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo); - -void ble_drv_attr_c_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo); - -void ble_drv_scan_start(uint16_t interval, uint16_t window); - -void ble_drv_scan_continue(void); - -void ble_drv_scan_stop(void); - -void ble_drv_adv_report_handler_set(bleio_scanner_obj_t *self, ble_drv_adv_evt_callback_t evt_handler); - -void ble_drv_connect(bleio_device_obj_t *device); - -void ble_drv_disconnect(bleio_device_obj_t *device); - -bool ble_drv_discover_services(bleio_device_obj_t *device, uint16_t start_handle); - -bool ble_drv_discover_characteristic(bleio_device_obj_t *device, bleio_service_obj_t *service, uint16_t start_handle); - -void ble_drv_discover_descriptors(void); - -#endif // BLUETOOTH_SD +void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param); #endif // BLUETOOTH_LE_DRIVER_H__ diff --git a/ports/nrf/drivers/bluetooth/ble_uart.h b/ports/nrf/drivers/bluetooth/ble_uart.h index 336624cd3..b57a75229 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.h +++ b/ports/nrf/drivers/bluetooth/ble_uart.h @@ -27,7 +27,7 @@ #ifndef BLUETOOTH_LE_UART_H__ #define BLUETOOTH_LE_UART_H__ -#if BLUETOOTH_SD +#include #include "ble_drv.h" @@ -36,6 +36,4 @@ void ble_uart_advertise(void); bool ble_uart_connected(void); bool ble_uart_enabled(void); -#endif // BLUETOOTH_SD - #endif // BLUETOOTH_LE_UART_H__ diff --git a/shared-bindings/bleio/Characteristic.c b/shared-bindings/bleio/Characteristic.c index 905babecb..3563d3895 100644 --- a/shared-bindings/bleio/Characteristic.c +++ b/shared-bindings/bleio/Characteristic.c @@ -125,6 +125,8 @@ STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t "Invalid UUID parameter")); } + common_hal_bleio_characteristic_construct(self); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/bleio/Characteristic.h b/shared-bindings/bleio/Characteristic.h index dd67170d9..0e99e97cc 100644 --- a/shared-bindings/bleio/Characteristic.h +++ b/shared-bindings/bleio/Characteristic.h @@ -31,6 +31,7 @@ extern const mp_obj_type_t bleio_characteristic_type; +extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self); extern void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self); extern void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); diff --git a/shared-bindings/bleio/Device.c b/shared-bindings/bleio/Device.c index 386261683..8c7cef95f 100644 --- a/shared-bindings/bleio/Device.c +++ b/shared-bindings/bleio/Device.c @@ -199,7 +199,7 @@ STATIC mp_obj_t bleio_device_add_service(mp_obj_t self_in, mp_obj_t service_in) "Can't add services in Central mode")); } - service->device = self_in; + service->device = self; mp_obj_list_append(self->service_list, service); @@ -266,31 +266,21 @@ STATIC mp_obj_t bleio_device_start_advertising(mp_uint_t n_args, const mp_obj_t "Can't advertise in Central mode")); } - enum { ARG_connectable }; + enum { ARG_connectable, ARG_data }; static const mp_arg_t allowed_args[] = { { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - // TODO: data - bleio_advertisement_data_t adv_data = { - .device_name = self->name, - .services = mp_obj_new_list(0, NULL), - .data = mp_obj_new_bytearray(0, NULL), - .connectable = args[ARG_connectable].u_bool - }; - - mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); - for (size_t i = 0; i < service_list->len; ++i) { - bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); - if (!service->is_secondary) { - mp_obj_list_append(adv_data.services, service_list->items[i]); - } + mp_buffer_info_t bufinfo = { 0 }; + if (args[ARG_data].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); } - common_hal_bleio_device_start_advertising(self, &adv_data); + common_hal_bleio_device_start_advertising(self, args[ARG_connectable].u_bool, &bufinfo); return mp_const_none; } diff --git a/shared-bindings/bleio/Device.h b/shared-bindings/bleio/Device.h index d099e5c43..f989736dd 100644 --- a/shared-bindings/bleio/Device.h +++ b/shared-bindings/bleio/Device.h @@ -32,7 +32,7 @@ extern const mp_obj_type_t bleio_device_type; -extern void common_hal_bleio_device_start_advertising(bleio_device_obj_t *device, bleio_advertisement_data_t *adv_data); +extern void common_hal_bleio_device_start_advertising(bleio_device_obj_t *device, bool connectable, mp_buffer_info_t *raw_data); extern void common_hal_bleio_device_stop_advertising(bleio_device_obj_t *device); extern void common_hal_bleio_device_connect(bleio_device_obj_t *device); extern void common_hal_bleio_device_disconnect(bleio_device_obj_t *device); diff --git a/shared-bindings/bleio/Scanner.h b/shared-bindings/bleio/Scanner.h index 03db5cd8b..9bd071747 100644 --- a/shared-bindings/bleio/Scanner.h +++ b/shared-bindings/bleio/Scanner.h @@ -34,5 +34,6 @@ extern const mp_obj_type_t bleio_scanner_type; extern void common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_int_t timeout); +extern void common_hal_bleio_scanner_stop(bleio_scanner_obj_t *self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c index 338269a5f..a2f9b9899 100644 --- a/shared-bindings/bleio/Service.c +++ b/shared-bindings/bleio/Service.c @@ -74,7 +74,7 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_check_num(n_args, n_kw, 1, 1, true); bleio_service_obj_t *self = m_new_obj(bleio_service_obj_t); self->base.type = &bleio_service_type; - self->device = mp_const_none; + self->device = NULL; self->char_list = mp_obj_new_list(0, NULL); mp_map_t kw_args; diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h index 2347c9c30..13f75b2c9 100644 --- a/shared-module/bleio/Characteristic.h +++ b/shared-module/bleio/Characteristic.h @@ -33,7 +33,7 @@ typedef struct { mp_obj_base_t base; bleio_service_obj_t *service; - uint16_t service_handle; + uint16_t service_handle; // TODO: Is this needed? bleio_uuid_obj_t *uuid; mp_obj_t value_data; uint16_t handle; diff --git a/shared-module/bleio/Device.h b/shared-module/bleio/Device.h index afbd8f063..8d9ece5ef 100644 --- a/shared-module/bleio/Device.h +++ b/shared-module/bleio/Device.h @@ -36,7 +36,7 @@ typedef struct { bool is_peripheral; mp_obj_t name; bleio_address_obj_t address; - uint16_t conn_handle; + volatile uint16_t conn_handle; mp_obj_t service_list; mp_obj_t notif_handler; mp_obj_t conn_handler; diff --git a/shared-module/bleio/Service.h b/shared-module/bleio/Service.h index 1e3f09119..ff506d3f3 100644 --- a/shared-module/bleio/Service.h +++ b/shared-module/bleio/Service.h @@ -28,13 +28,14 @@ #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SERVICE_H #include "common-hal/bleio/UUID.h" +#include "shared-module/bleio/Device.h" typedef struct { mp_obj_base_t base; uint16_t handle; bool is_secondary; bleio_uuid_obj_t *uuid; - mp_obj_t device; + bleio_device_obj_t *device; mp_obj_t char_list; uint16_t start_handle; uint16_t end_handle; -- cgit v1.2.3 From b5e5805bb408ca3bb5975d8fad21070dbf4d51e9 Mon Sep 17 00:00:00 2001 From: arturo182 Date: Mon, 23 Jul 2018 23:52:09 +0200 Subject: bleio: Remove redundant struct field --- ports/nrf/common-hal/bleio/Device.c | 2 -- ports/nrf/common-hal/bleio/Service.c | 3 +-- shared-bindings/bleio/Service.c | 4 ++-- shared-module/bleio/Characteristic.h | 1 - 4 files changed, 3 insertions(+), 7 deletions(-) (limited to 'shared-module') diff --git a/ports/nrf/common-hal/bleio/Device.c b/ports/nrf/common-hal/bleio/Device.c index 8e52b2fbc..fd772c04d 100644 --- a/ports/nrf/common-hal/bleio/Device.c +++ b/ports/nrf/common-hal/bleio/Device.c @@ -376,8 +376,6 @@ STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio characteristic->props.write = gattc_char->char_props.write; characteristic->props.write_wo_resp = gattc_char->char_props.write_wo_resp; characteristic->handle = gattc_char->handle_value; - - characteristic->service_handle = m_char_discovery_service->handle; characteristic->service = m_char_discovery_service; mp_obj_list_append(m_char_discovery_service->char_list, MP_OBJ_FROM_PTR(characteristic)); diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c index 9e376b333..93588d156 100644 --- a/ports/nrf/common-hal/bleio/Service.c +++ b/ports/nrf/common-hal/bleio/Service.c @@ -101,7 +101,7 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, blei ble_gatts_char_handles_t handles; uint32_t err_code; - err_code = sd_ble_gatts_characteristic_add(characteristic->service_handle, &char_md, &attr_char_value, &handles); + err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &attr_char_value, &handles); if (err_code != NRF_SUCCESS) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Failed to add characteristic, status: 0x%08lX", err_code)); @@ -111,5 +111,4 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, blei characteristic->cccd_handle = handles.cccd_handle; characteristic->sccd_handle = handles.sccd_handle; characteristic->handle = handles.value_handle; - characteristic->service = self; } diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c index 3a4aa4737..94d245d7f 100644 --- a/shared-bindings/bleio/Service.c +++ b/shared-bindings/bleio/Service.c @@ -113,12 +113,12 @@ STATIC mp_obj_t bleio_service_add_characteristic(mp_obj_t self_in, mp_obj_t char bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(characteristic_in); - characteristic->service_handle = self->handle; - // TODO: If service is 128b then update Chara UUID to be 128b too common_hal_bleio_service_add_characteristic(self, characteristic); + characteristic->service = self; + mp_obj_list_append(self->char_list, characteristic); return mp_const_none; diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h index 13f75b2c9..94c43f81e 100644 --- a/shared-module/bleio/Characteristic.h +++ b/shared-module/bleio/Characteristic.h @@ -33,7 +33,6 @@ typedef struct { mp_obj_base_t base; bleio_service_obj_t *service; - uint16_t service_handle; // TODO: Is this needed? bleio_uuid_obj_t *uuid; mp_obj_t value_data; uint16_t handle; -- cgit v1.2.3 From b714f5d650b53aff9fc5bc4efe1bf43eddfc7c24 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 25 Oct 2018 11:29:27 +1100 Subject: Add "dhcp" property to turn DHCP on and off --- shared-bindings/wiznet/wiznet5k.c | 41 ++++++++++++++++++++++++++++++++++----- shared-module/wiznet/wiznet5k.c | 10 ++++++---- shared-module/wiznet/wiznet5k.h | 4 ++++ 3 files changed, 46 insertions(+), 9 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/wiznet/wiznet5k.c b/shared-bindings/wiznet/wiznet5k.c index e1076bc15..4e5fff869 100644 --- a/shared-bindings/wiznet/wiznet5k.c +++ b/shared-bindings/wiznet/wiznet5k.c @@ -66,17 +66,17 @@ STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size return wiznet5k_create(args[0], args[1], args[2]); } +//| .. attribute:: connected +//| +//| is this device physically connected? +//| + STATIC mp_obj_t wiznet5k_connected_get_value(mp_obj_t self_in) { (void)self_in; return mp_obj_new_bool(wizphy_getphylink() == PHY_LINK_ON); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_connected_get_value_obj, wiznet5k_connected_get_value); -//| .. attribute:: connected -//| -//| is this device physically connected? -//| - const mp_obj_property_t wiznet5k_connected_obj = { .base.type = &mp_type_property, .proxy = {(mp_obj_t)&wiznet5k_connected_get_value_obj, @@ -84,6 +84,36 @@ const mp_obj_property_t wiznet5k_connected_obj = { (mp_obj_t)&mp_const_none_obj}, }; +//| .. attribute:: dhcp +//| +//| is DHCP active on this device? (set to true to activate DHCP, false to turn it off) +//| + +STATIC mp_obj_t wiznet5k_dhcp_get_value(mp_obj_t self_in) { + (void)self_in; + return mp_obj_new_bool(wiznet5k_check_dhcp()); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_dhcp_get_value_obj, wiznet5k_dhcp_get_value); + +STATIC mp_obj_t wiznet5k_dhcp_set_value(mp_obj_t self_in, mp_obj_t value) { + (void)self_in; + if (mp_obj_is_true(value)) { + wiznet5k_start_dhcp(); + } else { + wiznet5k_stop_dhcp(); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(wiznet5k_dhcp_set_value_obj, wiznet5k_dhcp_set_value); + +const mp_obj_property_t wiznet5k_dhcp_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&wiznet5k_dhcp_get_value_obj, + (mp_obj_t)&wiznet5k_dhcp_set_value_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + //| .. method:: ifconfig(...) //| //| Called without parameters, returns a tuple of @@ -121,6 +151,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k STATIC const mp_rom_map_elem_t wiznet5k_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wiznet5k_ifconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&wiznet5k_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_dhcp), MP_ROM_PTR(&wiznet5k_dhcp_obj) }, }; STATIC MP_DEFINE_CONST_DICT(wiznet5k_locals_dict, wiznet5k_locals_dict_table); diff --git a/shared-module/wiznet/wiznet5k.c b/shared-module/wiznet/wiznet5k.c index 4bf536226..ee732859a 100644 --- a/shared-module/wiznet/wiznet5k.c +++ b/shared-module/wiznet/wiznet5k.c @@ -324,7 +324,7 @@ void wiznet5k_socket_timer_tick(mod_network_socket_obj_t *socket) { } } -static void wiznet5k_start_dhcp(void) { +void wiznet5k_start_dhcp(void) { static DHCP_INIT_BUFFER_TYPE dhcp_buf[DHCP_INIT_BUFFER_SIZE]; if (!wiznet5k_obj.dhcp_active) { @@ -335,15 +335,17 @@ static void wiznet5k_start_dhcp(void) { } } -#if 0 -static void wiznet5k_stop_dhcp(void) { +void wiznet5k_stop_dhcp(void) { if (wiznet5k_obj.dhcp_active) { wiznet5k_obj.dhcp_active = 0; DHCP_stop(); WIZCHIP_EXPORT(close)(0); } } -#endif + +bool wiznet5k_check_dhcp(void) { + return wiznet5k_obj.dhcp_active; +} /// Create and return a WIZNET5K object. mp_obj_t wiznet5k_create(mp_obj_t spi_in, mp_obj_t cs_in, mp_obj_t rst_in) { diff --git a/shared-module/wiznet/wiznet5k.h b/shared-module/wiznet/wiznet5k.h index 04b8872fb..1284a44fd 100644 --- a/shared-module/wiznet/wiznet5k.h +++ b/shared-module/wiznet/wiznet5k.h @@ -60,6 +60,10 @@ void wiznet5k_socket_timer_tick(mod_network_socket_obj_t *socket); mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in); mp_obj_t wiznet5k_create(mp_obj_t spi_in, mp_obj_t cs_in, mp_obj_t rst_in); +void wiznet5k_start_dhcp(void); +void wiznet5k_stop_dhcp(void); +bool wiznet5k_check_dhcp(void); + extern const mod_network_nic_type_t mod_network_nic_type_wiznet5k; #endif // MICROPY_INCLUDED_SHARED_MODULE_WIZNET_WIZNET5K_H -- cgit v1.2.3 From 46f1a0719e5d24d0a53e64a01a667610c6c57086 Mon Sep 17 00:00:00 2001 From: caternuson Date: Wed, 31 Oct 2018 18:08:10 -0700 Subject: add channels and bits_per_sample to audioio.WaveFile --- shared-bindings/audioio/WaveFile.c | 39 ++++++++++++++++++++++++++++++++++++++ shared-bindings/audioio/WaveFile.h | 2 ++ shared-module/audioio/WaveFile.c | 8 ++++++++ 3 files changed, 49 insertions(+) (limited to 'shared-module') diff --git a/shared-bindings/audioio/WaveFile.c b/shared-bindings/audioio/WaveFile.c index 4ff0abdda..a3fc8017b 100644 --- a/shared-bindings/audioio/WaveFile.c +++ b/shared-bindings/audioio/WaveFile.c @@ -138,6 +138,43 @@ const mp_obj_property_t audioio_wavefile_sample_rate_obj = { (mp_obj_t)&mp_const_none_obj}, }; +//| .. attribute:: bits_per_sample +//| +//| Bits per sample. (read only) +//| +STATIC mp_obj_t audioio_wavefile_obj_get_bits_per_sample(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_wavefile_deinited(self)); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_wavefile_get_bits_per_sample(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_get_bits_per_sample_obj, audioio_wavefile_obj_get_bits_per_sample); + +const mp_obj_property_t audioio_wavefile_bits_per_sample_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_wavefile_get_bits_per_sample_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: channels +//| +//| Number of audio channels. (read only) +//| +STATIC mp_obj_t audioio_wavefile_obj_get_channel_count(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_wavefile_deinited(self)); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_wavefile_get_channel_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_get_channel_count_obj, audioio_wavefile_obj_get_channel_count); + +const mp_obj_property_t audioio_wavefile_channel_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_wavefile_get_channel_count_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + STATIC const mp_rom_map_elem_t audioio_wavefile_locals_dict_table[] = { // Methods { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_wavefile_deinit_obj) }, @@ -146,6 +183,8 @@ STATIC const mp_rom_map_elem_t audioio_wavefile_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_wavefile_sample_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_bits_per_sample), MP_ROM_PTR(&audioio_wavefile_bits_per_sample_obj) }, + { MP_ROM_QSTR(MP_QSTR_channels), MP_ROM_PTR(&audioio_wavefile_channel_count_obj) }, }; STATIC MP_DEFINE_CONST_DICT(audioio_wavefile_locals_dict, audioio_wavefile_locals_dict_table); diff --git a/shared-bindings/audioio/WaveFile.h b/shared-bindings/audioio/WaveFile.h index 255ffbcd5..62a4200dc 100644 --- a/shared-bindings/audioio/WaveFile.h +++ b/shared-bindings/audioio/WaveFile.h @@ -40,5 +40,7 @@ void common_hal_audioio_wavefile_deinit(audioio_wavefile_obj_t* self); bool common_hal_audioio_wavefile_deinited(audioio_wavefile_obj_t* self); uint32_t common_hal_audioio_wavefile_get_sample_rate(audioio_wavefile_obj_t* self); void common_hal_audioio_wavefile_set_sample_rate(audioio_wavefile_obj_t* self, uint32_t sample_rate); +uint8_t common_hal_audioio_wavefile_get_bits_per_sample(audioio_wavefile_obj_t* self); +uint8_t common_hal_audioio_wavefile_get_channel_count(audioio_wavefile_obj_t* self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_WAVEFILE_H diff --git a/shared-module/audioio/WaveFile.c b/shared-module/audioio/WaveFile.c index e5a7b1a3b..d5dd9419c 100644 --- a/shared-module/audioio/WaveFile.c +++ b/shared-module/audioio/WaveFile.c @@ -141,6 +141,14 @@ void common_hal_audioio_wavefile_set_sample_rate(audioio_wavefile_obj_t* self, self->sample_rate = sample_rate; } +uint8_t common_hal_audioio_wavefile_get_bits_per_sample(audioio_wavefile_obj_t* self) { + return self->bits_per_sample; +} + +uint8_t common_hal_audioio_wavefile_get_channel_count(audioio_wavefile_obj_t* self) { + return self->channel_count; +} + bool audioio_wavefile_samples_signed(audioio_wavefile_obj_t* self) { return self->bits_per_sample > 8; } -- cgit v1.2.3 From 9d91111b1b6a1eee77a88b13faf695ee0c5caea3 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 19 Oct 2018 18:46:22 -0700 Subject: Move atmel-samd to tinyusb and support nRF flash. This started while adding USB MIDI support (and descriptor support is in this change.) When seeing that I'd have to implement the MIDI class logic twice, once for atmel-samd and once for nrf, I decided to refactor the USB stack so its shared across ports. This has led to a number of changes that remove items from the ports folder and move them into supervisor. Furthermore, we had external SPI flash support for nrf pending so I factored out the connection between the usb stack and the flash API as well. This PR also includes the QSPI support for nRF. --- .gitmodules | 2 +- lib/tinyusb | 2 +- main.c | 3 + ports/atmel-samd/Makefile | 72 +-- ports/atmel-samd/background.c | 7 +- ports/atmel-samd/boards/arduino_zero/board.c | 7 - .../atmel-samd/boards/arduino_zero/mpconfigboard.h | 8 +- .../circuitplayground_express/mpconfigboard.h | 39 +- .../circuitplayground_express/mpconfigboard.mk | 2 + .../mpconfigboard.h | 40 +- .../mpconfigboard.mk | 2 + .../boards/feather_m0_adalogger/mpconfigboard.h | 4 +- .../boards/feather_m0_basic/mpconfigboard.h | 4 +- .../boards/feather_m0_express/mpconfigboard.h | 37 +- .../boards/feather_m0_express/mpconfigboard.mk | 2 + .../feather_m0_express_crickit/mpconfigboard.h | 38 +- .../feather_m0_express_crickit/mpconfigboard.mk | 2 + .../boards/feather_m0_rfm69/mpconfigboard.h | 4 +- .../boards/feather_m0_rfm9x/mpconfigboard.h | 4 +- .../boards/feather_m0_supersized/mpconfigboard.h | 37 +- .../boards/feather_m0_supersized/mpconfigboard.mk | 2 + .../boards/feather_m4_express/mpconfigboard.h | 7 - .../boards/feather_m4_express/mpconfigboard.mk | 3 + .../feather_radiofruit_zigbee/mpconfigboard.h | 38 +- .../feather_radiofruit_zigbee/mpconfigboard.mk | 2 + ports/atmel-samd/boards/gemma_m0/mpconfigboard.h | 2 - .../boards/grandcentral_m4_express/board.c | 7 - .../boards/grandcentral_m4_express/mpconfigboard.h | 11 +- .../grandcentral_m4_express/mpconfigboard.mk | 3 + .../boards/hallowing_m0_express/mpconfigboard.h | 41 +- .../boards/hallowing_m0_express/mpconfigboard.mk | 2 + .../boards/itsybitsy_m0_express/mpconfigboard.h | 38 +- .../boards/itsybitsy_m0_express/mpconfigboard.mk | 2 + .../boards/itsybitsy_m4_express/mpconfigboard.h | 7 - .../boards/itsybitsy_m4_express/mpconfigboard.mk | 2 + ports/atmel-samd/boards/meowmeow/mpconfigboard.h | 6 +- .../boards/metro_m0_express/mpconfigboard.h | 39 +- .../boards/metro_m0_express/mpconfigboard.mk | 4 + ports/atmel-samd/boards/metro_m4_express/board.c | 7 - .../boards/metro_m4_express/mpconfigboard.h | 19 +- .../boards/metro_m4_express/mpconfigboard.mk | 3 + ports/atmel-samd/boards/pirkey_m0/mpconfigboard.h | 2 - .../boards/trellis_m4_express/mpconfigboard.h | 7 - .../boards/trellis_m4_express/mpconfigboard.mk | 2 + ports/atmel-samd/boards/trinket_m0/mpconfigboard.h | 2 - .../boards/trinket_m0_haxpress/mpconfigboard.h | 42 +- .../boards/trinket_m0_haxpress/mpconfigboard.mk | 2 + ports/atmel-samd/boards/ugame10/mpconfigboard.h | 39 +- ports/atmel-samd/boards/ugame10/mpconfigboard.mk | 2 + ports/atmel-samd/common-hal/busio/SPI.c | 47 ++ ports/atmel-samd/common-hal/busio/SPI.h | 3 + ports/atmel-samd/common-hal/microcontroller/Pin.c | 15 +- ports/atmel-samd/common-hal/microcontroller/Pin.h | 1 + .../common-hal/microcontroller/__init__.c | 220 +++++-- ports/atmel-samd/common-hal/storage/__init__.c | 58 -- ports/atmel-samd/common-hal/supervisor/Runtime.c | 7 +- ports/atmel-samd/common-hal/usb_hid/Device.c | 106 ---- ports/atmel-samd/common-hal/usb_hid/Device.h | 53 -- ports/atmel-samd/common-hal/usb_hid/__init__.c | 152 ----- ports/atmel-samd/external_flash/common_commands.h | 46 -- ports/atmel-samd/external_flash/devices.h | 337 ---------- ports/atmel-samd/external_flash/external_flash.c | 699 --------------------- ports/atmel-samd/external_flash/external_flash.h | 64 -- ports/atmel-samd/external_flash/qspi_flash.c | 242 ------- ports/atmel-samd/external_flash/spi_flash.c | 158 ----- ports/atmel-samd/external_flash/spi_flash_api.h | 45 -- ports/atmel-samd/flash_api.c | 51 -- ports/atmel-samd/flash_api.h | 36 -- ports/atmel-samd/internal_flash.c | 285 --------- ports/atmel-samd/internal_flash.h | 69 -- ports/atmel-samd/mpconfigport.h | 10 +- ports/atmel-samd/mpconfigport.mk | 2 +- ports/atmel-samd/mphalport.c | 34 - ports/atmel-samd/mphalport.h | 2 - ports/atmel-samd/peripherals | 2 +- ports/atmel-samd/reset.c | 4 - ports/atmel-samd/supervisor/filesystem.c | 101 --- ports/atmel-samd/supervisor/internal_flash.c | 209 ++++++ ports/atmel-samd/supervisor/internal_flash.h | 69 ++ .../supervisor/internal_flash_root_pointers.h | 31 + ports/atmel-samd/supervisor/port.c | 39 +- ports/atmel-samd/supervisor/qspi_flash.c | 232 +++++++ ports/atmel-samd/supervisor/serial.c | 79 --- ports/atmel-samd/supervisor/usb.c | 59 ++ ports/atmel-samd/tick.h | 2 +- ports/atmel-samd/tools/gen_usb_descriptor.py | 353 ----------- ports/atmel-samd/tools/hid_report_descriptors.py | 239 ------- ports/atmel-samd/usb.c | 326 ---------- ports/atmel-samd/usb.h | 42 -- ports/atmel-samd/usb_mass_storage.c | 338 ---------- ports/atmel-samd/usb_mass_storage.h | 48 -- ports/esp8266/Makefile | 2 - ports/esp8266/mpconfigport.h | 2 - ports/nrf/Makefile | 48 +- ports/nrf/README.md | 2 +- ports/nrf/background.c | 11 +- ports/nrf/board_busses.c | 2 +- ports/nrf/boards/feather_nrf52840_express/board.c | 2 - .../feather_nrf52840_express/mpconfigboard.h | 23 +- .../feather_nrf52840_express/mpconfigboard.mk | 9 + ports/nrf/boards/makerdiary_nrf52840_mdk/board.c | 2 - .../boards/makerdiary_nrf52840_mdk/mpconfigboard.h | 25 +- .../makerdiary_nrf52840_mdk/mpconfigboard.mk | 9 + ports/nrf/boards/pca10056/board.c | 2 - ports/nrf/boards/pca10056/mpconfigboard.h | 23 + ports/nrf/boards/pca10056/mpconfigboard.mk | 9 + ports/nrf/boards/pca10059/board.c | 5 - ports/nrf/boards/pca10059/mpconfigboard.mk | 5 + ports/nrf/common-hal/busio/SPI.c | 18 + ports/nrf/common-hal/busio/UART.c | 2 +- ports/nrf/common-hal/digitalio/DigitalInOut.c | 5 + ports/nrf/common-hal/microcontroller/Pin.c | 13 +- ports/nrf/common-hal/microcontroller/Pin.h | 1 + ports/nrf/common-hal/microcontroller/Processor.c | 7 +- ports/nrf/common-hal/pulseio/PulseIn.c | 2 +- ports/nrf/common-hal/pulseio/PulseOut.c | 2 +- ports/nrf/common-hal/storage/__init__.c | 47 -- ports/nrf/common-hal/usb_hid/Device.c | 88 --- ports/nrf/common-hal/usb_hid/Device.h | 94 --- ports/nrf/common-hal/usb_hid/__init__.c | 160 ----- ports/nrf/internal_flash.c | 226 ------- ports/nrf/internal_flash.h | 59 -- ports/nrf/mpconfigport.h | 5 +- ports/nrf/mpconfigport.mk | 1 + ports/nrf/mphalport.c | 81 --- ports/nrf/nrfx_config.h | 3 + ports/nrf/peripherals/nrf/timers.c | 2 +- ports/nrf/supervisor/filesystem.c | 97 --- ports/nrf/supervisor/internal_flash.c | 121 ++++ ports/nrf/supervisor/internal_flash.h | 42 ++ .../nrf/supervisor/internal_flash_root_pointers.h | 31 + ports/nrf/supervisor/port.c | 7 + ports/nrf/supervisor/qspi_flash.c | 146 +++++ ports/nrf/supervisor/serial.c | 48 +- ports/nrf/supervisor/usb.c | 83 +++ ports/nrf/tick.h | 2 +- ports/nrf/usb/tusb_config.h | 129 ---- ports/nrf/usb/usb.c | 169 ----- ports/nrf/usb/usb.h | 34 - ports/nrf/usb/usb_desc.c | 393 ------------ ports/nrf/usb/usb_desc.h | 87 --- ports/nrf/usb/usb_msc_flash.c | 143 ----- shared-bindings/busio/SPI.h | 3 + shared-bindings/usb_hid/Device.h | 2 +- shared-module/bitbangio/SPI.c | 3 +- shared-module/storage/__init__.c | 26 + shared-module/usb_hid/Device.c | 88 +++ shared-module/usb_hid/Device.h | 55 ++ shared-module/usb_hid/__init__.c | 154 +++++ supervisor/flash.h | 54 ++ supervisor/flash_root_pointers.h | 35 ++ supervisor/port.h | 3 + supervisor/serial.h | 10 + supervisor/shared/external_flash/common_commands.h | 47 ++ supervisor/shared/external_flash/devices.h | 375 +++++++++++ supervisor/shared/external_flash/external_flash.c | 615 ++++++++++++++++++ supervisor/shared/external_flash/external_flash.h | 48 ++ .../external_flash/external_flash_root_pointers.h | 35 ++ supervisor/shared/external_flash/qspi_flash.c | 56 ++ supervisor/shared/external_flash/qspi_flash.h | 31 + supervisor/shared/external_flash/spi_flash.c | 150 +++++ supervisor/shared/filesystem.c | 109 ++++ supervisor/shared/flash.c | 123 ++++ supervisor/shared/micropython.c | 58 ++ supervisor/shared/rgb_led_status.h | 2 +- supervisor/shared/serial.c | 56 ++ supervisor/shared/status_leds.c | 62 ++ supervisor/shared/status_leds.h | 36 ++ supervisor/shared/usb/tusb_config.h | 115 ++++ supervisor/shared/usb/usb.c | 140 +++++ supervisor/shared/usb/usb_desc.c | 46 ++ supervisor/shared/usb/usb_desc.h | 43 ++ supervisor/shared/usb/usb_msc_flash.c | 205 ++++++ supervisor/spi_flash_api.h | 45 ++ supervisor/supervisor.mk | 68 +- supervisor/usb.h | 44 ++ tools/gen_usb_descriptor.py | 474 ++++++++++++++ tools/hid_report_descriptors.py | 239 +++++++ tools/usb_descriptor | 2 +- 179 files changed, 5248 insertions(+), 6512 deletions(-) delete mode 100644 ports/atmel-samd/common-hal/storage/__init__.c delete mode 100644 ports/atmel-samd/common-hal/usb_hid/Device.c delete mode 100644 ports/atmel-samd/common-hal/usb_hid/Device.h delete mode 100644 ports/atmel-samd/common-hal/usb_hid/__init__.c delete mode 100644 ports/atmel-samd/external_flash/common_commands.h delete mode 100644 ports/atmel-samd/external_flash/devices.h delete mode 100644 ports/atmel-samd/external_flash/external_flash.c delete mode 100644 ports/atmel-samd/external_flash/external_flash.h delete mode 100644 ports/atmel-samd/external_flash/qspi_flash.c delete mode 100644 ports/atmel-samd/external_flash/spi_flash.c delete mode 100644 ports/atmel-samd/external_flash/spi_flash_api.h delete mode 100644 ports/atmel-samd/flash_api.c delete mode 100644 ports/atmel-samd/flash_api.h delete mode 100644 ports/atmel-samd/internal_flash.c delete mode 100644 ports/atmel-samd/internal_flash.h delete mode 100644 ports/atmel-samd/supervisor/filesystem.c create mode 100644 ports/atmel-samd/supervisor/internal_flash.c create mode 100644 ports/atmel-samd/supervisor/internal_flash.h create mode 100644 ports/atmel-samd/supervisor/internal_flash_root_pointers.h create mode 100644 ports/atmel-samd/supervisor/qspi_flash.c delete mode 100644 ports/atmel-samd/supervisor/serial.c create mode 100644 ports/atmel-samd/supervisor/usb.c delete mode 100644 ports/atmel-samd/tools/gen_usb_descriptor.py delete mode 100644 ports/atmel-samd/tools/hid_report_descriptors.py delete mode 100644 ports/atmel-samd/usb.c delete mode 100644 ports/atmel-samd/usb.h delete mode 100644 ports/atmel-samd/usb_mass_storage.c delete mode 100644 ports/atmel-samd/usb_mass_storage.h delete mode 100644 ports/nrf/common-hal/storage/__init__.c delete mode 100644 ports/nrf/common-hal/usb_hid/Device.c delete mode 100644 ports/nrf/common-hal/usb_hid/Device.h delete mode 100644 ports/nrf/common-hal/usb_hid/__init__.c delete mode 100644 ports/nrf/internal_flash.c delete mode 100644 ports/nrf/internal_flash.h delete mode 100644 ports/nrf/supervisor/filesystem.c create mode 100644 ports/nrf/supervisor/internal_flash.c create mode 100644 ports/nrf/supervisor/internal_flash.h create mode 100644 ports/nrf/supervisor/internal_flash_root_pointers.h create mode 100644 ports/nrf/supervisor/qspi_flash.c create mode 100644 ports/nrf/supervisor/usb.c delete mode 100644 ports/nrf/usb/tusb_config.h delete mode 100644 ports/nrf/usb/usb.c delete mode 100644 ports/nrf/usb/usb.h delete mode 100644 ports/nrf/usb/usb_desc.c delete mode 100644 ports/nrf/usb/usb_desc.h delete mode 100644 ports/nrf/usb/usb_msc_flash.c create mode 100644 shared-module/usb_hid/Device.c create mode 100644 shared-module/usb_hid/Device.h create mode 100644 shared-module/usb_hid/__init__.c create mode 100644 supervisor/flash.h create mode 100644 supervisor/flash_root_pointers.h create mode 100644 supervisor/shared/external_flash/common_commands.h create mode 100644 supervisor/shared/external_flash/devices.h create mode 100644 supervisor/shared/external_flash/external_flash.c create mode 100644 supervisor/shared/external_flash/external_flash.h create mode 100644 supervisor/shared/external_flash/external_flash_root_pointers.h create mode 100644 supervisor/shared/external_flash/qspi_flash.c create mode 100644 supervisor/shared/external_flash/qspi_flash.h create mode 100644 supervisor/shared/external_flash/spi_flash.c create mode 100644 supervisor/shared/filesystem.c create mode 100644 supervisor/shared/flash.c create mode 100644 supervisor/shared/micropython.c create mode 100644 supervisor/shared/serial.c create mode 100644 supervisor/shared/status_leds.c create mode 100644 supervisor/shared/status_leds.h create mode 100644 supervisor/shared/usb/tusb_config.h create mode 100644 supervisor/shared/usb/usb.c create mode 100644 supervisor/shared/usb/usb_desc.c create mode 100644 supervisor/shared/usb/usb_desc.h create mode 100644 supervisor/shared/usb/usb_msc_flash.c create mode 100644 supervisor/spi_flash_api.h create mode 100644 supervisor/usb.h create mode 100644 tools/gen_usb_descriptor.py create mode 100644 tools/hid_report_descriptors.py (limited to 'shared-module') diff --git a/.gitmodules b/.gitmodules index 38b6ea5df..1c1f8eb65 100644 --- a/.gitmodules +++ b/.gitmodules @@ -78,7 +78,7 @@ url = https://github.com/adafruit/nrfx.git [submodule "lib/tinyusb"] path = lib/tinyusb - url = https://github.com/hathach/tinyusb.git + url = https://github.com/tannewt/tinyusb.git branch = develop [submodule "tools/huffman"] path = tools/huffman diff --git a/lib/tinyusb b/lib/tinyusb index 33c61bfda..30e3c6413 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 33c61bfda2c3aada3cb06d36e12d7cf57da02037 +Subproject commit 30e3c64134789416e10ed867fa12c210b808e98f diff --git a/main.c b/main.c index c1379c571..ac55b72ba 100755 --- a/main.c +++ b/main.c @@ -51,6 +51,7 @@ #include "supervisor/shared/autoreload.h" #include "supervisor/shared/translate.h" #include "supervisor/shared/rgb_led_status.h" +#include "supervisor/shared/status_leds.h" #include "supervisor/shared/stack.h" #include "supervisor/serial.h" @@ -388,6 +389,8 @@ int __attribute__((used)) main(void) { // initialise the cpu and peripherals safe_mode_t safe_mode = port_init(); + // Turn on LEDs + init_status_leds(); rgb_led_status_init(); stack_init(); diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 7861f4235..552be92be 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -45,16 +45,13 @@ INC += -I. \ -Iasf4/$(CHIP_FAMILY)/hpl/tc \ -Iasf4/$(CHIP_FAMILY)/include \ -Iasf4/$(CHIP_FAMILY)/CMSIS/Include \ - -Iasf4/$(CHIP_FAMILY)/usb \ - -Iasf4/$(CHIP_FAMILY)/usb/class/cdc \ - -Iasf4/$(CHIP_FAMILY)/usb/class/hid \ - -Iasf4/$(CHIP_FAMILY)/usb/class/msc \ - -Iasf4/$(CHIP_FAMILY)/usb/device \ -Iasf4_conf/$(CHIP_FAMILY) \ -Iboards/$(BOARD) \ -Iboards/ \ -Iperipherals/ \ -Ifreetouch \ + -I../../lib/tinyusb/src \ + -I../../supervisor/shared/usb \ -I$(BUILD) BASE_CFLAGS = \ @@ -90,11 +87,15 @@ BASE_CFLAGS = \ # NDEBUG disables assert() statements. This reduces code size pretty dramatically, per tannewt. ifeq ($(CHIP_FAMILY), samd21) -CFLAGS = -Os -DNDEBUG +CFLAGS += -Os -DNDEBUG +# TinyUSB defines +CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_SAMD21 -DCFG_TUD_CDC_RX_BUFSIZE=128 -DCFG_TUD_CDC_TX_BUFSIZE=128 -DCFG_TUD_MSC_BUFSIZE=512 endif ifeq ($(CHIP_FAMILY), samd51) -CFLAGS = -Os -DNDEBUG +CFLAGS += -O0 -DNDEBUG +# TinyUSB defines +CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_SAMD51 -DCFG_TUD_CDC_RX_BUFSIZE=256 -DCFG_TUD_CDC_TX_BUFSIZE=256 -DCFG_TUD_MSC_BUFSIZE=1024 endif #Debugging/Optimization @@ -104,7 +105,7 @@ ifeq ($(DEBUG), 1) # You may want to disable -flto if it interferes with debugging. CFLAGS += -flto # You may want to enable these flags to make setting breakpoints easier. -## CFLAGS += -fno-inline -fno-ipa-sra + # CFLAGS += -fno-inline -fno-ipa-sra ifeq ($(CHIP_FAMILY), samd21) CFLAGS += -DENABLE_MICRO_TRACE_BUFFER endif @@ -204,7 +205,6 @@ SRC_ASF := \ hal/src/hal_spi_m_sync.c \ hal/src/hal_timer.c \ hal/src/hal_usart_async.c \ - hal/src/hal_usb_device.c \ hpl/adc/hpl_adc.c \ hpl/core/hpl_init.c \ hpl/dac/hpl_dac.c \ @@ -214,12 +214,6 @@ SRC_ASF := \ hpl/rtc/hpl_rtc.c \ hpl/sercom/hpl_sercom.c \ hpl/systick/hpl_systick.c \ - hpl/usb/hpl_usb.c \ - usb/class/cdc/device/cdcdf_acm.c \ - usb/class/hid/device/hiddf_generic.c \ - usb/class/msc/device/mscdf.c \ - usb/device/usbdc.c \ - usb/usb_protocol.c \ hal/utils/src/utils_list.c \ hal/utils/src/utils_ringbuffer.c \ @@ -246,7 +240,6 @@ SRC_C = \ board_busses.c \ background.c \ fatfs_port.c \ - flash_api.c \ mphalport.c \ reset.c \ peripherals/samd/clocks.c \ @@ -265,8 +258,6 @@ SRC_C = \ peripherals/samd/$(CHIP_FAMILY)/sercom.c \ peripherals/samd/$(CHIP_FAMILY)/timers.c \ tick.c \ - usb.c \ - usb_mass_storage.c \ bindings/samd/__init__.c \ bindings/samd/Clock.c \ boards/$(BOARD)/board.c \ @@ -274,6 +265,8 @@ SRC_C = \ lib/oofatfs/ff.c \ lib/oofatfs/option/ccsbcs.c \ lib/timeutils/timeutils.c \ + lib/tinyusb/src/portable/microchip/$(CHIP_FAMILY)/dcd.c \ + lib/tinyusb/src/portable/microchip/$(CHIP_FAMILY)/hal.c \ lib/utils/buffer_helper.c \ lib/utils/context_manager_helpers.c \ lib/utils/interrupt_char.c \ @@ -282,7 +275,6 @@ SRC_C = \ lib/utils/sys_stdio_mphal.c \ lib/libc/string0.c \ lib/mp-readline/readline.c \ - $(BUILD)/autogen_usb_descriptor.c \ freetouch/adafruit_ptc.c \ supervisor/shared/memory.c @@ -306,19 +298,6 @@ SRC_MOD += $(addprefix $(WIZNET5K_DIR)/,\ endif # MICROPY_PY_WIZNET5K endif # MICROPY_PY_NETWORK -# Choose which flash filesystem impl to use. -# (Right now INTERNAL_FLASH_FILESYSTEM and SPI_FLASH_FILESYSTEM are mutually exclusive. -# But that might not be true in the future.) -ifeq ($(INTERNAL_FLASH_FILESYSTEM),1) -SRC_C += internal_flash.c -endif -ifeq ($(SPI_FLASH_FILESYSTEM),1) -SRC_C += external_flash/external_flash.c external_flash/spi_flash.c -endif -ifeq ($(QSPI_FLASH_FILESYSTEM),1) -SRC_C += external_flash/external_flash.c external_flash/qspi_flash.c -endif - SRC_COMMON_HAL = \ board/__init__.c \ busio/__init__.c \ @@ -339,7 +318,6 @@ SRC_COMMON_HAL = \ rotaryio/IncrementalEncoder.c \ rtc/__init__.c \ rtc/RTC.c \ - storage/__init__.c \ supervisor/__init__.c \ supervisor/Runtime.c \ time/__init__.c \ @@ -352,10 +330,8 @@ SRC_COMMON_HAL = \ pulseio/PulseIn.c \ pulseio/PulseOut.c \ pulseio/PWMOut.c \ - usb_hid/__init__.c \ - usb_hid/Device.c \ touchio/__init__.c \ - touchio/TouchIn.c \ + touchio/TouchIn.c ifeq ($(INTERNAL_LIBM),1) SRC_LIBM = $(addprefix lib/,\ @@ -412,12 +388,17 @@ SRC_SHARED_MODULE = \ _stage/__init__.c \ _stage/Layer.c \ _stage/Text.c \ + storage/__init__.c \ os/__init__.c \ random/__init__.c \ - storage/__init__.c \ struct/__init__.c \ uheap/__init__.c \ - ustack/__init__.c + ustack/__init__.c \ + usb_hid/__init__.c \ + usb_hid/Device.c + + # usb_midi/__init__.c + # usb_midi/Port.c ifeq ($(MICROPY_PY_NETWORK),1) SRC_SHARED_MODULE += socket/__init__.c network/__init__.c @@ -489,21 +470,6 @@ $(BUILD)/firmware.uf2: $(BUILD)/firmware.bin $(STEPECHO) "Create $@" $(Q)$(PYTHON3) $(TOP)/tools/uf2/utils/uf2conv.py -b $(BOOTLOADER_SIZE) -c -o $@ $^ -$(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: autogen_usb_descriptor.intermediate - -.INTERMEDIATE: autogen_usb_descriptor.intermediate - -autogen_usb_descriptor.intermediate: tools/gen_usb_descriptor.py Makefile | $(HEADER_BUILD) - $(STEPECHO) "GEN $@" - $(Q)install -d $(BUILD)/genhdr - $(Q)$(PYTHON3) tools/gen_usb_descriptor.py \ - --manufacturer $(USB_MANUFACTURER)\ - --product $(USB_PRODUCT)\ - --vid $(USB_VID)\ - --pid $(USB_PID)\ - --output_c_file $(BUILD)/autogen_usb_descriptor.c\ - --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h - deploy: $(BUILD)/firmware.bin $(ECHO) "Writing $< to the board" $(BOSSAC) -u $< diff --git a/ports/atmel-samd/background.c b/ports/atmel-samd/background.c index 3cf6831a3..099cb8b23 100644 --- a/ports/atmel-samd/background.c +++ b/ports/atmel-samd/background.c @@ -27,8 +27,7 @@ #include "audio_dma.h" #include "tick.h" -#include "usb.h" -#include "usb_mass_storage.h" +#include "supervisor/usb.h" #include "shared-module/displayio/__init__.h" #include "shared-module/network/__init__.h" @@ -45,8 +44,8 @@ void run_background_tasks(void) { #if MICROPY_PY_NETWORK network_module_background(); #endif - usb_msc_background(); - usb_cdc_background(); + usb_background(); + last_finished_tick = ticks_ms; } diff --git a/ports/atmel-samd/boards/arduino_zero/board.c b/ports/atmel-samd/boards/arduino_zero/board.c index 2dc1683ea..770bc8259 100644 --- a/ports/atmel-samd/boards/arduino_zero/board.c +++ b/ports/atmel-samd/boards/arduino_zero/board.c @@ -30,13 +30,6 @@ void board_init(void) { - gpio_set_pin_function(MICROPY_HW_LED_TX, GPIO_PIN_FUNCTION_OFF); - gpio_set_pin_direction(MICROPY_HW_LED_TX, GPIO_DIRECTION_OUT); - gpio_set_pin_level(MICROPY_HW_LED_TX, true); - - gpio_set_pin_function(MICROPY_HW_LED_RX, GPIO_PIN_FUNCTION_OFF); - gpio_set_pin_direction(MICROPY_HW_LED_RX, GPIO_DIRECTION_OUT); - gpio_set_pin_level(MICROPY_HW_LED_RX, true); } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/arduino_zero/mpconfigboard.h b/ports/atmel-samd/boards/arduino_zero/mpconfigboard.h index 1382d4736..56e4dbcec 100644 --- a/ports/atmel-samd/boards/arduino_zero/mpconfigboard.h +++ b/ports/atmel-samd/boards/arduino_zero/mpconfigboard.h @@ -1,16 +1,14 @@ #define MICROPY_HW_BOARD_NAME "Arduino Zero" #define MICROPY_HW_MCU_NAME "samd21g18" -// #define MICROPY_HW_LED_MSC PIN_PA17 // red -#define MICROPY_HW_LED_TX PIN_PA27 -#define MICROPY_HW_LED_RX PIN_PB03 +// #define MICROPY_HW_LED_MSC &pin_PA17 // red +#define MICROPY_HW_LED_TX &pin_PA27 +#define MICROPY_HW_LED_RX &pin_PB03 #define MICROPY_PORT_A (PORT_PA24 | PORT_PA25 | PORT_PA27) #define MICROPY_PORT_B (PORT_PB03) #define MICROPY_PORT_C (0) -#include "internal_flash.h" - #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) diff --git a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h index 5077840cb..6dfaf87e9 100644 --- a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h @@ -8,52 +8,25 @@ #define SPI_FLASH_BAUDRATE (8000000) // On-board flash -#define SPI_FLASH_MOSI_PIN PIN_PA20 -#define SPI_FLASH_MISO_PIN PIN_PA16 -#define SPI_FLASH_SCK_PIN PIN_PA21 -#define SPI_FLASH_CS_PIN PIN_PB22 - -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA20D_SERCOM3_PAD2 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA16D_SERCOM3_PAD0 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA21D_SERCOM3_PAD3 -#define SPI_FLASH_SERCOM SERCOM3 -#define SPI_FLASH_SERCOM_INDEX 3 -#define SPI_FLASH_MOSI_PAD 2 -#define SPI_FLASH_MISO_PAD 0 -#define SPI_FLASH_SCK_PAD 3 - -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 1 -#define SPI_FLASH_DIPO 0 // same as MISO PAD +#define SPI_FLASH_MOSI_PIN &pin_PA20 +#define SPI_FLASH_MISO_PIN &pin_PA16 +#define SPI_FLASH_SCK_PIN &pin_PA21 +#define SPI_FLASH_CS_PIN &pin_PB22 // These are pins not to reset. // PA24 and PA25 are USB. -#define MICROPY_PORT_A (PORT_PA16 | PORT_PA20 | PORT_PA21 | PORT_PA24 | PORT_PA25) -#define MICROPY_PORT_B (PORT_PB22) +#define MICROPY_PORT_A (0) +#define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) #define SPEAKER_ENABLE_PIN (&pin_PA30) -#include "external_flash/devices.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 -#define EXTERNAL_FLASH_DEVICES S25FL216K, \ - GD25Q16C - -#include "external_flash/external_flash.h" - #define CALIBRATE_CRYSTALLESS 1 // Explanation of how a user got into safe mode. diff --git a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk index 831106adc..e5665afb6 100644 --- a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "CircuitPlayground Express" USB_MANUFACTURER = "Adafruit Industries LLC" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21G18A diff --git a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h index 638a6646c..250300858 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h +++ b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h @@ -11,52 +11,24 @@ #define SPI_FLASH_BAUDRATE (8000000) // On-board flash -#define SPI_FLASH_MOSI_PIN PIN_PA20 -#define SPI_FLASH_MISO_PIN PIN_PA16 -#define SPI_FLASH_SCK_PIN PIN_PA21 -#define SPI_FLASH_CS_PIN PIN_PB22 - -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA20D_SERCOM3_PAD2 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA16D_SERCOM3_PAD0 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA21D_SERCOM3_PAD3 -#define SPI_FLASH_SERCOM SERCOM3 -#define SPI_FLASH_SERCOM_INDEX 3 -#define SPI_FLASH_MOSI_PAD 2 -#define SPI_FLASH_MISO_PAD 0 -#define SPI_FLASH_SCK_PAD 3 - -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 1 -#define SPI_FLASH_DIPO 0 // same as MISO PAD +#define SPI_FLASH_MOSI_PIN &pin_PA20 +#define SPI_FLASH_MISO_PIN &pin_PA16 +#define SPI_FLASH_SCK_PIN &pin_PA21 +#define SPI_FLASH_CS_PIN &pin_PB22 // These are pins not to reset. -// PA24 and PA25 are USB. -#define MICROPY_PORT_A (PORT_PA16 | PORT_PA20 | PORT_PA21 | PORT_PA24 | PORT_PA25) -#define MICROPY_PORT_B (PORT_PB22) +#define MICROPY_PORT_A (0) +#define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) #define SPEAKER_ENABLE_PIN (&pin_PA30) -#include "external_flash/devices.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 -#define EXTERNAL_FLASH_DEVICES S25FL216K, \ - GD25Q16C - -#include "external_flash/external_flash.h" - #define CALIBRATE_CRYSTALLESS 1 // Explanation of how a user got into safe mode. diff --git a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk index 972c4b10d..b07d63984 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "CircuitPlayground Express with Crickit libraries" USB_MANUFACTURER = "Adafruit Industries LLC" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" # Turn off longints for Crickit build to make room for additional frozen libs. LONGINT_IMPL = NONE diff --git a/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h index c8b5c56ee..c28c4aafb 100644 --- a/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h @@ -3,12 +3,10 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Adalogger" #define MICROPY_HW_MCU_NAME "samd21g18" -#define MICROPY_PORT_A (PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (0) #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) -#include "internal_flash.h" - #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) diff --git a/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.h index dcac9749d..d66efb36f 100644 --- a/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.h @@ -4,12 +4,10 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Basic" #define MICROPY_HW_MCU_NAME "samd21g18" -#define MICROPY_PORT_A (PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (0) #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) -#include "internal_flash.h" - #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h index 03d0ba9bd..1f02ee995 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h @@ -3,35 +3,16 @@ #define MICROPY_HW_NEOPIXEL (&pin_PA06) -// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PA08 -#define SPI_FLASH_MISO_PIN PIN_PA14 -#define SPI_FLASH_SCK_PIN PIN_PA09 -#define SPI_FLASH_CS_PIN PIN_PA13 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA08D_SERCOM2_PAD0 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA14C_SERCOM2_PAD2 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA09D_SERCOM2_PAD1 -#define SPI_FLASH_SERCOM SERCOM2 -#define SPI_FLASH_SERCOM_INDEX 2 -#define SPI_FLASH_MOSI_PAD 0 -#define SPI_FLASH_MISO_PAD 2 -#define SPI_FLASH_SCK_PAD 1 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 0 -#define SPI_FLASH_DIPO 2 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PA08 +#define SPI_FLASH_MISO_PIN &pin_PA14 +#define SPI_FLASH_SCK_PIN &pin_PA09 +#define SPI_FLASH_CS_PIN &pin_PA13 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA06 | PORT_PA08 | PORT_PA09 | PORT_PA13 | PORT_PA14 | PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (PORT_PA06) #define MICROPY_PORT_B ( 0 ) #define MICROPY_PORT_C ( 0 ) -#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -39,14 +20,6 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 -#define EXTERNAL_FLASH_DEVICES S25FL216K, \ - GD25Q16C - -#include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 1 #define DEFAULT_I2C_BUS_SCL (&pin_PA23) diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index e9f9b09fb..57ce96285 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "Feather M0 Express" USB_MANUFACTURER = "Adafruit Industries LLC" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21G18A diff --git a/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.h index 756462013..b5544e253 100644 --- a/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.h @@ -3,50 +3,22 @@ #define MICROPY_HW_NEOPIXEL (&pin_PA06) -// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PA08 -#define SPI_FLASH_MISO_PIN PIN_PA14 -#define SPI_FLASH_SCK_PIN PIN_PA09 -#define SPI_FLASH_CS_PIN PIN_PA13 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA08D_SERCOM2_PAD0 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA14C_SERCOM2_PAD2 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA09D_SERCOM2_PAD1 -#define SPI_FLASH_SERCOM SERCOM2 -#define SPI_FLASH_SERCOM_INDEX 2 -#define SPI_FLASH_MOSI_PAD 0 -#define SPI_FLASH_MISO_PAD 2 -#define SPI_FLASH_SCK_PAD 1 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 0 -#define SPI_FLASH_DIPO 2 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PA08 +#define SPI_FLASH_MISO_PIN &pin_PA14 +#define SPI_FLASH_SCK_PIN &pin_PA09 +#define SPI_FLASH_CS_PIN &pin_PA13 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA06 | PORT_PA08 | PORT_PA09 | PORT_PA13 | PORT_PA14 | PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (PORT_PA06) #define MICROPY_PORT_B ( 0 ) #define MICROPY_PORT_C ( 0 ) -#include "external_flash/external_flash.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 -#define EXTERNAL_FLASH_DEVICES S25FL216K, \ - GD25Q16C - -#include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 1 #define DEFAULT_I2C_BUS_SCL (&pin_PA23) diff --git a/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk index 7ff5337a3..f773bcb57 100644 --- a/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "Feather M0 Express" USB_MANUFACTURER = "Adafruit Industries LLC" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21G18A diff --git a/ports/atmel-samd/boards/feather_m0_rfm69/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_rfm69/mpconfigboard.h index 4a530ed29..c385a97ea 100644 --- a/ports/atmel-samd/boards/feather_m0_rfm69/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_rfm69/mpconfigboard.h @@ -4,12 +4,10 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 RFM69" #define MICROPY_HW_MCU_NAME "samd21g18" -#define MICROPY_PORT_A (PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (0) #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) -#include "internal_flash.h" - #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) diff --git a/ports/atmel-samd/boards/feather_m0_rfm9x/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_rfm9x/mpconfigboard.h index 3a6f28c12..1e3a0bda0 100644 --- a/ports/atmel-samd/boards/feather_m0_rfm9x/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_rfm9x/mpconfigboard.h @@ -4,12 +4,10 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 RFM9x" #define MICROPY_HW_MCU_NAME "samd21g18" -#define MICROPY_PORT_A (PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (0) #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) -#include "internal_flash.h" - #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h index a7a2d7de0..6bf1df156 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h @@ -5,49 +5,22 @@ #define MICROPY_HW_NEOPIXEL (&pin_PA06) -// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PA08 -#define SPI_FLASH_MISO_PIN PIN_PA14 -#define SPI_FLASH_SCK_PIN PIN_PA09 -#define SPI_FLASH_CS_PIN PIN_PA13 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA08D_SERCOM2_PAD0 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA14C_SERCOM2_PAD2 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA09D_SERCOM2_PAD1 -#define SPI_FLASH_SERCOM SERCOM2 -#define SPI_FLASH_SERCOM_INDEX 2 -#define SPI_FLASH_MOSI_PAD 0 -#define SPI_FLASH_MISO_PAD 2 -#define SPI_FLASH_SCK_PAD 1 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 0 -#define SPI_FLASH_DIPO 2 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PA08 +#define SPI_FLASH_MISO_PIN &pin_PA14 +#define SPI_FLASH_SCK_PIN &pin_PA09 +#define SPI_FLASH_CS_PIN &pin_PA13 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA06 | PORT_PA08 | PORT_PA09 | PORT_PA13 | PORT_PA14 | PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (PORT_PA06) #define MICROPY_PORT_B ( 0 ) #define MICROPY_PORT_C ( 0 ) -#include "external_flash/external_flash.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES S25FL064L - -#include "external_flash/external_flash.h" - #define DEFAULT_I2C_BUS_SCL (&pin_PA23) #define DEFAULT_I2C_BUS_SDA (&pin_PA22) diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk index 79bf2d34a..4bae6ffa7 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "Feather M0 Supersized" USB_MANUFACTURER = "Dave Astels" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = "S25FL064L" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21G18A diff --git a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h index e9e921b4d..98906a844 100644 --- a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h @@ -23,15 +23,8 @@ #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES GD25Q16C - #define EXTERNAL_FLASH_QSPI_DUAL -#include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 1 #define DEFAULT_I2C_BUS_SCL (&pin_PA13) diff --git a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk index bbc5985a9..3f2cb6ce4 100644 --- a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk @@ -5,6 +5,9 @@ USB_PRODUCT = "Feather M4 Express" USB_MANUFACTURER = "Adafruit Industries LLC" QSPI_FLASH_FILESYSTEM = 1 + +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = GD25Q16C LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD51J19A diff --git a/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.h b/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.h index 32c549953..c4e025178 100755 --- a/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.h @@ -3,50 +3,22 @@ #define MICROPY_HW_NEOPIXEL (&pin_PA22) -// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PA31 -#define SPI_FLASH_MISO_PIN PIN_PA30 -#define SPI_FLASH_SCK_PIN PIN_PA17 -#define SPI_FLASH_CS_PIN PIN_PA28 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA31D_SERCOM1_PAD3 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA30D_SERCOM1_PAD2 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA17C_SERCOM1_PAD1 -#define SPI_FLASH_SERCOM SERCOM1 -#define SPI_FLASH_SERCOM_INDEX 1 -#define SPI_FLASH_MOSI_PAD 3 -#define SPI_FLASH_MISO_PAD 2 -#define SPI_FLASH_SCK_PAD 1 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 2 -#define SPI_FLASH_DIPO 2 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PA31 +#define SPI_FLASH_MISO_PIN &pin_PA30 +#define SPI_FLASH_SCK_PIN &pin_PA17 +#define SPI_FLASH_CS_PIN &pin_PA28 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA17 | PORT_PA22 | PORT_PA24 | PORT_PA25 | PORT_PA28 | PORT_PA30 | PORT_PA31) +#define MICROPY_PORT_A (PORT_PA22) #define MICROPY_PORT_B ( 0 ) #define MICROPY_PORT_C ( 0 ) -#include "external_flash/external_flash.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 -#define EXTERNAL_FLASH_DEVICES S25FL216K, \ - GD25Q16C - -#include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 1 #define DEFAULT_I2C_BUS_SCL (&pin_PA13) diff --git a/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.mk b/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.mk index 42177d274..634a9ede7 100755 --- a/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "Feather RadioFruit Zigbee" USB_MANUFACTURER = "Adafruit Industries LLC" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMR21G18A diff --git a/ports/atmel-samd/boards/gemma_m0/mpconfigboard.h b/ports/atmel-samd/boards/gemma_m0/mpconfigboard.h index 1ec217096..08d1803f2 100644 --- a/ports/atmel-samd/boards/gemma_m0/mpconfigboard.h +++ b/ports/atmel-samd/boards/gemma_m0/mpconfigboard.h @@ -18,8 +18,6 @@ #define DEFAULT_UART_BUS_RX (&pin_PA05) #define DEFAULT_UART_BUS_TX (&pin_PA04) -#include "internal_flash.h" - #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) #define IGNORE_PIN_PA03 1 diff --git a/ports/atmel-samd/boards/grandcentral_m4_express/board.c b/ports/atmel-samd/boards/grandcentral_m4_express/board.c index 360b5d8f9..7599f02b8 100644 --- a/ports/atmel-samd/boards/grandcentral_m4_express/board.c +++ b/ports/atmel-samd/boards/grandcentral_m4_express/board.c @@ -29,13 +29,6 @@ #include "hal/include/hal_gpio.h" void board_init(void) { - gpio_set_pin_function(MICROPY_HW_LED_TX, GPIO_PIN_FUNCTION_OFF); - gpio_set_pin_direction(MICROPY_HW_LED_TX, GPIO_DIRECTION_OUT); - gpio_set_pin_level(MICROPY_HW_LED_TX, true); - - gpio_set_pin_function(MICROPY_HW_LED_RX, GPIO_PIN_FUNCTION_OFF); - gpio_set_pin_direction(MICROPY_HW_LED_RX, GPIO_DIRECTION_OUT); - gpio_set_pin_level(MICROPY_HW_LED_RX, true); } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.h index 231bf20e0..0d2d65ba5 100644 --- a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.h @@ -5,8 +5,8 @@ // This is for Rev A which is green -#define MICROPY_HW_LED_TX PIN_PC30 -#define MICROPY_HW_LED_RX PIN_PC31 +#define MICROPY_HW_LED_TX &(pin_PC30) +#define MICROPY_HW_LED_RX &(pin_PC31) #define MICROPY_HW_NEOPIXEL (&pin_PC24) @@ -27,13 +27,6 @@ #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES GD25Q64C - -#include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 1 #define DEFAULT_I2C_BUS_SCL (&pin_PB21) diff --git a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk index 3e8717ecf..2605c9f55 100644 --- a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk @@ -5,6 +5,9 @@ USB_PRODUCT = "Grand Central M4 Express" USB_MANUFACTURER = "Adafruit Industries LLC" QSPI_FLASH_FILESYSTEM = 1 + +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = "GD25Q64C" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD51P20A diff --git a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.h index 8450882b9..7dd4bf7c4 100644 --- a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.h @@ -3,51 +3,22 @@ #define MICROPY_HW_NEOPIXEL (&pin_PA12) -// Clock rates are off: Saleae reads 12MHz which is the limit even though we set it to the safer 8MHz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PB10 -#define SPI_FLASH_MISO_PIN PIN_PA13 -#define SPI_FLASH_SCK_PIN PIN_PB11 -#define SPI_FLASH_CS_PIN PIN_PA07 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PB10D_SERCOM4_PAD2 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA13D_SERCOM4_PAD1 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PB11D_SERCOM4_PAD3 -#define SPI_FLASH_SERCOM SERCOM4 -#define SPI_FLASH_SERCOM_INDEX 4 -#define SPI_FLASH_MOSI_PAD 2 -#define SPI_FLASH_MISO_PAD 1 -#define SPI_FLASH_SCK_PAD 3 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 0x1 -#define SPI_FLASH_DIPO 1 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PB10 +#define SPI_FLASH_MISO_PIN &pin_PA13 +#define SPI_FLASH_SCK_PIN &pin_PB11 +#define SPI_FLASH_CS_PIN &pin_PA07 // These are pins not to reset. -#define MICROPY_PORT_A ( PORT_PA01 | PORT_PA07 | PORT_PA12 | PORT_PA13 | PORT_PA24 | PORT_PA25 | PORT_PA27 | PORT_PA28) -#define MICROPY_PORT_B ( PORT_PB10 | PORT_PB11 | PORT_PB22 | PORT_PB23 ) +#define MICROPY_PORT_A ( PORT_PA01 | PORT_PA12 | PORT_PA27 | PORT_PA28) +#define MICROPY_PORT_B ( PORT_PB22 | PORT_PB23 ) #define MICROPY_PORT_C ( 0 ) -#include "external_flash/external_flash.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 - -#define EXTERNAL_FLASH_DEVICES W25Q64JV_IQ, \ - GD25Q64C - -#include "external_flash/external_flash.h" - #define DEFAULT_I2C_BUS_SCL (&pin_PA17) #define DEFAULT_I2C_BUS_SDA (&pin_PA16) diff --git a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk index 817d93943..37f1bdef0 100644 --- a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "HalloWing M0 Express" USB_MANUFACTURER = "Adafruit Industries LLC" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "W25Q64JV_IQ, GD25Q64C" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21G18A diff --git a/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.h index f56f57c46..4de28d388 100644 --- a/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.h @@ -5,32 +5,14 @@ #define MICROPY_HW_APA102_MOSI (&pin_PA01) #define MICROPY_HW_APA102_SCK (&pin_PA00) -// Saleae reads 12mhz which is the limit even though we set it to the safer 8mhz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PB22 -#define SPI_FLASH_MISO_PIN PIN_PB03 -#define SPI_FLASH_SCK_PIN PIN_PB23 -#define SPI_FLASH_CS_PIN PIN_PA27 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PB22D_SERCOM5_PAD2 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PB03D_SERCOM5_PAD1 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PB23D_SERCOM5_PAD3 -#define SPI_FLASH_SERCOM SERCOM5 -#define SPI_FLASH_SERCOM_INDEX 5 -#define SPI_FLASH_MOSI_PAD 2 -#define SPI_FLASH_MISO_PAD 1 -#define SPI_FLASH_SCK_PAD 3 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 1 -#define SPI_FLASH_DIPO 1 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PB22 +#define SPI_FLASH_MISO_PIN &pin_PB03 +#define SPI_FLASH_SCK_PIN &pin_PB23 +#define SPI_FLASH_CS_PIN &pin_PA27 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA00 | PORT_PA01 | PORT_PA27 | PORT_PA24 | PORT_PA25) -#define MICROPY_PORT_B (PORT_PB22 | PORT_PB23 | PORT_PB03 ) +#define MICROPY_PORT_A (PORT_PA00 | PORT_PA01) +#define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) // If you change this, then make sure to update the linker scripts as well to @@ -39,14 +21,6 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 -#define EXTERNAL_FLASH_DEVICES W25Q16FW, \ - GD25Q16C - -#include "external_flash/external_flash.h" - #define DEFAULT_I2C_BUS_SCL (&pin_PA23) #define DEFAULT_I2C_BUS_SDA (&pin_PA22) diff --git a/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk index 9f62ae839..8fcf5208f 100644 --- a/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "ItsyBitsy M0 Express" USB_MANUFACTURER = "Adafruit Industries LLC" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "W25Q16FW, GD25Q16C" LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21G18A diff --git a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.h index e47d5390d..5bbfec9ce 100644 --- a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.h @@ -25,13 +25,6 @@ #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES GD25Q16C - -#include "external_flash/external_flash.h" - #define DEFAULT_I2C_BUS_SCL (&pin_PA13) #define DEFAULT_I2C_BUS_SDA (&pin_PA12) diff --git a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk index 27f5ea2b8..2afff5965 100644 --- a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "ItsyBitsy M4 Express" USB_MANUFACTURER = "Adafruit Industries LLC" QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = GD25Q16C LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD51G19A diff --git a/ports/atmel-samd/boards/meowmeow/mpconfigboard.h b/ports/atmel-samd/boards/meowmeow/mpconfigboard.h index 3ce00b04f..f07eb9cce 100644 --- a/ports/atmel-samd/boards/meowmeow/mpconfigboard.h +++ b/ports/atmel-samd/boards/meowmeow/mpconfigboard.h @@ -1,15 +1,11 @@ #define MICROPY_HW_BOARD_NAME "Meow Meow" #define MICROPY_HW_MCU_NAME "samd21g18" - // These are pins not to reset. -// PA24 and PA25 are USB. -#define MICROPY_PORT_A (PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (0) #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) -#include "internal_flash.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 0 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h index 40ef3ce78..28bb1f293 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h @@ -1,37 +1,22 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Metro M0 Express" #define MICROPY_HW_MCU_NAME "samd21g18" -#define MICROPY_HW_LED_TX PIN_PA27 -//#define MICROPY_HW_LED_RX PIN_PA31 +//#define MICROPY_HW_LED_TX &pin_PA27 +//#define MICROPY_HW_LED_RX &pin_PA31 #define MICROPY_HW_NEOPIXEL (&pin_PA30) // Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz. #define SPI_FLASH_BAUDRATE (8000000) -#define SPI_FLASH_MOSI_PIN PIN_PB22 -#define SPI_FLASH_MISO_PIN PIN_PB03 -#define SPI_FLASH_SCK_PIN PIN_PB23 -#define SPI_FLASH_CS_PIN PIN_PA13 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PB22D_SERCOM5_PAD2 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PB03D_SERCOM5_PAD1 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PB23D_SERCOM5_PAD3 -#define SPI_FLASH_SERCOM SERCOM5 -#define SPI_FLASH_SERCOM_INDEX 5 -#define SPI_FLASH_MOSI_PAD 2 -#define SPI_FLASH_MISO_PAD 1 -#define SPI_FLASH_SCK_PAD 3 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 1 -#define SPI_FLASH_DIPO 1 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PB22 +#define SPI_FLASH_MISO_PIN &pin_PB03 +#define SPI_FLASH_SCK_PIN &pin_PB23 +#define SPI_FLASH_CS_PIN &pin_PA13 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA13 |PORT_PA24 | PORT_PA25 | PORT_PA27 | PORT_PA30 | PORT_PA31) -#define MICROPY_PORT_B (PORT_PB03 | PORT_PB22 | PORT_PB23) +#define MICROPY_PORT_A (PORT_PA24 | PORT_PA25 | PORT_PA30 | PORT_PA31) +#define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) // If you change this, then make sure to update the linker scripts as well to @@ -40,14 +25,6 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 2 -#define EXTERNAL_FLASH_DEVICES S25FL216K, \ - GD25Q16C - -#include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 1 #define DEFAULT_I2C_BUS_SCL (&pin_PA23) diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk index 40a0092ac..49a94144b 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk @@ -12,3 +12,7 @@ CHIP_FAMILY = samd21 MICROPY_PY_NETWORK = 1 MICROPY_PY_WIZNET5K = 5500 + +SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" diff --git a/ports/atmel-samd/boards/metro_m4_express/board.c b/ports/atmel-samd/boards/metro_m4_express/board.c index a98385d29..0f60736a2 100644 --- a/ports/atmel-samd/boards/metro_m4_express/board.c +++ b/ports/atmel-samd/boards/metro_m4_express/board.c @@ -29,13 +29,6 @@ #include "hal/include/hal_gpio.h" void board_init(void) { - gpio_set_pin_function(MICROPY_HW_LED_TX, GPIO_PIN_FUNCTION_OFF); - gpio_set_pin_direction(MICROPY_HW_LED_TX, GPIO_DIRECTION_OUT); - gpio_set_pin_level(MICROPY_HW_LED_TX, true); - - gpio_set_pin_function(MICROPY_HW_LED_RX, GPIO_PIN_FUNCTION_OFF); - gpio_set_pin_direction(MICROPY_HW_LED_RX, GPIO_DIRECTION_OUT); - gpio_set_pin_level(MICROPY_HW_LED_RX, true); } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h index f1201c8a0..06feb1a22 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h @@ -5,16 +5,16 @@ // This is for Rev F which is green -#define MICROPY_HW_LED_TX PIN_PA27 -#define MICROPY_HW_LED_RX PIN_PB06 +#define MICROPY_HW_LED_TX (&pin_PA27) +#define MICROPY_HW_LED_RX (&pin_PB06) #define MICROPY_HW_NEOPIXEL (&pin_PB22) // These are pins not to reset. -// QSPI Data pins and TX LED -#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11 | PORT_PA27) -// RX LED, QSPI CS, QSPI SCK and NeoPixel pin -#define MICROPY_PORT_B ( PORT_PB06 | PORT_PB10 | PORT_PB11 | PORT_PB22) +// QSPI Data pins +#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11) +// QSPI CS, QSPI SCK and NeoPixel pin +#define MICROPY_PORT_B (PORT_PB10 | PORT_PB11 | PORT_PB22) #define MICROPY_PORT_C (0) #define MICROPY_PORT_D (0) @@ -26,13 +26,6 @@ #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 3 -#define EXTERNAL_FLASH_DEVICES S25FL116K, S25FL216K, GD25Q16C - -#include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 1 #define DEFAULT_I2C_BUS_SCL (&pin_PB03) diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk index cbc89159f..633856362 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk @@ -5,6 +5,9 @@ USB_PRODUCT = "Metro M4 Express" USB_MANUFACTURER = "Adafruit Industries LLC" QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 3 +EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C" + LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD51J19A diff --git a/ports/atmel-samd/boards/pirkey_m0/mpconfigboard.h b/ports/atmel-samd/boards/pirkey_m0/mpconfigboard.h index 15979ae77..cbbd4b569 100644 --- a/ports/atmel-samd/boards/pirkey_m0/mpconfigboard.h +++ b/ports/atmel-samd/boards/pirkey_m0/mpconfigboard.h @@ -15,8 +15,6 @@ // A number of modules are removed for pIRKey to make room for frozen libraries. #define PIRKEY_M0 (1) -#include "internal_flash.h" - #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) #define IGNORE_PIN_PA02 1 diff --git a/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.h index 1464d100c..6870b86ba 100644 --- a/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.h @@ -26,13 +26,6 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES GD25Q64C - -#include "external_flash/external_flash.h" - #define DEFAULT_I2C_BUS_SCL (&pin_PB08) #define DEFAULT_I2C_BUS_SDA (&pin_PB09) diff --git a/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk index 3841a87c7..7094b79be 100644 --- a/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT = "Trellis M4 Express" USB_MANUFACTURER = "Adafruit Industries LLC" QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = GD25Q64C LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD51G19A diff --git a/ports/atmel-samd/boards/trinket_m0/mpconfigboard.h b/ports/atmel-samd/boards/trinket_m0/mpconfigboard.h index 9036f8e7b..24f3004e9 100644 --- a/ports/atmel-samd/boards/trinket_m0/mpconfigboard.h +++ b/ports/atmel-samd/boards/trinket_m0/mpconfigboard.h @@ -9,8 +9,6 @@ #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) -#include "internal_flash.h" - #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - 0x010000) diff --git a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h index 8e3fdd7a8..dfbd873b6 100644 --- a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h +++ b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h @@ -5,56 +5,24 @@ // #define MICROPY_HW_APA102_MOSI (&pin_PA00) // #define MICROPY_HW_APA102_SCK (&pin_PA01) -// Salae reads 12mhz which is the limit even though we set it to the -// safer 8mhz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PA16 -#define SPI_FLASH_MISO_PIN PIN_PA19 -#define SPI_FLASH_SCK_PIN PIN_PA17 -#define SPI_FLASH_CS_PIN PIN_PA11 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA16D_SERCOM3_PAD0 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA19D_SERCOM3_PAD3 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA17D_SERCOM3_PAD1 -#define SPI_FLASH_SERCOM SERCOM3 -#define SPI_FLASH_SERCOM_INDEX 3 -#define SPI_FLASH_MOSI_PAD 0 -#define SPI_FLASH_MISO_PAD 3 -#define SPI_FLASH_SCK_PAD 1 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 0 -#define SPI_FLASH_DIPO 3 // same as MISO pad - -#define SPI_FLASH_CS PIN_PA11 +#define SPI_FLASH_MOSI_PIN &pin_PA16 +#define SPI_FLASH_MISO_PIN &pin_PA19 +#define SPI_FLASH_SCK_PIN &pin_PA17 +#define SPI_FLASH_CS_PIN &pin_PA11 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA00 | PORT_PA01 | PORT_PA11 | PORT_PA16 |\ - PORT_PA17 | PORT_PA18 | PORT_PA19 | PORT_PA24 |\ - PORT_PA25) +#define MICROPY_PORT_A (PORT_PA00 | PORT_PA01 | PORT_PA18) #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) #define CALIBRATE_CRYSTALLESS 1 -#include "external_flash/external_flash.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES W25Q32BV - -#include "external_flash/external_flash.h" - #define DEFAULT_I2C_BUS_SCL (&pin_PA09) #define DEFAULT_I2C_BUS_SDA (&pin_PA08) diff --git a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk index d97aa08ab..c9c196da7 100644 --- a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk +++ b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk @@ -5,6 +5,8 @@ USB_PRODUCT="Trinket M0 Haxpress" USB_MANUFACTURER="Radomir Dopieralski" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = W25Q32BV LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21E18A diff --git a/ports/atmel-samd/boards/ugame10/mpconfigboard.h b/ports/atmel-samd/boards/ugame10/mpconfigboard.h index ed822952d..c22a3f735 100644 --- a/ports/atmel-samd/boards/ugame10/mpconfigboard.h +++ b/ports/atmel-samd/boards/ugame10/mpconfigboard.h @@ -1,53 +1,24 @@ #define MICROPY_HW_BOARD_NAME "uGame10" #define MICROPY_HW_MCU_NAME "samd21e18" -// Salae reads 12mhz which is the limit even though we set it to the -// safer 8mhz. -#define SPI_FLASH_BAUDRATE (8000000) - -#define SPI_FLASH_MOSI_PIN PIN_PA16 -#define SPI_FLASH_MISO_PIN PIN_PA19 -#define SPI_FLASH_SCK_PIN PIN_PA17 -#define SPI_FLASH_CS_PIN PIN_PA18 -#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA16D_SERCOM3_PAD0 -#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA19D_SERCOM3_PAD3 -#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA17D_SERCOM3_PAD1 -#define SPI_FLASH_SERCOM SERCOM3 -#define SPI_FLASH_SERCOM_INDEX 3 -#define SPI_FLASH_MOSI_PAD 0 -#define SPI_FLASH_MISO_PAD 3 -#define SPI_FLASH_SCK_PAD 1 -// Transmit Data Pinout -// <0x0=>PAD[0,1]_DO_SCK -// <0x1=>PAD[2,3]_DO_SCK -// <0x2=>PAD[3,1]_DO_SCK -// <0x3=>PAD[0,3]_DO_SCK -#define SPI_FLASH_DOPO 0 -#define SPI_FLASH_DIPO 3 // same as MISO pad +#define SPI_FLASH_MOSI_PIN &pin_PA16 +#define SPI_FLASH_MISO_PIN &pin_PA19 +#define SPI_FLASH_SCK_PIN &pin_PA17 +#define SPI_FLASH_CS_PIN &pin_PA18 // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA16 | PORT_PA17 | PORT_PA18 | PORT_PA19 |\ - PORT_PA24 | PORT_PA25) +#define MICROPY_PORT_A (0) #define MICROPY_PORT_B (0) #define MICROPY_PORT_C (0) #define CALIBRATE_CRYSTALLESS 1 -#include "external_flash/external_flash.h" - // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES S25FL216K - -#include "external_flash/external_flash.h" - #define EXTRA_BUILTIN_MODULES \ { MP_OBJ_NEW_QSTR(MP_QSTR_audioio), (mp_obj_t)&audioio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_gamepad),(mp_obj_t)&gamepad_module }, \ diff --git a/ports/atmel-samd/boards/ugame10/mpconfigboard.mk b/ports/atmel-samd/boards/ugame10/mpconfigboard.mk index 2386531f9..6dc0af9cf 100644 --- a/ports/atmel-samd/boards/ugame10/mpconfigboard.mk +++ b/ports/atmel-samd/boards/ugame10/mpconfigboard.mk @@ -6,6 +6,8 @@ USB_PRODUCT = "uGame10" USB_MANUFACTURER = "Radomir Dopieralski" SPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = S25FL216K LONGINT_IMPL = MPZ CHIP_VARIANT = SAMD21E18A diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index a292f9e3d..4de34c975 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -31,6 +31,8 @@ #include "hpl_sercom_config.h" #include "peripheral_clk_config.h" +#include "boards/board.h" +#include "common-hal/microcontroller/Pin.h" #include "hal/include/hal_gpio.h" #include "hal/include/hal_spi_m_sync.h" #include "hal/include/hpl_spi_m_sync.h" @@ -39,6 +41,43 @@ #include "samd/dma.h" #include "samd/sercom.h" +bool never_reset_sercoms[SERCOM_INST_NUM]; + +void never_reset_sercom(Sercom* sercom) { + // Reset all SERCOMs except the ones being used by on-board devices. + Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS; + for (int i = 0; i < SERCOM_INST_NUM; i++) { + if (sercom_instances[i] == sercom) { + never_reset_sercoms[i] = true; + break; + } + } +} + +void reset_sercoms(void) { + // Reset all SERCOMs except the ones being used by on-board devices. + Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS; + for (int i = 0; i < SERCOM_INST_NUM; i++) { + if (never_reset_sercoms[i]) { + continue; + } + #ifdef MICROPY_HW_APA102_SERCOM + if (sercom_instances[i] == MICROPY_HW_APA102_SERCOM) { + continue; + } + #endif + #ifdef CIRCUITPY_DISPLAYIO + // TODO(tannewt): Make this dynamic. + if (sercom_instances[i] == board_display_obj.bus.spi_desc.dev.prvt) { + continue; + } + #endif + // SWRST is same for all modes of SERCOMs. + sercom_instances[i]->SPI.CTRLA.bit.SWRST = 1; + } +} + + void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi, const mcu_pin_obj_t * miso) { @@ -186,6 +225,14 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, spi_m_sync_enable(&self->spi_desc); } +void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { + never_reset_sercom(self->spi_desc.dev.prvt); + + never_reset_pin_number(self->clock_pin); + never_reset_pin_number(self->MOSI_pin); + never_reset_pin_number(self->MISO_pin); +} + bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock_pin == NO_PIN; } diff --git a/ports/atmel-samd/common-hal/busio/SPI.h b/ports/atmel-samd/common-hal/busio/SPI.h index 2fced6d64..56d163a9d 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.h +++ b/ports/atmel-samd/common-hal/busio/SPI.h @@ -42,4 +42,7 @@ typedef struct { uint8_t MISO_pin; } busio_spi_obj_t; +void reset_sercoms(void); + + #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_H diff --git a/ports/atmel-samd/common-hal/microcontroller/Pin.c b/ports/atmel-samd/common-hal/microcontroller/Pin.c index 0f179a6c1..9af5aeaf7 100644 --- a/ports/atmel-samd/common-hal/microcontroller/Pin.c +++ b/ports/atmel-samd/common-hal/microcontroller/Pin.c @@ -43,8 +43,12 @@ bool apa102_mosi_in_use; bool speaker_enable_in_use; #endif +#define PORT_COUNT (PORT_BITS / 32 + 1) + +STATIC uint32_t never_reset_pins[PORT_COUNT]; + void reset_all_pins(void) { - uint32_t pin_mask[PORT_BITS / 32 + 1] = PORT_OUT_IMPLEMENTED; + uint32_t pin_mask[PORT_COUNT] = PORT_OUT_IMPLEMENTED; // Do not full reset USB or SWD lines. pin_mask[0] &= ~(PORT_PA24 | PORT_PA25 | PORT_PA30 | PORT_PA31); @@ -53,6 +57,11 @@ void reset_all_pins(void) { pin_mask[0] &= ~(PORT_PA31); #endif + for (uint32_t i = 0; i < PORT_COUNT; i++) { + pin_mask[i] &= ~(PORT_PA31); + pin_mask[i] &= ~never_reset_pins[i]; + } + gpio_set_port_direction(GPIO_PORTA, pin_mask[0] & ~MICROPY_PORT_A, GPIO_DIRECTION_OFF); gpio_set_port_direction(GPIO_PORTB, pin_mask[1] & ~MICROPY_PORT_B, GPIO_DIRECTION_OFF); #if PORT_BITS > 64 @@ -91,6 +100,10 @@ void reset_all_pins(void) { #endif } +void never_reset_pin_number(uint8_t pin_number) { + never_reset_pins[GPIO_PORT(pin_number)] |= 1 << GPIO_PIN(pin_number); +} + void reset_pin_number(uint8_t pin_number) { if (pin_number >= PORT_BITS) { return; diff --git a/ports/atmel-samd/common-hal/microcontroller/Pin.h b/ports/atmel-samd/common-hal/microcontroller/Pin.h index dcd80997e..f798ea300 100644 --- a/ports/atmel-samd/common-hal/microcontroller/Pin.h +++ b/ports/atmel-samd/common-hal/microcontroller/Pin.h @@ -43,6 +43,7 @@ void reset_all_pins(void); // reset_pin_number takes the pin number instead of the pointer so that objects don't // need to store a full pointer. void reset_pin_number(uint8_t pin_number); +void never_reset_pin_number(uint8_t pin_number); void claim_pin(const mcu_pin_obj_t* pin); #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_MICROCONTROLLER_PIN_H diff --git a/ports/atmel-samd/common-hal/microcontroller/__init__.c b/ports/atmel-samd/common-hal/microcontroller/__init__.c index 746207787..0ca3a0835 100644 --- a/ports/atmel-samd/common-hal/microcontroller/__init__.c +++ b/ports/atmel-samd/common-hal/microcontroller/__init__.c @@ -107,7 +107,6 @@ const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = { // This maps MCU pin names to pin objects. STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = { -// Pins in datasheet order. #if defined(PIN_PA00) && !defined(IGNORE_PIN_PA00) { MP_ROM_QSTR(MP_QSTR_PA00), MP_ROM_PTR(&pin_PA00) }, #endif @@ -120,24 +119,6 @@ STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = { #if defined(PIN_PA03) && !defined(IGNORE_PIN_PA03) { MP_ROM_QSTR(MP_QSTR_PA03), MP_ROM_PTR(&pin_PA03) }, #endif -#if defined(PIN_PB04) && !defined(IGNORE_PIN_PB04) - { MP_ROM_QSTR(MP_QSTR_PB04), MP_ROM_PTR(&pin_PB04) }, -#endif -#if defined(PIN_PB05) && !defined(IGNORE_PIN_PB05) - { MP_ROM_QSTR(MP_QSTR_PB05), MP_ROM_PTR(&pin_PB05) }, -#endif -#if defined(PIN_PB06) && !defined(IGNORE_PIN_PB06) - { MP_ROM_QSTR(MP_QSTR_PB06), MP_ROM_PTR(&pin_PB06) }, -#endif -#if defined(PIN_PB07) && !defined(IGNORE_PIN_PB07) - { MP_ROM_QSTR(MP_QSTR_PB07), MP_ROM_PTR(&pin_PB07) }, -#endif -#if defined(PIN_PB08) && !defined(IGNORE_PIN_PB08) - { MP_ROM_QSTR(MP_QSTR_PB08), MP_ROM_PTR(&pin_PB08) }, -#endif -#if defined(PIN_PB09) && !defined(IGNORE_PIN_PB09) - { MP_ROM_QSTR(MP_QSTR_PB09), MP_ROM_PTR(&pin_PB09) }, -#endif #if defined(PIN_PA04) && !defined(IGNORE_PIN_PA04) { MP_ROM_QSTR(MP_QSTR_PA04), MP_ROM_PTR(&pin_PA04) }, #endif @@ -162,26 +143,6 @@ STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = { #if defined(PIN_PA11) && !defined(IGNORE_PIN_PA11) { MP_ROM_QSTR(MP_QSTR_PA11), MP_ROM_PTR(&pin_PA11) }, #endif -#if defined(PIN_PB10) && !defined(IGNORE_PIN_PB10) - { MP_ROM_QSTR(MP_QSTR_PB10), MP_ROM_PTR(&pin_PB10) }, -#endif -#if defined(PIN_PB11) && !defined(IGNORE_PIN_PB11) - { MP_ROM_QSTR(MP_QSTR_PB11), MP_ROM_PTR(&pin_PB11) }, -#endif -#if defined(PIN_PB12) && !defined(IGNORE_PIN_PB12) - { MP_ROM_QSTR(MP_QSTR_PB12), MP_ROM_PTR(&pin_PB12) }, -#endif -#if defined(PIN_PB13) && !defined(IGNORE_PIN_PB13) - { MP_ROM_QSTR(MP_QSTR_PB13), MP_ROM_PTR(&pin_PB13) }, -#endif -#if defined(PIN_PB14) && !defined(IGNORE_PIN_PB14) - { MP_ROM_QSTR(MP_QSTR_PB14), MP_ROM_PTR(&pin_PB14) }, -#endif - -// Second page. -#if defined(PIN_PB15) && !defined(IGNORE_PIN_PB15) - { MP_ROM_QSTR(MP_QSTR_PB15), MP_ROM_PTR(&pin_PB15) }, -#endif #if defined(PIN_PA12) && !defined(IGNORE_PIN_PA12) { MP_ROM_QSTR(MP_QSTR_PA12), MP_ROM_PTR(&pin_PA12) }, #endif @@ -206,12 +167,6 @@ STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = { #if defined(PIN_PA19) && !defined(IGNORE_PIN_PA19) { MP_ROM_QSTR(MP_QSTR_PA19), MP_ROM_PTR(&pin_PA19) }, #endif -#if defined(PIN_PB16) && !defined(IGNORE_PIN_PB16) - { MP_ROM_QSTR(MP_QSTR_PB16), MP_ROM_PTR(&pin_PB16) }, -#endif -#if defined(PIN_PB17) && !defined(IGNORE_PIN_PB17) - { MP_ROM_QSTR(MP_QSTR_PB17), MP_ROM_PTR(&pin_PB17) }, -#endif #if defined(PIN_PA20) && !defined(IGNORE_PIN_PA20) { MP_ROM_QSTR(MP_QSTR_PA20), MP_ROM_PTR(&pin_PA20) }, #endif @@ -230,12 +185,6 @@ STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = { #if defined(PIN_PA25) && !defined(IGNORE_PIN_PA25) { MP_ROM_QSTR(MP_QSTR_PA25), MP_ROM_PTR(&pin_PA25) }, #endif -#if defined(PIN_PB22) && !defined(IGNORE_PIN_PB22) - { MP_ROM_QSTR(MP_QSTR_PB22), MP_ROM_PTR(&pin_PB22) }, -#endif -#if defined(PIN_PB23) && !defined(IGNORE_PIN_PB23) - { MP_ROM_QSTR(MP_QSTR_PB23), MP_ROM_PTR(&pin_PB23) }, -#endif #if defined(PIN_PA27) && !defined(IGNORE_PIN_PA27) { MP_ROM_QSTR(MP_QSTR_PA27), MP_ROM_PTR(&pin_PA27) }, #endif @@ -248,12 +197,7 @@ STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = { #if defined(PIN_PA31) && !defined(IGNORE_PIN_PA31) { MP_ROM_QSTR(MP_QSTR_PA31), MP_ROM_PTR(&pin_PA31) }, #endif -#if defined(PIN_PB30) && !defined(IGNORE_PIN_PB30) - { MP_ROM_QSTR(MP_QSTR_PB30), MP_ROM_PTR(&pin_PB30) }, -#endif -#if defined(PIN_PB31) && !defined(IGNORE_PIN_PB31) - { MP_ROM_QSTR(MP_QSTR_PB31), MP_ROM_PTR(&pin_PB31) }, -#endif + #if defined(PIN_PB00) && !defined(IGNORE_PIN_PB00) { MP_ROM_QSTR(MP_QSTR_PB00), MP_ROM_PTR(&pin_PB00) }, #endif @@ -266,14 +210,176 @@ STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = { #if defined(PIN_PB03) && !defined(IGNORE_PIN_PB03) { MP_ROM_QSTR(MP_QSTR_PB03), MP_ROM_PTR(&pin_PB03) }, #endif +#if defined(PIN_PB04) && !defined(IGNORE_PIN_PB04) + { MP_ROM_QSTR(MP_QSTR_PB04), MP_ROM_PTR(&pin_PB04) }, +#endif +#if defined(PIN_PB05) && !defined(IGNORE_PIN_PB05) + { MP_ROM_QSTR(MP_QSTR_PB05), MP_ROM_PTR(&pin_PB05) }, +#endif +#if defined(PIN_PB06) && !defined(IGNORE_PIN_PB06) + { MP_ROM_QSTR(MP_QSTR_PB06), MP_ROM_PTR(&pin_PB06) }, +#endif +#if defined(PIN_PB07) && !defined(IGNORE_PIN_PB07) + { MP_ROM_QSTR(MP_QSTR_PB07), MP_ROM_PTR(&pin_PB07) }, +#endif +#if defined(PIN_PB08) && !defined(IGNORE_PIN_PB08) + { MP_ROM_QSTR(MP_QSTR_PB08), MP_ROM_PTR(&pin_PB08) }, +#endif +#if defined(PIN_PB09) && !defined(IGNORE_PIN_PB09) + { MP_ROM_QSTR(MP_QSTR_PB09), MP_ROM_PTR(&pin_PB09) }, +#endif +#if defined(PIN_PB10) && !defined(IGNORE_PIN_PB10) + { MP_ROM_QSTR(MP_QSTR_PB10), MP_ROM_PTR(&pin_PB10) }, +#endif +#if defined(PIN_PB11) && !defined(IGNORE_PIN_PB11) + { MP_ROM_QSTR(MP_QSTR_PB11), MP_ROM_PTR(&pin_PB11) }, +#endif +#if defined(PIN_PB12) && !defined(IGNORE_PIN_PB12) + { MP_ROM_QSTR(MP_QSTR_PB12), MP_ROM_PTR(&pin_PB12) }, +#endif +#if defined(PIN_PB13) && !defined(IGNORE_PIN_PB13) + { MP_ROM_QSTR(MP_QSTR_PB13), MP_ROM_PTR(&pin_PB13) }, +#endif +#if defined(PIN_PB14) && !defined(IGNORE_PIN_PB14) + { MP_ROM_QSTR(MP_QSTR_PB14), MP_ROM_PTR(&pin_PB14) }, +#endif +#if defined(PIN_PB15) && !defined(IGNORE_PIN_PB15) + { MP_ROM_QSTR(MP_QSTR_PB15), MP_ROM_PTR(&pin_PB15) }, +#endif +#if defined(PIN_PB16) && !defined(IGNORE_PIN_PB16) + { MP_ROM_QSTR(MP_QSTR_PB16), MP_ROM_PTR(&pin_PB16) }, +#endif +#if defined(PIN_PB17) && !defined(IGNORE_PIN_PB17) + { MP_ROM_QSTR(MP_QSTR_PB17), MP_ROM_PTR(&pin_PB17) }, +#endif +#if defined(PIN_PB22) && !defined(IGNORE_PIN_PB22) + { MP_ROM_QSTR(MP_QSTR_PB22), MP_ROM_PTR(&pin_PB22) }, +#endif +#if defined(PIN_PB23) && !defined(IGNORE_PIN_PB23) + { MP_ROM_QSTR(MP_QSTR_PB23), MP_ROM_PTR(&pin_PB23) }, +#endif +#if defined(PIN_PB30) && !defined(IGNORE_PIN_PB30) + { MP_ROM_QSTR(MP_QSTR_PB30), MP_ROM_PTR(&pin_PB30) }, +#endif +#if defined(PIN_PB31) && !defined(IGNORE_PIN_PB31) + { MP_ROM_QSTR(MP_QSTR_PB31), MP_ROM_PTR(&pin_PB31) }, +#endif + +// These are SAMD51 specific so we assume we want them in RAM +#if defined(PIN_PC00) + { MP_ROM_QSTR(MP_QSTR_PC00), MP_ROM_PTR(&pin_PC00) }, +#endif +#if defined(PIN_PC01) + { MP_ROM_QSTR(MP_QSTR_PC01), MP_ROM_PTR(&pin_PC01) }, +#endif +#if defined(PIN_PC02) + { MP_ROM_QSTR(MP_QSTR_PC02), MP_ROM_PTR(&pin_PC02) }, +#endif +#if defined(PIN_PC03) + { MP_ROM_QSTR(MP_QSTR_PC03), MP_ROM_PTR(&pin_PC03) }, +#endif +#if defined(PIN_PC04) + { MP_ROM_QSTR(MP_QSTR_PC04), MP_ROM_PTR(&pin_PC04) }, +#endif +#if defined(PIN_PC05) + { MP_ROM_QSTR(MP_QSTR_PC05), MP_ROM_PTR(&pin_PC05) }, +#endif +#if defined(PIN_PC06) + { MP_ROM_QSTR(MP_QSTR_PC06), MP_ROM_PTR(&pin_PC06) }, +#endif +#if defined(PIN_PC07) + { MP_ROM_QSTR(MP_QSTR_PC07), MP_ROM_PTR(&pin_PC07) }, +#endif +#if defined(PIN_PC10) + { MP_ROM_QSTR(MP_QSTR_PC10), MP_ROM_PTR(&pin_PC10) }, +#endif +#if defined(PIN_PC11) + { MP_ROM_QSTR(MP_QSTR_PC11), MP_ROM_PTR(&pin_PC11) }, +#endif +#if defined(PIN_PC12) + { MP_ROM_QSTR(MP_QSTR_PC12), MP_ROM_PTR(&pin_PC12) }, +#endif +#if defined(PIN_PC13) + { MP_ROM_QSTR(MP_QSTR_PC13), MP_ROM_PTR(&pin_PC13) }, +#endif +#if defined(PIN_PC14) + { MP_ROM_QSTR(MP_QSTR_PC14), MP_ROM_PTR(&pin_PC14) }, +#endif +#if defined(PIN_PC15) + { MP_ROM_QSTR(MP_QSTR_PC15), MP_ROM_PTR(&pin_PC15) }, +#endif #if defined(PIN_PC16) { MP_ROM_QSTR(MP_QSTR_PC16), MP_ROM_PTR(&pin_PC16) }, #endif +#if defined(PIN_PC17) + { MP_ROM_QSTR(MP_QSTR_PC17), MP_ROM_PTR(&pin_PC17) }, +#endif #if defined(PIN_PC18) { MP_ROM_QSTR(MP_QSTR_PC18), MP_ROM_PTR(&pin_PC18) }, #endif #if defined(PIN_PC19) { MP_ROM_QSTR(MP_QSTR_PC19), MP_ROM_PTR(&pin_PC19) }, #endif +#if defined(PIN_PC20) + { MP_ROM_QSTR(MP_QSTR_PC20), MP_ROM_PTR(&pin_PC20) }, +#endif +#if defined(PIN_PC21) + { MP_ROM_QSTR(MP_QSTR_PC21), MP_ROM_PTR(&pin_PC21) }, +#endif +#if defined(PIN_PC22) + { MP_ROM_QSTR(MP_QSTR_PC22), MP_ROM_PTR(&pin_PC22) }, +#endif +#if defined(PIN_PC23) + { MP_ROM_QSTR(MP_QSTR_PC23), MP_ROM_PTR(&pin_PC23) }, +#endif +#if defined(PIN_PC24) + { MP_ROM_QSTR(MP_QSTR_PC24), MP_ROM_PTR(&pin_PC24) }, +#endif +#if defined(PIN_PC25) + { MP_ROM_QSTR(MP_QSTR_PC25), MP_ROM_PTR(&pin_PC25) }, +#endif +#if defined(PIN_PC26) + { MP_ROM_QSTR(MP_QSTR_PC26), MP_ROM_PTR(&pin_PC26) }, +#endif +#if defined(PIN_PC27) + { MP_ROM_QSTR(MP_QSTR_PC27), MP_ROM_PTR(&pin_PC27) }, +#endif +#if defined(PIN_PC28) + { MP_ROM_QSTR(MP_QSTR_PC28), MP_ROM_PTR(&pin_PC28) }, +#endif +#if defined(PIN_PC30) + { MP_ROM_QSTR(MP_QSTR_PC30), MP_ROM_PTR(&pin_PC30) }, +#endif +#if defined(PIN_PC31) + { MP_ROM_QSTR(MP_QSTR_PC31), MP_ROM_PTR(&pin_PC31) }, +#endif + +#if defined(PIN_PD00) + { MP_ROM_QSTR(MP_QSTR_PD00), MP_ROM_PTR(&pin_PD00) }, +#endif +#if defined(PIN_PD01) + { MP_ROM_QSTR(MP_QSTR_PD01), MP_ROM_PTR(&pin_PD01) }, +#endif +#if defined(PIN_PD08) + { MP_ROM_QSTR(MP_QSTR_PD08), MP_ROM_PTR(&pin_PD08) }, +#endif +#if defined(PIN_PD09) + { MP_ROM_QSTR(MP_QSTR_PD09), MP_ROM_PTR(&pin_PD09) }, +#endif +#if defined(PIN_PD10) + { MP_ROM_QSTR(MP_QSTR_PD10), MP_ROM_PTR(&pin_PD10) }, +#endif +#if defined(PIN_PD11) + { MP_ROM_QSTR(MP_QSTR_PD11), MP_ROM_PTR(&pin_PD11) }, +#endif +#if defined(PIN_PD12) + { MP_ROM_QSTR(MP_QSTR_PD12), MP_ROM_PTR(&pin_PD12) }, +#endif +#if defined(PIN_PD20) + { MP_ROM_QSTR(MP_QSTR_PD20), MP_ROM_PTR(&pin_PD20) }, +#endif +#if defined(PIN_PD21) + { MP_ROM_QSTR(MP_QSTR_PD21), MP_ROM_PTR(&pin_PD21) }, +#endif }; MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_global_dict_table); diff --git a/ports/atmel-samd/common-hal/storage/__init__.c b/ports/atmel-samd/common-hal/storage/__init__.c deleted file mode 100644 index 501a19dd7..000000000 --- a/ports/atmel-samd/common-hal/storage/__init__.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 - -#include "flash_api.h" -#include "py/mperrno.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/__init__.h" -#include "shared-bindings/storage/__init__.h" -#include "supervisor/filesystem.h" -#include "supervisor/shared/translate.h" -#include "usb.h" - -extern volatile bool mp_msc_enabled; - -void common_hal_storage_remount(const char* mount_path, bool readonly) { - if (strcmp(mount_path, "/") != 0) { - mp_raise_OSError(MP_EINVAL); - } - - // TODO(dhalbert): is this is a good enough check? It checks for - // CDC enabled. There is no "MSC enabled" check. - if (usb_connected()) { - mp_raise_RuntimeError(translate("Cannot remount '/' when USB is active.")); - } - - flash_set_usb_writable(readonly); -} - -void common_hal_storage_erase_filesystem(void) { - filesystem_init(false, true); // Force a re-format. - common_hal_mcu_reset(); - // We won't actually get here, since we're resetting. -} diff --git a/ports/atmel-samd/common-hal/supervisor/Runtime.c b/ports/atmel-samd/common-hal/supervisor/Runtime.c index 2636fe646..ea663f897 100755 --- a/ports/atmel-samd/common-hal/supervisor/Runtime.c +++ b/ports/atmel-samd/common-hal/supervisor/Runtime.c @@ -26,13 +26,12 @@ #include #include "shared-bindings/supervisor/Runtime.h" -#include "usb.h" +#include "supervisor/serial.h" bool common_hal_get_serial_connected(void) { - return (bool) usb_connected(); + return (bool) serial_connected(); } bool common_hal_get_serial_bytes_available(void) { - return (bool) usb_bytes_available(); + return (bool) serial_bytes_available(); } - diff --git a/ports/atmel-samd/common-hal/usb_hid/Device.c b/ports/atmel-samd/common-hal/usb_hid/Device.c deleted file mode 100644 index 31bace018..000000000 --- a/ports/atmel-samd/common-hal/usb_hid/Device.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 - -#include "common-hal/usb_hid/Device.h" - -#include "py/runtime.h" -#include "shared-bindings/microcontroller/__init__.h" -#include "shared-bindings/usb_hid/Device.h" -#include "supervisor/shared/translate.h" -#include "genhdr/autogen_usb_descriptor.h" - -#include "tick.h" - -#include "usb/class/hid/device/hiddf_generic.h" - -static uint32_t usb_hid_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { - - int32_t status; - - // Don't get stuck if USB fails in some way; timeout after a while. - uint64_t end_ticks = ticks_ms + 2000; - - while (ticks_ms < end_ticks) { - status = usb_d_ep_get_status(self->endpoint, NULL); - if (status == USB_BUSY) { - continue; - } - if (status == USB_OK) { - break; - } - // Some error. Give up. - return status; - } - - // Copy the data only when endpoint is ready to send. The previous - // buffer load gets zero'd out when transaction completes, so if - // you copy before it's ready, only zeros will get sent. - - // Prefix with a report id if one is supplied. - if (self->report_id > 0) { - self->report_buffer[0] = self->report_id; - memcpy(&(self->report_buffer[1]), report, len); - return hiddf_generic_write(self->report_buffer, len + 1); - } else { - memcpy(self->report_buffer, report, len); - return hiddf_generic_write(self->report_buffer, len); - } - -} - -void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { - if (len != self->report_length) { - mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->report_length); - } - int32_t status = usb_hid_send_report(self, report, len); - if (status != ERR_NONE) { - mp_raise_msg(&mp_type_OSError, status == USB_BUSY ? translate("USB Busy") : translate("USB Error")); - } -} - -uint8_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self) { - return self->usage_page; -} - -uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) { - return self->usage; -} - - -void usb_hid_init() { -} - -void usb_hid_reset() { - // We don't actually reset. We just set a report that is empty to prevent - // long keypresses and such. - uint8_t report[USB_HID_MAX_REPORT_LENGTH] = {0}; - - for (size_t i = 0; i < USB_HID_NUM_DEVICES; i++) { - usb_hid_send_report(&usb_hid_devices[i], report, usb_hid_devices[i].report_length); - } -} diff --git a/ports/atmel-samd/common-hal/usb_hid/Device.h b/ports/atmel-samd/common-hal/usb_hid/Device.h deleted file mode 100644 index b81c0e709..000000000 --- a/ports/atmel-samd/common-hal/usb_hid/Device.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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. - */ - -#ifndef COMMON_HAL_USB_HID_DEVICE_H -#define COMMON_HAL_USB_HID_DEVICE_H - -#include -#include - -#include "py/obj.h" - -#include "genhdr/autogen_usb_descriptor.h" - -typedef struct { - mp_obj_base_t base; - uint8_t* report_buffer; - uint8_t endpoint; - uint8_t report_id; // If non-zero, prefix report with given id. - uint8_t report_length; // Length not including Report ID. - uint8_t usage_page; - uint8_t usage; - -} usb_hid_device_obj_t; - -extern usb_hid_device_obj_t usb_hid_devices[USB_HID_NUM_DEVICES]; - -void usb_hid_init(void); -void usb_hid_reset(void); - -#endif // COMMON_HAL_USB_HID_DEVICE_H diff --git a/ports/atmel-samd/common-hal/usb_hid/__init__.c b/ports/atmel-samd/common-hal/usb_hid/__init__.c deleted file mode 100644 index 770d221c7..000000000 --- a/ports/atmel-samd/common-hal/usb_hid/__init__.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "py/obj.h" -#include "py/mphal.h" -#include "py/runtime.h" - -#include "common-hal/usb_hid/Device.h" - -#include "shared-bindings/usb_hid/Device.h" - -#include "genhdr/autogen_usb_descriptor.h" - -// Buffers are report size + 1 to include the Report ID prefix byte if needed. -#ifdef USB_HID_REPORT_ID_KEYBOARD -static uint8_t keyboard_report_buffer[USB_HID_REPORT_LENGTH_KEYBOARD + 1]; -#endif -#ifdef USB_HID_REPORT_ID_MOUSE -static uint8_t mouse_report_buffer[USB_HID_REPORT_LENGTH_MOUSE + 1]; -#endif -#ifdef USB_HID_REPORT_ID_CONSUMER -static uint8_t consumer_report_buffer[USB_HID_REPORT_LENGTH_CONSUMER + 1]; -#endif -#ifdef USB_HID_REPORT_ID_SYS_CONTROL -static uint8_t sys_control_report_buffer[USB_HID_REPORT_LENGTH_SYS_CONTROL + 1]; -#endif -#ifdef USB_HID_REPORT_ID_GAMEPAD -static uint8_t gamepad_report_buffer[USB_HID_REPORT_LENGTH_GAMEPAD + 1]; -#endif -#ifdef USB_HID_REPORT_ID_DIGITIZER -static uint8_t digitizer_report_buffer[USB_HID_REPORT_LENGTH_DIGITIZER + 1]; -#endif - -usb_hid_device_obj_t usb_hid_devices[USB_HID_NUM_DEVICES] = { -#ifdef USB_HID_REPORT_ID_KEYBOARD - { - .base = { .type = &usb_hid_device_type }, - .report_buffer = keyboard_report_buffer, - .endpoint = USB_HID_ENDPOINT_IN, - .report_id = USB_HID_REPORT_ID_KEYBOARD, - .report_length = USB_HID_REPORT_LENGTH_KEYBOARD, - .usage_page = 0x01, - .usage = 0x06, - }, -#endif -#ifdef USB_HID_REPORT_ID_MOUSE - { - .base = { .type = &usb_hid_device_type }, - .report_buffer = mouse_report_buffer, - .endpoint = USB_HID_ENDPOINT_IN, - .report_id = USB_HID_REPORT_ID_MOUSE, - .report_length = USB_HID_REPORT_LENGTH_MOUSE, - .usage_page = 0x01, - .usage = 0x02, - }, -#endif -#ifdef USB_HID_REPORT_ID_CONSUMER - { - .base = { .type = &usb_hid_device_type }, - .report_buffer = consumer_report_buffer, - .endpoint = USB_HID_ENDPOINT_IN, - .report_id = USB_HID_REPORT_ID_CONSUMER, - .report_length = USB_HID_REPORT_LENGTH_CONSUMER, - .usage_page = 0x0C, - .usage = 0x01, - }, -#endif -#ifdef USB_HID_REPORT_ID_SYS_CONTROL - { - .base = { .type = &usb_hid_device_type }, - .report_buffer = sys_control_report_buffer, - .endpoint = USB_HID_ENDPOINT_IN, - .report_id = USB_HID_REPORT_ID_SYS_CONTROL, - .report_length = USB_HID_REPORT_LENGTH_SYS_CONTROL, - .usage_page = 0x01, - .usage = 0x80, - }, -#endif -#ifdef USB_HID_REPORT_ID_GAMEPAD - { - .base = { .type = &usb_hid_device_type }, - .report_buffer = gamepad_report_buffer, - .endpoint = USB_HID_ENDPOINT_IN, - .report_id = USB_HID_REPORT_ID_GAMEPAD, - .report_length = USB_HID_REPORT_LENGTH_GAMEPAD, - .usage_page = 0x01, - .usage = 0x05, - }, -#endif -#ifdef USB_HID_REPORT_ID_DIGITIZER - { - .base = { .type = &usb_hid_device_type }, - .report_buffer = digitizer_report_buffer, - .endpoint = USB_HID_ENDPOINT_IN, - .report_id = USB_HID_REPORT_ID_DIGITIZER, - .report_length = USB_HID_REPORT_LENGTH_DIGITIZER, - .usage_page = 0x0D, - .usage = 0x02, - }, -#endif -}; - - -mp_obj_tuple_t common_hal_usb_hid_devices = { - .base = { - .type = &mp_type_tuple, - }, - .len = USB_HID_NUM_DEVICES, - .items = { -#if USB_HID_NUM_DEVICES >= 1 - (mp_obj_t) &usb_hid_devices[0], -#endif -#if USB_HID_NUM_DEVICES >= 2 - (mp_obj_t) &usb_hid_devices[1], -#endif -#if USB_HID_NUM_DEVICES >= 3 - (mp_obj_t) &usb_hid_devices[2], -#endif -#if USB_HID_NUM_DEVICES >= 4 - (mp_obj_t) &usb_hid_devices[3], -#endif -#if USB_HID_NUM_DEVICES >= 5 - (mp_obj_t) &usb_hid_devices[4], -#endif -#if USB_HID_NUM_DEVICES >= 6 - (mp_obj_t) &usb_hid_devices[5], -#endif - } -}; diff --git a/ports/atmel-samd/external_flash/common_commands.h b/ports/atmel-samd/external_flash/common_commands.h deleted file mode 100644 index d82d2c046..000000000 --- a/ports/atmel-samd/external_flash/common_commands.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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. - */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H -#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H - -#define CMD_READ_JEDEC_ID 0x9f -#define CMD_READ_DATA 0x03 -#define CMD_SECTOR_ERASE 0x20 -// #define CMD_SECTOR_ERASE CMD_READ_JEDEC_ID -#define CMD_DISABLE_WRITE 0x04 -#define CMD_ENABLE_WRITE 0x06 -#define CMD_PAGE_PROGRAM 0x02 -// #define CMD_PAGE_PROGRAM CMD_READ_JEDEC_ID -#define CMD_READ_STATUS 0x05 -#define CMD_READ_STATUS2 0x35 -#define CMD_WRITE_STATUS_BYTE1 0x01 -#define CMD_WRITE_STATUS_BYTE2 0x31 -#define CMD_DUAL_READ 0x3b -#define CMD_QUAD_READ 0x6b -#define CMD_ENABLE_RESET 0x66 -#define CMD_RESET 0x99 - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H diff --git a/ports/atmel-samd/external_flash/devices.h b/ports/atmel-samd/external_flash/devices.h deleted file mode 100644 index 58da2e509..000000000 --- a/ports/atmel-samd/external_flash/devices.h +++ /dev/null @@ -1,337 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC - * - * 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. - */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H -#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H - -#include -#include - -typedef struct { - uint32_t total_size; - uint16_t start_up_time_us; - - // Three response bytes to 0x9f JEDEC ID command. - uint8_t manufacturer_id; - uint8_t memory_type; - uint8_t capacity; - - // Max clock speed for all operations and the fastest read mode. - uint8_t max_clock_speed_mhz; - bool has_sector_protection : 1; - - // Supports the 0x0b fast read command with 8 dummy cycles. - bool supports_fast_read : 1; - - // Supports the fast read, quad output command 0x6b with 8 dummy cycles. - bool supports_qspi : 1; - - // Requires quad enable set in status bit 9. - bool has_quad_enable : 1; - - // Supports the quad input page program command 0x32. This is known as 1-1-4 because it only - // uses all four lines for data. - bool supports_qspi_writes: 1; - - // Requires a separate command 0x31 to write to the second byte of the status register. - // Otherwise two byte are written via 0x01. - bool write_status_register_split: 1; -} external_flash_device; - -// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21 -// Xplained board. -// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf -#define AT25DF081A {\ - .total_size = (1 << 20), /* 1 MiB */ \ - .start_up_time_us = 10000, \ - .manufacturer_id = 0x1f, \ - .memory_type = 0x45, \ - .capacity = 0x01, \ - .max_clock_speed_mhz = 85, \ - .has_sector_protection = true, \ - .supports_fast_read = true, \ - .supports_qspi = false, \ - .has_quad_enable = false, \ - .supports_qspi_writes = false, \ - .write_status_register_split = false, \ -} - -// Settings for the Gigadevice GD25Q16C 2MiB SPI flash. -// Datasheet: http://www.gigadevice.com/wp-content/uploads/2017/12/DS-00086-GD25Q16C-Rev2.6.pdf -#define GD25Q16C {\ - .total_size = (1 << 21), /* 2 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xc8, \ - .memory_type = 0x40, \ - .capacity = 0x15, \ - .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Gigadevice GD25Q64C 8MiB SPI flash. -// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf -#define GD25Q64C {\ - .total_size = (1 << 23), /* 8 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xc8, \ - .memory_type = 0x40, \ - .capacity = 0x17, \ - .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = true, \ -} - -// Settings for the Cypress (was Spansion) S25FL064L 8MiB SPI flash. -// Datasheet: http://www.cypress.com/file/316661/download -#define S25FL064L {\ - .total_size = (1 << 23), /* 8 MiB */ \ - .start_up_time_us = 300, \ - .manufacturer_id = 0x01, \ - .memory_type = 0x60, \ - .capacity = 0x17, \ - .max_clock_speed_mhz = 108, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Cypress (was Spansion) S25FL116K 2MiB SPI flash. -// Datasheet: http://www.cypress.com/file/196886/download -#define S25FL116K {\ - .total_size = (1 << 21), /* 2 MiB */ \ - .start_up_time_us = 10000, \ - .manufacturer_id = 0x01, \ - .memory_type = 0x40, \ - .capacity = 0x15, \ - .max_clock_speed_mhz = 108, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = false, \ - .write_status_register_split = false, \ -} - -// Settings for the Cypress (was Spansion) S25FL216K 2MiB SPI flash. -// Datasheet: http://www.cypress.com/file/197346/download -#define S25FL216K {\ - .total_size = (1 << 21), /* 2 MiB */ \ - .start_up_time_us = 10000, \ - .manufacturer_id = 0x01, \ - .memory_type = 0x40, \ - .capacity = 0x15, \ - .max_clock_speed_mhz = 65, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = false, \ - .has_quad_enable = false, \ - .supports_qspi_writes = false, \ - .write_status_register_split = false, \ -} - -// Settings for the Winbond W25Q16FW 2MiB SPI flash. -// Datasheet: https://www.winbond.com/resource-files/w25q16fw%20revj%2005182017%20sfdp.pdf -#define W25Q16FW {\ - .total_size = (1 << 21), /* 2 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x60, \ - .capacity = 0x15, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) -// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf -#define W25Q16JV_IQ {\ - .total_size = (1 << 21), /* 2 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x40, \ - .capacity = 0x15, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Winbond W25Q16JV-IM 2MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40) -// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf -#define W25Q16JV_IM {\ - .total_size = (1 << 21), /* 2 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x70, \ - .capacity = 0x15, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Winbond W25Q32BV 4MiB SPI flash. -// Datasheet: https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf -#define W25Q32BV {\ - .total_size = (1 << 22), /* 4 MiB */ \ - .start_up_time_us = 10000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x60, \ - .capacity = 0x16, \ - .max_clock_speed_mhz = 104, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = false, \ - .write_status_register_split = false, \ -} -// Settings for the Winbond W25Q32JV-IM 4MiB SPI flash. -// Datasheet: https://www.winbond.com/resource-files/w25q32jv%20revg%2003272018%20plus.pdf -#define W25Q32JV_IM {\ - .total_size = (1 << 22), /* 4 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x70, \ - .capacity = 0x16, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40) -// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf -#define W25Q64JV_IM {\ - .total_size = (1 << 23), /* 8 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x70, \ - .capacity = 0x17, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Winbond W25Q64JV-IQ 8MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) -// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf -#define W25Q64JV_IQ {\ - .total_size = (1 << 23), /* 8 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x40, \ - .capacity = 0x17, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -// Settings for the Winbond W25Q80DL 1MiB SPI flash. -// Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf -#define W25Q80DL {\ - .total_size = (1 << 20), /* 1 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x60, \ - .capacity = 0x14, \ - .max_clock_speed_mhz = 104, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = false, \ - .write_status_register_split = false, \ -} - - -// Settings for the Winbond W25Q128JV-SQ 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) -// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf -#define W25Q128JV_SQ {\ - .total_size = (1 << 24), /* 16 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x40, \ - .capacity = 0x18, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - - -// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) -// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf -#define W25Q128JV_PM {\ - .total_size = (1 << 24), /* 16 MiB */ \ - .start_up_time_us = 5000, \ - .manufacturer_id = 0xef, \ - .memory_type = 0x70, \ - .capacity = 0x18, \ - .max_clock_speed_mhz = 133, \ - .has_sector_protection = false, \ - .supports_fast_read = true, \ - .supports_qspi = true, \ - .has_quad_enable = true, \ - .supports_qspi_writes = true, \ - .write_status_register_split = false, \ -} - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H diff --git a/ports/atmel-samd/external_flash/external_flash.c b/ports/atmel-samd/external_flash/external_flash.c deleted file mode 100644 index b4a637be4..000000000 --- a/ports/atmel-samd/external_flash/external_flash.c +++ /dev/null @@ -1,699 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "external_flash.h" - -#include -#include - -#include "external_flash/spi_flash_api.h" -#include "external_flash/common_commands.h" -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "py/misc.h" -#include "py/obj.h" -#include "py/runtime.h" -#include "lib/oofatfs/ff.h" -#include "shared-bindings/microcontroller/__init__.h" -#include "supervisor/memory.h" -#include "supervisor/shared/rgb_led_status.h" - -#include "hal_gpio.h" -#include "hal_spi_m_sync.h" - -#define SPI_FLASH_PART1_START_BLOCK (0x1) - -#define NO_SECTOR_LOADED 0xFFFFFFFF - -struct spi_m_sync_descriptor spi_flash_desc; - -// The currently cached sector in the cache, ram or flash based. -static uint32_t current_sector; - -const external_flash_device possible_devices[EXTERNAL_FLASH_DEVICE_COUNT] = {EXTERNAL_FLASH_DEVICES}; - -static const external_flash_device* flash_device = NULL; - -// Track which blocks (up to 32) in the current sector currently live in the -// cache. -static uint32_t dirty_mask; - -static supervisor_allocation* supervisor_cache = NULL; - -// Wait until both the write enable and write in progress bits have cleared. -static bool wait_for_flash_ready(void) { - uint8_t read_status_response[1] = {0x00}; - bool ok = true; - // Both the write enable and write in progress bits should be low. - do { - ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); - } while (ok && (read_status_response[0] & 0x3) != 0); - return ok; -} - -// Turn on the write enable bit so we can program and erase the flash. -static bool write_enable(void) { - return spi_flash_command(CMD_ENABLE_WRITE); -} - -// Read data_length's worth of bytes starting at address into data. -static bool read_flash(uint32_t address, uint8_t* data, uint32_t data_length) { - if (flash_device == NULL) { - return false; - } - if (!wait_for_flash_ready()) { - return false; - } - return spi_flash_read_data(address, data, data_length); -} - -// Writes data_length's worth of bytes starting at address from data. Assumes -// that the sector that address resides in has already been erased. So make sure -// to run erase_sector. -static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_length) { - if (flash_device == NULL) { - return false; - } - // Don't bother writing if the data is all 1s. Thats equivalent to the flash - // state after an erase. - bool all_ones = true; - for (uint16_t i = 0; i < data_length; i++) { - if (data[i] != 0xff) { - all_ones = false; - break; - } - } - if (all_ones) { - return true; - } - - for (uint32_t bytes_written = 0; - bytes_written < data_length; - bytes_written += SPI_FLASH_PAGE_SIZE) { - if (!wait_for_flash_ready() || !write_enable()) { - return false; - } - - if (!spi_flash_write_data(address + bytes_written, (uint8_t*) data + bytes_written, - SPI_FLASH_PAGE_SIZE)) { - return false; - } - } - return true; -} - -static bool page_erased(uint32_t sector_address) { - // Check the first few bytes to catch the common case where there is data - // without using a bunch of memory. - uint8_t short_buffer[4]; - if (read_flash(sector_address, short_buffer, 4)) { - for (uint16_t i = 0; i < 4; i++) { - if (short_buffer[i] != 0xff) { - return false; - } - } - } else { - return false; - } - - // Now check the full length. - uint8_t full_buffer[FILESYSTEM_BLOCK_SIZE]; - if (read_flash(sector_address, full_buffer, FILESYSTEM_BLOCK_SIZE)) { - for (uint16_t i = 0; i < FILESYSTEM_BLOCK_SIZE; i++) { - if (short_buffer[i] != 0xff) { - return false; - } - } - } else { - return false; - } - return true; -} - -// Erases the given sector. Make sure you copied all of the data out of it you -// need! Also note, sector_address is really 24 bits. -static bool erase_sector(uint32_t sector_address) { - // Before we erase the sector we need to wait for any writes to finish and - // and then enable the write again. - if (!wait_for_flash_ready() || !write_enable()) { - return false; - } - - spi_flash_sector_command(CMD_SECTOR_ERASE, sector_address); - return true; -} - -// Sector is really 24 bits. -static bool copy_block(uint32_t src_address, uint32_t dest_address) { - // Copy page by page to minimize RAM buffer. - uint16_t page_size = SPI_FLASH_PAGE_SIZE; - uint8_t buffer[page_size]; - for (uint32_t i = 0; i < FILESYSTEM_BLOCK_SIZE / page_size; i++) { - if (!read_flash(src_address + i * page_size, buffer, page_size)) { - return false; - } - if (!write_flash(dest_address + i * page_size, buffer, page_size)) { - return false; - } - } - return true; -} - -void external_flash_init(void) { - if (flash_device != NULL) { - return; - } - - // Delay to give the SPI Flash time to get going. - // TODO(tannewt): Only do this when we know power was applied vs a reset. - uint16_t max_start_up_delay_us = 0; - for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { - if (possible_devices[i].start_up_time_us > max_start_up_delay_us) { - max_start_up_delay_us = possible_devices[i].start_up_time_us; - } - } - common_hal_mcu_delay_us(max_start_up_delay_us); - - spi_flash_init(); - - // The response will be 0xff if the flash needs more time to start up. - uint8_t jedec_id_response[3] = {0xff, 0xff, 0xff}; - while (jedec_id_response[0] == 0xff) { - spi_flash_read_command(CMD_READ_JEDEC_ID, jedec_id_response, 3); - } - - for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { - const external_flash_device* possible_device = &possible_devices[i]; - if (jedec_id_response[0] == possible_device->manufacturer_id && - jedec_id_response[1] == possible_device->memory_type && - jedec_id_response[2] == possible_device->capacity) { - flash_device = possible_device; - break; - } - } - - if (flash_device == NULL) { - return; - } - - // We don't know what state the flash is in so wait for any remaining writes and then reset. - uint8_t read_status_response[1] = {0x00}; - // The write in progress bit should be low. - do { - spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); - } while ((read_status_response[0] & 0x1) != 0); - // The suspended write/erase bit should be low. - do { - spi_flash_read_command(CMD_READ_STATUS2, read_status_response, 1); - } while ((read_status_response[0] & 0x80) != 0); - - - spi_flash_command(CMD_ENABLE_RESET); - spi_flash_command(CMD_RESET); - - // Wait 30us for the reset - common_hal_mcu_delay_us(30); - - spi_flash_init_device(flash_device); - - // Activity LED for flash writes. -#ifdef MICROPY_HW_LED_MSC - gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF); - gpio_set_pin_direction(MICROPY_HW_LED_MSC, GPIO_DIRECTION_OUT); - // There's already a pull-up on the board. - gpio_set_pin_level(MICROPY_HW_LED_MSC, false); -#endif - - if (flash_device->has_sector_protection) { - write_enable(); - - // Turn off sector protection - uint8_t data[1] = {0x00}; - spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, data, 1); - } - - // Turn off writes in case this is a microcontroller only reset. - spi_flash_command(CMD_DISABLE_WRITE); - - wait_for_flash_ready(); - - current_sector = NO_SECTOR_LOADED; - dirty_mask = 0; - MP_STATE_VM(flash_ram_cache) = NULL; -} - -// The size of each individual block. -uint32_t external_flash_get_block_size(void) { - return FILESYSTEM_BLOCK_SIZE; -} - -// The total number of available blocks. -uint32_t external_flash_get_block_count(void) { - // We subtract one erase sector size because we may use it as a staging area - // for writes. - return SPI_FLASH_PART1_START_BLOCK + (flash_device->total_size - SPI_FLASH_ERASE_SIZE) / FILESYSTEM_BLOCK_SIZE; -} - -// Flush the cache that was written to the scratch portion of flash. Only used -// when ram is tight. -static bool flush_scratch_flash(void) { - // First, copy out any blocks that we haven't touched from the sector we've - // cached. - bool copy_to_scratch_ok = true; - uint32_t scratch_sector = flash_device->total_size - SPI_FLASH_ERASE_SIZE; - for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { - if ((dirty_mask & (1 << i)) == 0) { - copy_to_scratch_ok = copy_to_scratch_ok && - copy_block(current_sector + i * FILESYSTEM_BLOCK_SIZE, - scratch_sector + i * FILESYSTEM_BLOCK_SIZE); - } - } - if (!copy_to_scratch_ok) { - // TODO(tannewt): Do more here. We opted to not erase and copy bad data - // in. We still risk losing the data written to the scratch sector. - return false; - } - // Second, erase the current sector. - erase_sector(current_sector); - // Finally, copy the new version into it. - for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { - copy_block(scratch_sector + i * FILESYSTEM_BLOCK_SIZE, - current_sector + i * FILESYSTEM_BLOCK_SIZE); - } - return true; -} - -// Attempts to allocate a new set of page buffers for caching a full sector in -// ram. Each page is allocated separately so that the GC doesn't need to provide -// one huge block. We can free it as we write if we want to also. -static bool allocate_ram_cache(void) { - uint8_t blocks_per_sector = SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; - uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; - - uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t); - // Attempt to allocate outside the heap first. - supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false); - if (supervisor_cache != NULL) { - MP_STATE_VM(flash_ram_cache) = (uint8_t **) supervisor_cache->ptr; - uint8_t* page_start = (uint8_t *) supervisor_cache->ptr + table_size; - - for (uint8_t i = 0; i < blocks_per_sector; i++) { - for (uint8_t j = 0; j < pages_per_block; j++) { - uint32_t offset = i * pages_per_block + j; - MP_STATE_VM(flash_ram_cache)[offset] = page_start + offset * SPI_FLASH_PAGE_SIZE; - } - } - return true; - } - - MP_STATE_VM(flash_ram_cache) = m_malloc_maybe(blocks_per_sector * pages_per_block * sizeof(uint32_t), false); - if (MP_STATE_VM(flash_ram_cache) == NULL) { - return false; - } - // Declare i and j outside the loops in case we fail to allocate everything - // we need. In that case we'll give it back. - uint8_t i = 0; - uint8_t j = 0; - bool success = true; - for (i = 0; i < blocks_per_sector; i++) { - for (j = 0; j < pages_per_block; j++) { - uint8_t *page_cache = m_malloc_maybe(SPI_FLASH_PAGE_SIZE, false); - if (page_cache == NULL) { - success = false; - break; - } - MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j] = page_cache; - } - if (!success) { - break; - } - } - // We couldn't allocate enough so give back what we got. - if (!success) { - // We add 1 so that we delete 0 when i is 1. Going to zero (i >= 0) - // would never stop because i is unsigned. - i++; - for (; i > 0; i--) { - for (; j > 0; j--) { - m_free(MP_STATE_VM(flash_ram_cache)[(i - 1) * pages_per_block + (j - 1)]); - } - j = pages_per_block; - } - m_free(MP_STATE_VM(flash_ram_cache)); - MP_STATE_VM(flash_ram_cache) = NULL; - } - return success; -} - -// Flush the cached sector from ram onto the flash. We'll free the cache unless -// keep_cache is true. -static bool flush_ram_cache(bool keep_cache) { - // First, copy out any blocks that we haven't touched from the sector - // we've cached. If we don't do this we'll erase the data during the sector - // erase below. - bool copy_to_ram_ok = true; - uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; - for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { - if ((dirty_mask & (1 << i)) == 0) { - for (uint8_t j = 0; j < pages_per_block; j++) { - copy_to_ram_ok = read_flash( - current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE, - MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j], - SPI_FLASH_PAGE_SIZE); - if (!copy_to_ram_ok) { - break; - } - } - } - if (!copy_to_ram_ok) { - break; - } - } - - if (!copy_to_ram_ok) { - return false; - } - // Second, erase the current sector. - erase_sector(current_sector); - // Lastly, write all the data in ram that we've cached. - for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { - for (uint8_t j = 0; j < pages_per_block; j++) { - write_flash(current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE, - MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j], - SPI_FLASH_PAGE_SIZE); - if (!keep_cache && supervisor_cache == NULL) { - m_free(MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j]); - } - } - } - // We're done with the cache for now so give it back. - if (!keep_cache) { - if (supervisor_cache != NULL) { - free_memory(supervisor_cache); - supervisor_cache = NULL; - } else { - m_free(MP_STATE_VM(flash_ram_cache)); - } - MP_STATE_VM(flash_ram_cache) = NULL; - } - return true; -} - -// Delegates to the correct flash flush method depending on the existing cache. -static void spi_flash_flush_keep_cache(bool keep_cache) { - if (current_sector == NO_SECTOR_LOADED) { - return; - } - #ifdef MICROPY_HW_LED_MSC - port_pin_set_output_level(MICROPY_HW_LED_MSC, true); - #endif - temp_status_color(ACTIVE_WRITE); - // If we've cached to the flash itself flush from there. - if (MP_STATE_VM(flash_ram_cache) == NULL) { - flush_scratch_flash(); - } else { - flush_ram_cache(keep_cache); - } - current_sector = NO_SECTOR_LOADED; - clear_temp_status(); - #ifdef MICROPY_HW_LED_MSC - port_pin_set_output_level(MICROPY_HW_LED_MSC, false); - #endif -} - -// External flash function used. If called externally we assume we won't need -// the cache after. -void external_flash_flush(void) { - spi_flash_flush_keep_cache(false); -} - -void flash_flush(void) { - external_flash_flush(); -} - -// Builds a partition entry for the MBR. -static void build_partition(uint8_t *buf, int boot, int type, - uint32_t start_block, uint32_t num_blocks) { - buf[0] = boot; - - if (num_blocks == 0) { - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - } else { - buf[1] = 0xff; - buf[2] = 0xff; - buf[3] = 0xff; - } - - buf[4] = type; - - if (num_blocks == 0) { - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - } else { - buf[5] = 0xff; - buf[6] = 0xff; - buf[7] = 0xff; - } - - buf[8] = start_block; - buf[9] = start_block >> 8; - buf[10] = start_block >> 16; - buf[11] = start_block >> 24; - - buf[12] = num_blocks; - buf[13] = num_blocks >> 8; - buf[14] = num_blocks >> 16; - buf[15] = num_blocks >> 24; -} - -static int32_t convert_block_to_flash_addr(uint32_t block) { - if (SPI_FLASH_PART1_START_BLOCK <= block && block < external_flash_get_block_count()) { - // a block in partition 1 - block -= SPI_FLASH_PART1_START_BLOCK; - return block * FILESYSTEM_BLOCK_SIZE; - } - // bad block - return -1; -} - -bool external_flash_read_block(uint8_t *dest, uint32_t block) { - if (block == 0) { - // Fake the MBR so we can decide on our own partition table - for (int i = 0; i < 446; i++) { - dest[i] = 0; - } - - build_partition(dest + 446, 0, 0x01 /* FAT12 */, - SPI_FLASH_PART1_START_BLOCK, - external_flash_get_block_count() - SPI_FLASH_PART1_START_BLOCK); - build_partition(dest + 462, 0, 0, 0, 0); - build_partition(dest + 478, 0, 0, 0, 0); - build_partition(dest + 494, 0, 0, 0, 0); - - dest[510] = 0x55; - dest[511] = 0xaa; - - return true; - } else if (block < SPI_FLASH_PART1_START_BLOCK) { - memset(dest, 0, FILESYSTEM_BLOCK_SIZE); - return true; - } else { - // Non-MBR block, get data from flash memory. - int32_t address = convert_block_to_flash_addr(block); - if (address == -1) { - // bad block number - return false; - } - - // Mask out the lower bits that designate the address within the sector. - uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1)); - uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE); - uint8_t mask = 1 << (block_index); - // We're reading from the currently cached sector. - if (current_sector == this_sector && (mask & dirty_mask) > 0) { - if (MP_STATE_VM(flash_ram_cache) != NULL) { - uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; - for (int i = 0; i < pages_per_block; i++) { - memcpy(dest + i * SPI_FLASH_PAGE_SIZE, - MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i], - SPI_FLASH_PAGE_SIZE); - } - return true; - } else { - uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE; - return read_flash(scratch_address, dest, FILESYSTEM_BLOCK_SIZE); - } - } - return read_flash(address, dest, FILESYSTEM_BLOCK_SIZE); - } -} - -bool external_flash_write_block(const uint8_t *data, uint32_t block) { - if (block < SPI_FLASH_PART1_START_BLOCK) { - // Fake writing below the flash partition. - return true; - } else { - // Non-MBR block, copy to cache - int32_t address = convert_block_to_flash_addr(block); - if (address == -1) { - // bad block number - return false; - } - // Wait for any previous writes to finish. - wait_for_flash_ready(); - // Mask out the lower bits that designate the address within the sector. - uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1)); - uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE); - uint8_t mask = 1 << (block_index); - // Flush the cache if we're moving onto a sector or we're writing the - // same block again. - if (current_sector != this_sector || (mask & dirty_mask) > 0) { - // Check to see if we'd write to an erased page. In that case we - // can write directly. - if (page_erased(address)) { - return write_flash(address, data, FILESYSTEM_BLOCK_SIZE); - } - if (current_sector != NO_SECTOR_LOADED) { - spi_flash_flush_keep_cache(true); - } - if (MP_STATE_VM(flash_ram_cache) == NULL && !allocate_ram_cache()) { - erase_sector(flash_device->total_size - SPI_FLASH_ERASE_SIZE); - wait_for_flash_ready(); - } - current_sector = this_sector; - dirty_mask = 0; - } - dirty_mask |= mask; - // Copy the block to the appropriate cache. - if (MP_STATE_VM(flash_ram_cache) != NULL) { - uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; - for (int i = 0; i < pages_per_block; i++) { - memcpy(MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i], - data + i * SPI_FLASH_PAGE_SIZE, - SPI_FLASH_PAGE_SIZE); - } - return true; - } else { - uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE; - return write_flash(scratch_address, data, FILESYSTEM_BLOCK_SIZE); - } - } -} - -mp_uint_t external_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { - for (size_t i = 0; i < num_blocks; i++) { - if (!external_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { - return 1; // error - } - } - return 0; // success -} - -mp_uint_t external_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { - for (size_t i = 0; i < num_blocks; i++) { - if (!external_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { - return 1; // error - } - } - return 0; // success -} - -/******************************************************************************/ -// MicroPython bindings -// -// Expose the flash as an object with the block protocol. - -// there is a singleton Flash object -STATIC const mp_obj_base_t external_flash_obj = {&external_flash_type}; - -STATIC mp_obj_t external_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - // check arguments - mp_arg_check_num(n_args, n_kw, 0, 0, false); - - // return singleton object - return (mp_obj_t)&external_flash_obj; -} - -STATIC mp_obj_t external_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); - mp_uint_t ret = external_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); - return MP_OBJ_NEW_SMALL_INT(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(external_flash_obj_readblocks_obj, external_flash_obj_readblocks); - -STATIC mp_obj_t external_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); - mp_uint_t ret = external_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); - return MP_OBJ_NEW_SMALL_INT(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(external_flash_obj_writeblocks_obj, external_flash_obj_writeblocks); - -STATIC mp_obj_t external_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { - mp_int_t cmd = mp_obj_get_int(cmd_in); - switch (cmd) { - case BP_IOCTL_INIT: external_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_DEINIT: external_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly - case BP_IOCTL_SYNC: external_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(external_flash_get_block_count()); - case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(external_flash_get_block_size()); - default: return mp_const_none; - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(external_flash_obj_ioctl_obj, external_flash_obj_ioctl); - -STATIC const mp_rom_map_elem_t external_flash_obj_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&external_flash_obj_readblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&external_flash_obj_writeblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&external_flash_obj_ioctl_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(external_flash_obj_locals_dict, external_flash_obj_locals_dict_table); - -const mp_obj_type_t external_flash_type = { - { &mp_type_type }, - .name = MP_QSTR_SPIFlash, - .make_new = external_flash_obj_make_new, - .locals_dict = (mp_obj_t)&external_flash_obj_locals_dict, -}; - -void flash_init_vfs(fs_user_mount_t *vfs) { - vfs->base.type = &mp_fat_vfs_type; - vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; - vfs->fatfs.drv = vfs; - vfs->fatfs.part = 1; // flash filesystem lives on first partition - vfs->readblocks[0] = (mp_obj_t)&external_flash_obj_readblocks_obj; - vfs->readblocks[1] = (mp_obj_t)&external_flash_obj; - vfs->readblocks[2] = (mp_obj_t)external_flash_read_blocks; // native version - vfs->writeblocks[0] = (mp_obj_t)&external_flash_obj_writeblocks_obj; - vfs->writeblocks[1] = (mp_obj_t)&external_flash_obj; - vfs->writeblocks[2] = (mp_obj_t)external_flash_write_blocks; // native version - vfs->u.ioctl[0] = (mp_obj_t)&external_flash_obj_ioctl_obj; - vfs->u.ioctl[1] = (mp_obj_t)&external_flash_obj; -} diff --git a/ports/atmel-samd/external_flash/external_flash.h b/ports/atmel-samd/external_flash/external_flash.h deleted file mode 100644 index f8ec5673c..000000000 --- a/ports/atmel-samd/external_flash/external_flash.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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. - */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_H -#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_H - -#include - -#include "mpconfigport.h" - -// We use this when we can allocate the whole cache in RAM. -#define FLASH_ROOT_POINTERS \ - uint8_t** flash_ram_cache; \ - -// Erase sector size. -#define SPI_FLASH_SECTOR_SIZE (0x1000 - 100) - -// These are common across all NOR Flash. -#define SPI_FLASH_ERASE_SIZE (1 << 12) -#define SPI_FLASH_PAGE_SIZE (256) - -#define SPI_FLASH_SYSTICK_MASK (0x1ff) // 512ms -#define SPI_FLASH_IDLE_TICK(tick) (((tick) & SPI_FLASH_SYSTICK_MASK) == 2) - -void external_flash_init(void); -uint32_t external_flash_get_block_size(void); -uint32_t external_flash_get_block_count(void); -void external_flash_irq_handler(void); -void external_flash_flush(void); -bool external_flash_read_block(uint8_t *dest, uint32_t block); -bool external_flash_write_block(const uint8_t *src, uint32_t block); - -// these return 0 on success, non-zero on error -mp_uint_t external_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); -mp_uint_t external_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); - -extern const struct _mp_obj_type_t external_flash_type; - -struct _fs_user_mount_t; -void flash_init_vfs(struct _fs_user_mount_t *vfs); - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_H diff --git a/ports/atmel-samd/external_flash/qspi_flash.c b/ports/atmel-samd/external_flash/qspi_flash.c deleted file mode 100644 index f6dc00d50..000000000 --- a/ports/atmel-samd/external_flash/qspi_flash.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "spi_flash_api.h" - -#include -#include - -#include "mpconfigboard.h" // for EXTERNAL_FLASH_QSPI_DUAL - -#include "external_flash/common_commands.h" -#include "samd/cache.h" -#include "samd/dma.h" - -#include "atmel_start_pins.h" -#include "hal_gpio.h" - -bool spi_flash_command(uint8_t command) { - QSPI->INSTRCTRL.bit.INSTR = command; - - QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_READ | - QSPI_INSTRFRAME_INSTREN; - - QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; - - while( !QSPI->INTFLAG.bit.INSTREND ); - - QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; - - return true; -} - -bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) { - samd_peripherals_disable_and_clear_cache(); - - QSPI->INSTRCTRL.bit.INSTR = command; - - QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_READ | - QSPI_INSTRFRAME_INSTREN | - QSPI_INSTRFRAME_DATAEN; - - // Dummy read of INSTRFRAME needed to synchronize. - // See Instruction Transmission Flow Diagram, figure 37.9, page 995 - // and Example 4, page 998, section 37.6.8.5. - (volatile uint32_t) QSPI->INSTRFRAME.reg; - - memcpy(response, (uint8_t *) QSPI_AHB, length); - - QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; - - while( !QSPI->INTFLAG.bit.INSTREND ); - - QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; - - samd_peripherals_enable_cache(); - - return true; -} - -bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { - samd_peripherals_disable_and_clear_cache(); - - QSPI->INSTRCTRL.bit.INSTR = command; - - QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_WRITE | - QSPI_INSTRFRAME_INSTREN | - (data != NULL ? QSPI_INSTRFRAME_DATAEN : 0); - - // Dummy read of INSTRFRAME needed to synchronize. - // See Instruction Transmission Flow Diagram, figure 37.9, page 995 - // and Example 4, page 998, section 37.6.8.5. - (volatile uint32_t) QSPI->INSTRFRAME.reg; - - if (data != NULL) { - memcpy((uint8_t *) QSPI_AHB, data, length); - } - - QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; - - while( !QSPI->INTFLAG.bit.INSTREND ); - - QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; - - samd_peripherals_enable_cache(); - - return true; -} - -bool spi_flash_sector_command(uint8_t command, uint32_t address) { - QSPI->INSTRCTRL.bit.INSTR = command; - QSPI->INSTRADDR.bit.ADDR = address; - - QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_WRITE | - QSPI_INSTRFRAME_INSTREN | - QSPI_INSTRFRAME_ADDREN; - - QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; - - while( !QSPI->INTFLAG.bit.INSTREND ); - - QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; - - return true; -} - -bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { - samd_peripherals_disable_and_clear_cache(); - - QSPI->INSTRCTRL.bit.INSTR = CMD_PAGE_PROGRAM; - uint32_t mode = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI; - - QSPI->INSTRFRAME.reg = mode | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY | - QSPI_INSTRFRAME_INSTREN | - QSPI_INSTRFRAME_ADDREN | - QSPI_INSTRFRAME_DATAEN; - - memcpy(((uint8_t *) QSPI_AHB) + address, data, length); - // TODO(tannewt): Fix DMA and enable it. - // qspi_dma_write(address, data, length); - - QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; - - while( !QSPI->INTFLAG.bit.INSTREND ); - - QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; - - samd_peripherals_enable_cache(); - - return true; -} - -bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { - samd_peripherals_disable_and_clear_cache(); - - #ifdef EXTERNAL_FLASH_QSPI_DUAL - QSPI->INSTRCTRL.bit.INSTR = CMD_DUAL_READ; - uint32_t mode = QSPI_INSTRFRAME_WIDTH_DUAL_OUTPUT; - #else - QSPI->INSTRCTRL.bit.INSTR = CMD_QUAD_READ; - uint32_t mode = QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT; - #endif - - QSPI->INSTRFRAME.reg = mode | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_READMEMORY | - QSPI_INSTRFRAME_INSTREN | - QSPI_INSTRFRAME_ADDREN | - QSPI_INSTRFRAME_DATAEN | - QSPI_INSTRFRAME_DUMMYLEN(8); - - memcpy(data, ((uint8_t *) QSPI_AHB) + address, length); - // TODO(tannewt): Fix DMA and enable it. - // qspi_dma_read(address, data, length); - - QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; - - while( !QSPI->INTFLAG.bit.INSTREND ); - - QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; - - samd_peripherals_enable_cache(); - - return true; -} - - -void spi_flash_init(void) { - MCLK->APBCMASK.bit.QSPI_ = true; - MCLK->AHBMASK.bit.QSPI_ = true; - MCLK->AHBMASK.bit.QSPI_2X_ = false; // Only true if we are doing DDR. - - QSPI->CTRLA.reg = QSPI_CTRLA_SWRST; - // We don't need to wait because we're running as fast as the CPU. - - // Slow, good for debugging with Saleae - // QSPI->BAUD.bit.BAUD = 32; - // Super fast, may be unreliable when Saleae is connected to high speed lines. - QSPI->BAUD.bit.BAUD = 2; - QSPI->CTRLB.reg = QSPI_CTRLB_MODE_MEMORY | // Serial memory mode (map to QSPI_AHB) - QSPI_CTRLB_DATALEN_8BITS | - QSPI_CTRLB_CSMODE_LASTXFER; - - QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE; - - // The QSPI is only connected to one set of pins in the SAMD51 so we can hard code it. - uint32_t pins[6] = {PIN_PA08, PIN_PA09, PIN_PA10, PIN_PA11, PIN_PB10, PIN_PB11}; - for (uint8_t i = 0; i < 6; i++) { - gpio_set_pin_direction(pins[i], GPIO_DIRECTION_IN); - gpio_set_pin_pull_mode(pins[i], GPIO_PULL_OFF); - gpio_set_pin_function(pins[i], GPIO_PIN_FUNCTION_H); - } -} - -void spi_flash_init_device(const external_flash_device* device) { - // Verify that QSPI mode is enabled. - uint8_t status; - spi_flash_read_command(CMD_READ_STATUS2, &status, 1); - - // Bit 1 is Quad Enable - if ((status & 0x2) == 0) { - uint8_t full_status[2] = {0x0, 0x2}; - spi_flash_command(CMD_ENABLE_WRITE); - if (device->write_status_register_split) { - spi_flash_write_command(CMD_WRITE_STATUS_BYTE2, full_status + 1, 1); - } else { - spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, full_status, 2); - } - } -} diff --git a/ports/atmel-samd/external_flash/spi_flash.c b/ports/atmel-samd/external_flash/spi_flash.c deleted file mode 100644 index 273915df7..000000000 --- a/ports/atmel-samd/external_flash/spi_flash.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "spi_flash_api.h" - -#include -#include - -#include "external_flash/common_commands.h" -#include "samd/sercom.h" -#include "py/mpconfig.h" - -#include "hal_gpio.h" -#include "hal_spi_m_sync.h" - -struct spi_m_sync_descriptor spi_flash_desc; - -// Enable the flash over SPI. -static void flash_enable(void) { - gpio_set_pin_level(SPI_FLASH_CS_PIN, false); -} - -// Disable the flash over SPI. -static void flash_disable(void) { - gpio_set_pin_level(SPI_FLASH_CS_PIN, true); -} - -static bool transfer(uint8_t* command, uint32_t command_length, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { - struct spi_xfer xfer = { command, NULL, command_length }; - flash_enable(); - int32_t status = spi_m_sync_transfer(&spi_flash_desc, &xfer); - if (status >= 0 && !(data_in == NULL && data_out == NULL)) { - struct spi_xfer data_xfer = {data_in, data_out, data_length}; - status = spi_m_sync_transfer(&spi_flash_desc, &data_xfer); - } - flash_disable(); - return status >= 0; -} - -static bool transfer_command(uint8_t command, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { - return transfer(&command, 1, data_in, data_out, data_length); -} - -bool spi_flash_command(uint8_t command) { - return transfer_command(command, NULL, NULL, 0); -} - -bool spi_flash_read_command(uint8_t command, uint8_t* data, uint32_t data_length) { - return transfer_command(command, NULL, data, data_length); -} - -bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t data_length) { - return transfer_command(command, data, NULL, data_length); -} - -// Pack the low 24 bits of the address into a uint8_t array. -static void address_to_bytes(uint32_t address, uint8_t* bytes) { - bytes[0] = (address >> 16) & 0xff; - bytes[1] = (address >> 8) & 0xff; - bytes[2] = address & 0xff; -} - -bool spi_flash_sector_command(uint8_t command, uint32_t address) { - uint8_t request[4] = {command, 0x00, 0x00, 0x00}; - address_to_bytes(address, request + 1); - return transfer(request, 4, NULL, NULL, 0); -} - -bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length) { - uint8_t request[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00}; - // Write the SPI flash write address into the bytes following the command byte. - address_to_bytes(address, request + 1); - struct spi_xfer xfer = { request, NULL, 4 }; - flash_enable(); - int32_t status = spi_m_sync_transfer(&spi_flash_desc, &xfer); - if (status >= 0) { - status = sercom_dma_write(spi_flash_desc.dev.prvt, data, data_length); - } - flash_disable(); - return status >= 0; -} - -bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length) { - uint8_t request[4] = {CMD_READ_DATA, 0x00, 0x00, 0x00}; - // Write the SPI flash write address into the bytes following the command byte. - address_to_bytes(address, request + 1); - struct spi_xfer xfer = { request, NULL, 4 }; - flash_enable(); - int32_t status = spi_m_sync_transfer(&spi_flash_desc, &xfer); - if (status >= 0) { - status = sercom_dma_read(spi_flash_desc.dev.prvt, data, data_length, 0xff); - } - flash_disable(); - return status >= 0; -} - -void spi_flash_init(void) { - samd_peripherals_sercom_clock_init(SPI_FLASH_SERCOM, SPI_FLASH_SERCOM_INDEX); - - // Set up with defaults, then change. - spi_m_sync_init(&spi_flash_desc, SPI_FLASH_SERCOM); - - hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO); - hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO); - - gpio_set_pin_direction(SPI_FLASH_SCK_PIN, GPIO_DIRECTION_OUT); - gpio_set_pin_pull_mode(SPI_FLASH_SCK_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_SCK_PIN, SPI_FLASH_SCK_PIN_FUNCTION); - - gpio_set_pin_direction(SPI_FLASH_MOSI_PIN, GPIO_DIRECTION_OUT); - gpio_set_pin_pull_mode(SPI_FLASH_MOSI_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_MOSI_PIN, SPI_FLASH_MOSI_PIN_FUNCTION); - - gpio_set_pin_direction(SPI_FLASH_MISO_PIN, GPIO_DIRECTION_IN); - gpio_set_pin_pull_mode(SPI_FLASH_MISO_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_MISO_PIN, SPI_FLASH_MISO_PIN_FUNCTION); - - hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO); - hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO); - - spi_m_sync_set_baudrate(&spi_flash_desc, samd_peripherals_spi_baudrate_to_baud_reg_value(SPI_FLASH_BAUDRATE)); - - gpio_set_pin_direction(SPI_FLASH_CS_PIN, GPIO_DIRECTION_OUT); - // There's already a pull-up on the board. - gpio_set_pin_pull_mode(SPI_FLASH_CS_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF); - - // Set CS high (disabled). - flash_disable(); - - spi_m_sync_enable(&spi_flash_desc); -} - -void spi_flash_init_device(const external_flash_device* device) { - -} diff --git a/ports/atmel-samd/external_flash/spi_flash_api.h b/ports/atmel-samd/external_flash/spi_flash_api.h deleted file mode 100644 index a6df0c459..000000000 --- a/ports/atmel-samd/external_flash/spi_flash_api.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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. - */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H -#define MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H - -#include -#include - -#include "external_flash/devices.h" - -// This API is implemented for both normal SPI peripherals and QSPI peripherals. - -bool spi_flash_command(uint8_t command); -bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length); -bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length); -bool spi_flash_sector_command(uint8_t command, uint32_t address); -bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length); -bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length); -void spi_flash_init(void); -void spi_flash_init_device(const external_flash_device* device); - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H diff --git a/ports/atmel-samd/flash_api.c b/ports/atmel-samd/flash_api.c deleted file mode 100644 index b8a212718..000000000 --- a/ports/atmel-samd/flash_api.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "flash_api.h" - -#include "py/mpstate.h" - -#define VFS_INDEX 0 - -void flash_set_usb_writable(bool usb_writable) { - mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table); - for (uint8_t i = 0; current_mount != NULL; i++) { - if (i == VFS_INDEX) { - break; - } - current_mount = current_mount->next; - } - if (current_mount == NULL) { - return; - } - fs_user_mount_t *vfs = (fs_user_mount_t *) current_mount->obj; - - if (usb_writable) { - vfs->flags |= FSUSER_USB_WRITABLE; - } else { - vfs->flags &= ~FSUSER_USB_WRITABLE; - } -} diff --git a/ports/atmel-samd/flash_api.h b/ports/atmel-samd/flash_api.h deleted file mode 100644 index 3e41f59a2..000000000 --- a/ports/atmel-samd/flash_api.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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. - */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H -#define MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H - -#include "extmod/vfs_fat.h" - -extern void flash_init_vfs(fs_user_mount_t *vfs); -extern void flash_flush(void); - -void flash_set_usb_writable(bool usb_writable); - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H diff --git a/ports/atmel-samd/internal_flash.c b/ports/atmel-samd/internal_flash.c deleted file mode 100644 index 35eb52da2..000000000 --- a/ports/atmel-samd/internal_flash.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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 "internal_flash.h" - -#include -#include - -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "py/mphal.h" -#include "py/obj.h" -#include "py/runtime.h" -#include "lib/oofatfs/ff.h" - -#ifdef SAMD21 -#include "hpl/pm/hpl_pm_base.h" -#endif -#include "hal/include/hal_flash.h" - -#include "supervisor/shared/rgb_led_status.h" - -static struct flash_descriptor internal_flash_desc; - -void internal_flash_init(void) { - // Activity LED for flash writes. - #ifdef MICROPY_HW_LED_MSC - struct port_config pin_conf; - port_get_config_defaults(&pin_conf); - - pin_conf.direction = PORT_PIN_DIR_OUTPUT; - port_pin_set_config(MICROPY_HW_LED_MSC, &pin_conf); - port_pin_set_output_level(MICROPY_HW_LED_MSC, false); - #endif - - #ifdef SAMD51 - hri_mclk_set_AHBMASK_NVMCTRL_bit(MCLK); - #endif - #ifdef SAMD21 - _pm_enable_bus_clock(PM_BUS_APBB, NVMCTRL); - #endif - flash_init(&internal_flash_desc, NVMCTRL); -} - -uint32_t internal_flash_get_block_size(void) { - return FILESYSTEM_BLOCK_SIZE; -} - -uint32_t internal_flash_get_block_count(void) { - return INTERNAL_FLASH_PART1_START_BLOCK + INTERNAL_FLASH_PART1_NUM_BLOCKS; -} - -void internal_flash_flush(void) { -} - -void flash_flush(void) { - internal_flash_flush(); -} - -static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) { - buf[0] = boot; - - if (num_blocks == 0) { - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - } else { - buf[1] = 0xff; - buf[2] = 0xff; - buf[3] = 0xff; - } - - buf[4] = type; - - if (num_blocks == 0) { - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - } else { - buf[5] = 0xff; - buf[6] = 0xff; - buf[7] = 0xff; - } - - buf[8] = start_block; - buf[9] = start_block >> 8; - buf[10] = start_block >> 16; - buf[11] = start_block >> 24; - - buf[12] = num_blocks; - buf[13] = num_blocks >> 8; - buf[14] = num_blocks >> 16; - buf[15] = num_blocks >> 24; -} - -static int32_t convert_block_to_flash_addr(uint32_t block) { - if (INTERNAL_FLASH_PART1_START_BLOCK <= block && block < INTERNAL_FLASH_PART1_START_BLOCK + INTERNAL_FLASH_PART1_NUM_BLOCKS) { - // a block in partition 1 - block -= INTERNAL_FLASH_PART1_START_BLOCK; - return INTERNAL_FLASH_MEM_SEG1_START_ADDR + block * FILESYSTEM_BLOCK_SIZE; - } - // bad block - return -1; -} - -bool internal_flash_read_block(uint8_t *dest, uint32_t block) { - if (block == 0) { - // fake the MBR so we can decide on our own partition table - - for (int i = 0; i < 446; i++) { - dest[i] = 0; - } - - build_partition(dest + 446, 0, 0x01 /* FAT12 */, INTERNAL_FLASH_PART1_START_BLOCK, INTERNAL_FLASH_PART1_NUM_BLOCKS); - build_partition(dest + 462, 0, 0, 0, 0); - build_partition(dest + 478, 0, 0, 0, 0); - build_partition(dest + 494, 0, 0, 0, 0); - - dest[510] = 0x55; - dest[511] = 0xaa; - - return true; - - } else { - // non-MBR block, get data from flash memory - int32_t src = convert_block_to_flash_addr(block); - if (src == -1) { - // bad block number - return false; - } - int32_t error_code = flash_read(&internal_flash_desc, src, dest, FILESYSTEM_BLOCK_SIZE); - return error_code == ERR_NONE; - } -} - -bool internal_flash_write_block(const uint8_t *src, uint32_t block) { - if (block == 0) { - // can't write MBR, but pretend we did - return true; - - } else { - #ifdef MICROPY_HW_LED_MSC - port_pin_set_output_level(MICROPY_HW_LED_MSC, true); - #endif - temp_status_color(ACTIVE_WRITE); - // non-MBR block, copy to cache - int32_t dest = convert_block_to_flash_addr(block); - if (dest == -1) { - // bad block number - return false; - } - int32_t error_code; - error_code = flash_erase(&internal_flash_desc, - dest, - FILESYSTEM_BLOCK_SIZE / flash_get_page_size(&internal_flash_desc)); - if (error_code != ERR_NONE) { - return false; - } - - error_code = flash_append(&internal_flash_desc, dest, src, FILESYSTEM_BLOCK_SIZE); - if (error_code != ERR_NONE) { - return false; - } - clear_temp_status(); - #ifdef MICROPY_HW_LED_MSC - port_pin_set_output_level(MICROPY_HW_LED_MSC, false); - #endif - return true; - } -} - -mp_uint_t internal_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { - for (size_t i = 0; i < num_blocks; i++) { - if (!internal_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { - return 1; // error - } - } - return 0; // success -} - -mp_uint_t internal_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { - for (size_t i = 0; i < num_blocks; i++) { - if (!internal_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { - return 1; // error - } - } - return 0; // success -} - -/******************************************************************************/ -// MicroPython bindings -// -// Expose the flash as an object with the block protocol. - -// there is a singleton Flash object -STATIC const mp_obj_base_t internal_flash_obj = {&internal_flash_type}; - -STATIC mp_obj_t internal_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - // check arguments - mp_arg_check_num(n_args, n_kw, 0, 0, false); - - // return singleton object - return (mp_obj_t)&internal_flash_obj; -} - -STATIC mp_obj_t internal_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); - mp_uint_t ret = internal_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); - return MP_OBJ_NEW_SMALL_INT(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_readblocks_obj, internal_flash_obj_readblocks); - -STATIC mp_obj_t internal_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); - mp_uint_t ret = internal_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); - return MP_OBJ_NEW_SMALL_INT(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_writeblocks_obj, internal_flash_obj_writeblocks); - -STATIC mp_obj_t internal_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { - mp_int_t cmd = mp_obj_get_int(cmd_in); - switch (cmd) { - case BP_IOCTL_INIT: internal_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_DEINIT: internal_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly - case BP_IOCTL_SYNC: internal_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(internal_flash_get_block_count()); - case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(internal_flash_get_block_size()); - default: return mp_const_none; - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_ioctl_obj, internal_flash_obj_ioctl); - -STATIC const mp_rom_map_elem_t internal_flash_obj_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&internal_flash_obj_readblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&internal_flash_obj_writeblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&internal_flash_obj_ioctl_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(internal_flash_obj_locals_dict, internal_flash_obj_locals_dict_table); - -const mp_obj_type_t internal_flash_type = { - { &mp_type_type }, - .name = MP_QSTR_InternalFlash, - .make_new = internal_flash_obj_make_new, - .locals_dict = (mp_obj_t)&internal_flash_obj_locals_dict, -}; - -void flash_init_vfs(fs_user_mount_t *vfs) { - vfs->base.type = &mp_fat_vfs_type; - vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; - vfs->fatfs.drv = vfs; - vfs->fatfs.part = 1; // flash filesystem lives on first partition - vfs->readblocks[0] = (mp_obj_t)&internal_flash_obj_readblocks_obj; - vfs->readblocks[1] = (mp_obj_t)&internal_flash_obj; - vfs->readblocks[2] = (mp_obj_t)internal_flash_read_blocks; // native version - vfs->writeblocks[0] = (mp_obj_t)&internal_flash_obj_writeblocks_obj; - vfs->writeblocks[1] = (mp_obj_t)&internal_flash_obj; - vfs->writeblocks[2] = (mp_obj_t)internal_flash_write_blocks; // native version - vfs->u.ioctl[0] = (mp_obj_t)&internal_flash_obj_ioctl_obj; - vfs->u.ioctl[1] = (mp_obj_t)&internal_flash_obj; -} diff --git a/ports/atmel-samd/internal_flash.h b/ports/atmel-samd/internal_flash.h deleted file mode 100644 index 88c105386..000000000 --- a/ports/atmel-samd/internal_flash.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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. - */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H -#define MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H - -#include - -#include "mpconfigport.h" - -#include "sam.h" - -#define FLASH_ROOT_POINTERS - -#ifdef SAMD51 -#define TOTAL_INTERNAL_FLASH_SIZE (FLASH_SIZE / 2) -#endif - -#ifdef SAMD21 -#define TOTAL_INTERNAL_FLASH_SIZE 0x010000 -#endif - -#define INTERNAL_FLASH_MEM_SEG1_START_ADDR (FLASH_SIZE - TOTAL_INTERNAL_FLASH_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE) -#define INTERNAL_FLASH_PART1_START_BLOCK (0x1) -#define INTERNAL_FLASH_PART1_NUM_BLOCKS (TOTAL_INTERNAL_FLASH_SIZE / FILESYSTEM_BLOCK_SIZE) - -#define INTERNAL_FLASH_SYSTICK_MASK (0x1ff) // 512ms -#define INTERNAL_FLASH_IDLE_TICK(tick) (((tick) & INTERNAL_FLASH_SYSTICK_MASK) == 2) - -void internal_flash_init(void); -uint32_t internal_flash_get_block_size(void); -uint32_t internal_flash_get_block_count(void); -void internal_flash_irq_handler(void); -void internal_flash_flush(void); -bool internal_flash_read_block(uint8_t *dest, uint32_t block); -bool internal_flash_write_block(const uint8_t *src, uint32_t block); - -// these return 0 on success, non-zero on error -mp_uint_t internal_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); -mp_uint_t internal_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); - -extern const struct _mp_obj_type_t internal_flash_type; - -struct _fs_user_mount_t; -void flash_init_vfs(struct _fs_user_mount_t *vfs); - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index a4c763d05..3b95aefa9 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -154,6 +154,7 @@ typedef long mp_off_t; #define CIRCUITPY_MCU_FAMILY samd21 #define MICROPY_PY_SYS_PLATFORM "Atmel SAMD21" #define PORT_HEAP_SIZE (16384 + 4096) +#define SPI_FLASH_MAX_BAUDRATE 8000000 #define CIRCUITPY_DEFAULT_STACK_SIZE 4096 #define MICROPY_CPYTHON_COMPAT (0) #define MICROPY_MODULE_WEAK_LINKS (0) @@ -169,6 +170,7 @@ typedef long mp_off_t; #define CIRCUITPY_MCU_FAMILY samd51 #define MICROPY_PY_SYS_PLATFORM "MicroChip SAMD51" #define PORT_HEAP_SIZE (0x20000) // 128KiB +#define SPI_FLASH_MAX_BAUDRATE 24000000 #define CIRCUITPY_DEFAULT_STACK_SIZE 8192 #define MICROPY_CPYTHON_COMPAT (1) #define MICROPY_MODULE_WEAK_LINKS (1) @@ -421,8 +423,13 @@ extern const struct _mp_obj_module_t wiznet_module; #define MP_STATE_PORT MP_STATE_VM +void run_background_tasks(void); +#define MICROPY_VM_HOOK_LOOP run_background_tasks(); +#define MICROPY_VM_HOOK_RETURN run_background_tasks(); + #include "peripherals/samd/dma.h" +#include "supervisor/flash_root_pointers.h" #if MICROPY_PY_NETWORK #define NETWORK_ROOT_POINTERS mp_obj_list_t mod_network_nic_list; #else @@ -438,9 +445,6 @@ extern const struct _mp_obj_module_t wiznet_module; mp_obj_t gamepad_singleton; \ NETWORK_ROOT_POINTERS \ -void run_background_tasks(void); -#define MICROPY_VM_HOOK_LOOP run_background_tasks(); -#define MICROPY_VM_HOOK_RETURN run_background_tasks(); #define CIRCUITPY_AUTORELOAD_DELAY_MS 500 #define CIRCUITPY_BOOT_OUTPUT_FILE "/boot_out.txt" diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index d5c9f2ce6..7db581dd6 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -14,6 +14,6 @@ ifeq ($(LONGINT_IMPL),LONGLONG) MPY_TOOL_LONGINT_IMPL = -mlongint-impl=longlong endif - INTERNAL_LIBM = 1 +USB_SERIAL_NUMBER_LENGTH = 32 diff --git a/ports/atmel-samd/mphalport.c b/ports/atmel-samd/mphalport.c index a4eca1c74..ac5b0fbca 100644 --- a/ports/atmel-samd/mphalport.c +++ b/ports/atmel-samd/mphalport.c @@ -46,43 +46,9 @@ #include "mphalport.h" #include "reset.h" #include "tick.h" -#include "usb.h" -extern struct usart_module usart_instance; extern uint32_t common_hal_mcu_processor_get_frequency(void); -int mp_hal_stdin_rx_chr(void) { - for (;;) { - #ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP - #endif - // if (reload_requested) { - // return CHAR_CTRL_D; - // } - if (usb_bytes_available()) { - #ifdef MICROPY_HW_LED_RX - gpio_toggle_pin_level(MICROPY_HW_LED_RX); - #endif - return usb_read(); - } - } -} - -void mp_hal_stdout_tx_strn(const char *str, size_t len) { - #ifdef MICROPY_HW_LED_TX - gpio_toggle_pin_level(MICROPY_HW_LED_TX); - #endif - - #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - if (boot_output_file != NULL) { - UINT bytes_written = 0; - f_write(boot_output_file, str, len, &bytes_written); - } - #endif - - usb_write(str, len); -} - void mp_hal_delay_ms(mp_uint_t delay) { uint64_t start_tick = ticks_ms; uint64_t duration = 0; diff --git a/ports/atmel-samd/mphalport.h b/ports/atmel-samd/mphalport.h index 3bc824d4e..64269b201 100644 --- a/ports/atmel-samd/mphalport.h +++ b/ports/atmel-samd/mphalport.h @@ -41,8 +41,6 @@ static inline mp_uint_t mp_hal_ticks_ms(void) { volatile uint8_t usb_rx_count; volatile bool mp_cdc_enabled; -FIL* boot_output_file; - int receive_usb(void); void mp_hal_set_interrupt_char(int c); diff --git a/ports/atmel-samd/peripherals b/ports/atmel-samd/peripherals index d0dcba251..f20fcf642 160000 --- a/ports/atmel-samd/peripherals +++ b/ports/atmel-samd/peripherals @@ -1 +1 @@ -Subproject commit d0dcba251c27f629c0a89b5ced4433dd0a609ca3 +Subproject commit f20fcf642b5654ee68d7d551ea7db39716ef83bf diff --git a/ports/atmel-samd/reset.c b/ports/atmel-samd/reset.c index ba5bf9ba1..735639cc4 100644 --- a/ports/atmel-samd/reset.c +++ b/ports/atmel-samd/reset.c @@ -34,10 +34,6 @@ void reset(void) { NVIC_SystemReset(); } -void reset_to_bootloader(void) { - _bootloader_dbl_tap = DBL_TAP_MAGIC; - reset(); -} extern uint32_t _srelocate; bool bootloader_available(void) { diff --git a/ports/atmel-samd/supervisor/filesystem.c b/ports/atmel-samd/supervisor/filesystem.c deleted file mode 100644 index 8a6010951..000000000 --- a/ports/atmel-samd/supervisor/filesystem.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "extmod/vfs_fat.h" -#include "lib/oofatfs/ff.h" -#include "lib/oofatfs/diskio.h" - -#include "py/mpstate.h" - -#include "flash_api.h" - -fs_user_mount_t fs_user_mount_flash; -mp_vfs_mount_t mp_vfs_mount_flash; - -static void make_empty_file(FATFS *fatfs, const char *path) { - FIL fp; - f_open(fatfs, &fp, path, FA_WRITE | FA_CREATE_ALWAYS); - f_close(&fp); -} - -// we don't make this function static because it needs a lot of stack and we -// want it to be executed without using stack within main() function -void filesystem_init(bool create_allowed, bool force_create) { - // init the vfs object - fs_user_mount_t *vfs_fat = &fs_user_mount_flash; - vfs_fat->flags = 0; - flash_init_vfs(vfs_fat); - - // try to mount the flash - FRESULT res = f_mount(&vfs_fat->fatfs); - - if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) { - // No filesystem so create a fresh one, or reformat has been requested. - uint8_t working_buf[_MAX_SS]; - res = f_mkfs(&vfs_fat->fatfs, FM_FAT, 0, working_buf, sizeof(working_buf)); - // Flush the new file system to make sure it's repaired immediately. - flash_flush(); - if (res != FR_OK) { - return; - } - - // set label - f_setlabel(&vfs_fat->fatfs, "CIRCUITPY"); - - // inhibit file indexing on MacOS - f_mkdir(&vfs_fat->fatfs, "/.fseventsd"); - make_empty_file(&vfs_fat->fatfs, "/.metadata_never_index"); - make_empty_file(&vfs_fat->fatfs, "/.Trashes"); - make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log"); - - // and ensure everything is flushed - flash_flush(); - } else if (res != FR_OK) { - return; - } - mp_vfs_mount_t *vfs = &mp_vfs_mount_flash; - vfs->str = "/"; - vfs->len = 1; - vfs->obj = MP_OBJ_FROM_PTR(vfs_fat); - vfs->next = NULL; - MP_STATE_VM(vfs_mount_table) = vfs; - - // The current directory is used as the boot up directory. - // It is set to the internal flash filesystem by default. - MP_STATE_PORT(vfs_cur) = vfs; -} - -void filesystem_flush(void) { - flash_flush(); -} - -void filesystem_writable_by_python(bool writable) { - flash_set_usb_writable(!writable); -} - -bool filesystem_present(void) { - return true; -} diff --git a/ports/atmel-samd/supervisor/internal_flash.c b/ports/atmel-samd/supervisor/internal_flash.c new file mode 100644 index 000000000..bf12ad0cd --- /dev/null +++ b/ports/atmel-samd/supervisor/internal_flash.c @@ -0,0 +1,209 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 +#include + +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "py/mphal.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "lib/oofatfs/ff.h" + +#ifdef SAMD21 +#include "hpl/pm/hpl_pm_base.h" +#endif +#include "hal/include/hal_flash.h" + +#include "supervisor/shared/rgb_led_status.h" + +static struct flash_descriptor supervisor_flash_desc; + +void supervisor_flash_init(void) { + // Activity LED for flash writes. + #ifdef MICROPY_HW_LED_MSC + struct port_config pin_conf; + port_get_config_defaults(&pin_conf); + + pin_conf.direction = PORT_PIN_DIR_OUTPUT; + port_pin_set_config(MICROPY_HW_LED_MSC, &pin_conf); + port_pin_set_output_level(MICROPY_HW_LED_MSC, false); + #endif + + #ifdef SAMD51 + hri_mclk_set_AHBMASK_NVMCTRL_bit(MCLK); + #endif + #ifdef SAMD21 + _pm_enable_bus_clock(PM_BUS_APBB, NVMCTRL); + #endif + flash_init(&supervisor_flash_desc, NVMCTRL); +} + +uint32_t supervisor_flash_get_block_size(void) { + return FILESYSTEM_BLOCK_SIZE; +} + +uint32_t supervisor_flash_get_block_count(void) { + return INTERNAL_FLASH_PART1_START_BLOCK + INTERNAL_FLASH_PART1_NUM_BLOCKS; +} + +void supervisor_flash_flush(void) { +} + +void flash_flush(void) { + supervisor_flash_flush(); +} + +static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) { + buf[0] = boot; + + if (num_blocks == 0) { + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + } else { + buf[1] = 0xff; + buf[2] = 0xff; + buf[3] = 0xff; + } + + buf[4] = type; + + if (num_blocks == 0) { + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + } else { + buf[5] = 0xff; + buf[6] = 0xff; + buf[7] = 0xff; + } + + buf[8] = start_block; + buf[9] = start_block >> 8; + buf[10] = start_block >> 16; + buf[11] = start_block >> 24; + + buf[12] = num_blocks; + buf[13] = num_blocks >> 8; + buf[14] = num_blocks >> 16; + buf[15] = num_blocks >> 24; +} + +static int32_t convert_block_to_flash_addr(uint32_t block) { + if (INTERNAL_FLASH_PART1_START_BLOCK <= block && block < INTERNAL_FLASH_PART1_START_BLOCK + INTERNAL_FLASH_PART1_NUM_BLOCKS) { + // a block in partition 1 + block -= INTERNAL_FLASH_PART1_START_BLOCK; + return INTERNAL_FLASH_MEM_SEG1_START_ADDR + block * FILESYSTEM_BLOCK_SIZE; + } + // bad block + return -1; +} + +bool supervisor_flash_read_block(uint8_t *dest, uint32_t block) { + if (block == 0) { + // fake the MBR so we can decide on our own partition table + + for (int i = 0; i < 446; i++) { + dest[i] = 0; + } + + build_partition(dest + 446, 0, 0x01 /* FAT12 */, INTERNAL_FLASH_PART1_START_BLOCK, INTERNAL_FLASH_PART1_NUM_BLOCKS); + build_partition(dest + 462, 0, 0, 0, 0); + build_partition(dest + 478, 0, 0, 0, 0); + build_partition(dest + 494, 0, 0, 0, 0); + + dest[510] = 0x55; + dest[511] = 0xaa; + + return true; + + } else { + // non-MBR block, get data from flash memory + int32_t src = convert_block_to_flash_addr(block); + if (src == -1) { + // bad block number + return false; + } + int32_t error_code = flash_read(&supervisor_flash_desc, src, dest, FILESYSTEM_BLOCK_SIZE); + return error_code == ERR_NONE; + } +} + +bool supervisor_flash_write_block(const uint8_t *src, uint32_t block) { + if (block == 0) { + // can't write MBR, but pretend we did + return true; + + } else { + #ifdef MICROPY_HW_LED_MSC + port_pin_set_output_level(MICROPY_HW_LED_MSC, true); + #endif + temp_status_color(ACTIVE_WRITE); + // non-MBR block, copy to cache + int32_t dest = convert_block_to_flash_addr(block); + if (dest == -1) { + // bad block number + return false; + } + int32_t error_code; + error_code = flash_erase(&supervisor_flash_desc, + dest, + FILESYSTEM_BLOCK_SIZE / flash_get_page_size(&supervisor_flash_desc)); + if (error_code != ERR_NONE) { + return false; + } + + error_code = flash_append(&supervisor_flash_desc, dest, src, FILESYSTEM_BLOCK_SIZE); + if (error_code != ERR_NONE) { + return false; + } + clear_temp_status(); + #ifdef MICROPY_HW_LED_MSC + port_pin_set_output_level(MICROPY_HW_LED_MSC, false); + #endif + return true; + } +} + +mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + if (!supervisor_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} + +mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + if (!supervisor_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} diff --git a/ports/atmel-samd/supervisor/internal_flash.h b/ports/atmel-samd/supervisor/internal_flash.h new file mode 100644 index 000000000..88c105386 --- /dev/null +++ b/ports/atmel-samd/supervisor/internal_flash.h @@ -0,0 +1,69 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H +#define MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H + +#include + +#include "mpconfigport.h" + +#include "sam.h" + +#define FLASH_ROOT_POINTERS + +#ifdef SAMD51 +#define TOTAL_INTERNAL_FLASH_SIZE (FLASH_SIZE / 2) +#endif + +#ifdef SAMD21 +#define TOTAL_INTERNAL_FLASH_SIZE 0x010000 +#endif + +#define INTERNAL_FLASH_MEM_SEG1_START_ADDR (FLASH_SIZE - TOTAL_INTERNAL_FLASH_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE) +#define INTERNAL_FLASH_PART1_START_BLOCK (0x1) +#define INTERNAL_FLASH_PART1_NUM_BLOCKS (TOTAL_INTERNAL_FLASH_SIZE / FILESYSTEM_BLOCK_SIZE) + +#define INTERNAL_FLASH_SYSTICK_MASK (0x1ff) // 512ms +#define INTERNAL_FLASH_IDLE_TICK(tick) (((tick) & INTERNAL_FLASH_SYSTICK_MASK) == 2) + +void internal_flash_init(void); +uint32_t internal_flash_get_block_size(void); +uint32_t internal_flash_get_block_count(void); +void internal_flash_irq_handler(void); +void internal_flash_flush(void); +bool internal_flash_read_block(uint8_t *dest, uint32_t block); +bool internal_flash_write_block(const uint8_t *src, uint32_t block); + +// these return 0 on success, non-zero on error +mp_uint_t internal_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); +mp_uint_t internal_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); + +extern const struct _mp_obj_type_t internal_flash_type; + +struct _fs_user_mount_t; +void flash_init_vfs(struct _fs_user_mount_t *vfs); + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H diff --git a/ports/atmel-samd/supervisor/internal_flash_root_pointers.h b/ports/atmel-samd/supervisor/internal_flash_root_pointers.h new file mode 100644 index 000000000..3e9148ce0 --- /dev/null +++ b/ports/atmel-samd/supervisor/internal_flash_root_pointers.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_ROOT_POINTERS_H +#define MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_ROOT_POINTERS_H + +#define FLASH_ROOT_POINTERS + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_ROOT_POINTERS_H diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 45b8ed1a4..9f951b035 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -32,7 +32,6 @@ #include "hal/include/hal_delay.h" #include "hal/include/hal_gpio.h" #include "hal/include/hal_init.h" -#include "hal/include/hal_usb_device.h" #include "hpl/gclk/hpl_gclk_base.h" #include "hpl/pm/hpl_pm_base.h" @@ -48,13 +47,13 @@ #include "common-hal/audiobusio/PDMIn.h" #include "common-hal/audiobusio/I2SOut.h" #include "common-hal/audioio/AudioOut.h" +#include "common-hal/busio/SPI.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/pulseio/PulseIn.h" #include "common-hal/pulseio/PulseOut.h" #include "common-hal/pulseio/PWMOut.h" #include "common-hal/rtc/RTC.h" #include "common-hal/touchio/TouchIn.h" -#include "common-hal/usb_hid/Device.h" #include "samd/cache.h" #include "samd/clocks.h" #include "samd/events.h" @@ -62,8 +61,10 @@ #include "samd/dma.h" #include "shared-bindings/rtc/__init__.h" #include "board_busses.h" +#include "reset.h" #include "tick.h" -#include "usb.h" + +#include "tusb.h" #ifdef CIRCUITPY_GAMEPAD_TICKS #include "shared-module/gamepad/__init__.h" @@ -225,28 +226,7 @@ safe_mode_t port_init(void) { } void reset_port(void) { - // Reset all SERCOMs except the ones being used by on-board devices. - Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS; - for (int i = 0; i < SERCOM_INST_NUM; i++) { -#ifdef SPI_FLASH_SERCOM - if (sercom_instances[i] == SPI_FLASH_SERCOM) { - continue; - } -#endif -#ifdef MICROPY_HW_APA102_SERCOM - if (sercom_instances[i] == MICROPY_HW_APA102_SERCOM) { - continue; - } -#endif -#ifdef CIRCUITPY_DISPLAYIO - // TODO(tannewt): Make this dynamic. - if (sercom_instances[i] == board_display_obj.bus.spi_desc.dev.prvt) { - continue; - } -#endif - // SWRST is same for all modes of SERCOMs. - sercom_instances[i]->SPI.CTRLA.bit.SWRST = 1; - } + reset_sercoms(); #if defined(EXPRESS_BOARD) && !defined(__SAMR21G18A__) audio_dma_reset(); @@ -290,13 +270,16 @@ void reset_port(void) { // gpio_set_pin_function(PIN_PB15, GPIO_PIN_FUNCTION_M); // GCLK1, D6 // #endif - usb_hid_reset(); - - if (usb_connected()) { + if (tud_cdc_connected()) { save_usb_clock_calibration(); } } +void reset_to_bootloader(void) { + _bootloader_dbl_tap = DBL_TAP_MAGIC; + reset(); +} + /** * \brief Default interrupt handler for unused IRQs. */ diff --git a/ports/atmel-samd/supervisor/qspi_flash.c b/ports/atmel-samd/supervisor/qspi_flash.c new file mode 100644 index 000000000..eca47b164 --- /dev/null +++ b/ports/atmel-samd/supervisor/qspi_flash.c @@ -0,0 +1,232 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/spi_flash_api.h" + +#include +#include + +#include "mpconfigboard.h" // for EXTERNAL_FLASH_QSPI_DUAL + +#include "supervisor/shared/external_flash/common_commands.h" +#include "supervisor/shared/external_flash/qspi_flash.h" +#include "samd/cache.h" +#include "samd/dma.h" + +#include "atmel_start_pins.h" +#include "hal_gpio.h" + +bool spi_flash_command(uint8_t command) { + QSPI->INSTRCTRL.bit.INSTR = command; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READ | + QSPI_INSTRFRAME_INSTREN; + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + +bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + + QSPI->INSTRCTRL.bit.INSTR = command; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READ | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_DATAEN; + + // Dummy read of INSTRFRAME needed to synchronize. + // See Instruction Transmission Flow Diagram, figure 37.9, page 995 + // and Example 4, page 998, section 37.6.8.5. + (volatile uint32_t) QSPI->INSTRFRAME.reg; + + memcpy(response, (uint8_t *) QSPI_AHB, length); + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + samd_peripherals_enable_cache(); + + return true; +} + +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + + QSPI->INSTRCTRL.bit.INSTR = command; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_WRITE | + QSPI_INSTRFRAME_INSTREN | + (data != NULL ? QSPI_INSTRFRAME_DATAEN : 0); + + // Dummy read of INSTRFRAME needed to synchronize. + // See Instruction Transmission Flow Diagram, figure 37.9, page 995 + // and Example 4, page 998, section 37.6.8.5. + (volatile uint32_t) QSPI->INSTRFRAME.reg; + + if (data != NULL) { + memcpy((uint8_t *) QSPI_AHB, data, length); + } + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + samd_peripherals_enable_cache(); + + return true; +} + +bool spi_flash_sector_command(uint8_t command, uint32_t address) { + QSPI->INSTRCTRL.bit.INSTR = command; + QSPI->INSTRADDR.bit.ADDR = address; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_WRITE | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN; + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + + QSPI->INSTRCTRL.bit.INSTR = CMD_PAGE_PROGRAM; + uint32_t mode = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI; + + QSPI->INSTRFRAME.reg = mode | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN | + QSPI_INSTRFRAME_DATAEN; + + memcpy(((uint8_t *) QSPI_AHB) + address, data, length); + // TODO(tannewt): Fix DMA and enable it. + // qspi_dma_write(address, data, length); + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + samd_peripherals_enable_cache(); + + return true; +} + +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { + samd_peripherals_disable_and_clear_cache(); + + #ifdef EXTERNAL_FLASH_QSPI_DUAL + QSPI->INSTRCTRL.bit.INSTR = CMD_DUAL_READ; + uint32_t mode = QSPI_INSTRFRAME_WIDTH_DUAL_OUTPUT; + #else + QSPI->INSTRCTRL.bit.INSTR = CMD_QUAD_READ; + uint32_t mode = QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT; + #endif + + QSPI->INSTRFRAME.reg = mode | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READMEMORY | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN | + QSPI_INSTRFRAME_DATAEN | + QSPI_INSTRFRAME_DUMMYLEN(8); + + memcpy(data, ((uint8_t *) QSPI_AHB) + address, length); + // TODO(tannewt): Fix DMA and enable it. + // qspi_dma_read(address, data, length); + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + samd_peripherals_enable_cache(); + + return true; +} + + +void spi_flash_init(void) { + MCLK->APBCMASK.bit.QSPI_ = true; + MCLK->AHBMASK.bit.QSPI_ = true; + MCLK->AHBMASK.bit.QSPI_2X_ = false; // Only true if we are doing DDR. + + QSPI->CTRLA.reg = QSPI_CTRLA_SWRST; + // We don't need to wait because we're running as fast as the CPU. + + // Slow, good for debugging with Saleae + // QSPI->BAUD.bit.BAUD = 32; + // Super fast, may be unreliable when Saleae is connected to high speed lines. + QSPI->BAUD.bit.BAUD = 2; + QSPI->CTRLB.reg = QSPI_CTRLB_MODE_MEMORY | // Serial memory mode (map to QSPI_AHB) + QSPI_CTRLB_DATALEN_8BITS | + QSPI_CTRLB_CSMODE_LASTXFER; + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE; + + // The QSPI is only connected to one set of pins in the SAMD51 so we can hard code it. + uint32_t pins[6] = {PIN_PA08, PIN_PA09, PIN_PA10, PIN_PA11, PIN_PB10, PIN_PB11}; + for (uint8_t i = 0; i < 6; i++) { + gpio_set_pin_direction(pins[i], GPIO_DIRECTION_IN); + gpio_set_pin_pull_mode(pins[i], GPIO_PULL_OFF); + gpio_set_pin_function(pins[i], GPIO_PIN_FUNCTION_H); + } +} + +void spi_flash_init_device(const external_flash_device* device) { + check_quad_enable(device); + + // TODO(tannewt): Adjust the speed for the found device. +} diff --git a/ports/atmel-samd/supervisor/serial.c b/ports/atmel-samd/supervisor/serial.c deleted file mode 100644 index 4917dfb5b..000000000 --- a/ports/atmel-samd/supervisor/serial.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 - -#include "supervisor/serial.h" - -#include "common-hal/usb_hid/Device.h" - -#include "usb.h" -#include "genhdr/autogen_usb_descriptor.h" - -// Serial number as hex characters. This writes directly to the USB -// descriptor. -void load_serial_number(void) { - char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F'}; - #ifdef SAMD21 - uint32_t* addresses[4] = {(uint32_t *) 0x0080A00C, (uint32_t *) 0x0080A040, - (uint32_t *) 0x0080A044, (uint32_t *) 0x0080A048}; - #endif - #ifdef SAMD51 - uint32_t* addresses[4] = {(uint32_t *) 0x008061FC, (uint32_t *) 0x00806010, - (uint32_t *) 0x00806014, (uint32_t *) 0x00806018}; - #endif - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 8; j++) { - uint8_t nibble = (*(addresses[i]) >> j * 4) & 0xf; - // Strings are UTF-16-LE encoded. - serial_number[i * 16 + j * 2] = nibble_to_hex[nibble]; - serial_number[i * 16 + j * 2 + 1] = 0; - } - } -} - -void serial_init(void) { - load_serial_number(); - init_usb(); - usb_hid_init(); -} - -bool serial_connected(void) { - return usb_connected(); -} - -char serial_read(void) { - return usb_read(); -} - -bool serial_bytes_available(void) { - return usb_bytes_available(); -} - -void serial_write(const char* text) { - usb_write(text, strlen(text)); -} diff --git a/ports/atmel-samd/supervisor/usb.c b/ports/atmel-samd/supervisor/usb.c new file mode 100644 index 000000000..d13ca0ef1 --- /dev/null +++ b/ports/atmel-samd/supervisor/usb.c @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "atmel_start_pins.h" +#include "hpl/pm/hpl_pm_base.h" +#include "hpl/gclk/hpl_gclk_base.h" +#include "hal_gpio.h" + +void init_usb_hardware(void) { + #ifdef SAMD21 + _pm_enable_bus_clock(PM_BUS_APBB, USB); + _pm_enable_bus_clock(PM_BUS_AHB, USB); + _gclk_enable_channel(USB_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val); + #endif + + #ifdef SAMD51 + hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN); + hri_mclk_set_AHBMASK_USB_bit(MCLK); + hri_mclk_set_APBBMASK_USB_bit(MCLK); + #endif + + gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT); + gpio_set_pin_level(PIN_PA24, false); + gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF); + gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT); + gpio_set_pin_level(PIN_PA25, false); + gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF); + #ifdef SAMD21 + gpio_set_pin_function(PIN_PA24, PINMUX_PA24G_USB_DM); + gpio_set_pin_function(PIN_PA25, PINMUX_PA25G_USB_DP); + #endif + #ifdef SAMD51 + gpio_set_pin_function(PIN_PA24, PINMUX_PA24H_USB_DM); + gpio_set_pin_function(PIN_PA25, PINMUX_PA25H_USB_DP); + #endif +} diff --git a/ports/atmel-samd/tick.h b/ports/atmel-samd/tick.h index 80e7bc9af..c8c8d739a 100644 --- a/ports/atmel-samd/tick.h +++ b/ports/atmel-samd/tick.h @@ -26,7 +26,7 @@ #ifndef MICROPY_INCLUDED_ATMEL_SAMD_TICK_H #define MICROPY_INCLUDED_ATMEL_SAMD_TICK_H -#include "mpconfigport.h" +#include "py/mpconfig.h" extern volatile uint64_t ticks_ms; diff --git a/ports/atmel-samd/tools/gen_usb_descriptor.py b/ports/atmel-samd/tools/gen_usb_descriptor.py deleted file mode 100644 index efa8124bf..000000000 --- a/ports/atmel-samd/tools/gen_usb_descriptor.py +++ /dev/null @@ -1,353 +0,0 @@ -import argparse - -import os -import sys - -# path hacking -sys.path.append("../../tools/usb_descriptor") - -from adafruit_usb_descriptor import cdc, hid, msc, standard, util -import hid_report_descriptors - -parser = argparse.ArgumentParser(description='Generate USB descriptors.') -parser.add_argument('--manufacturer', type=str, - help='manufacturer of the device') -parser.add_argument('--product', type=str, - help='product name of the device') -parser.add_argument('--vid', type=lambda x: int(x, 16), - help='vendor id') -parser.add_argument('--pid', type=lambda x: int(x, 16), - help='product id') -parser.add_argument('--serial_number_length', type=int, default=32, - help='length needed for the serial number in digits') -parser.add_argument('--output_c_file', type=argparse.FileType('w'), required=True) -parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True) - -args = parser.parse_args() - -class StringIndex: - """Assign a monotonically increasing index to each unique string. Start with 0.""" - string_to_index = {} - strings = [] - - @classmethod - def index(cls, string): - if string in cls.string_to_index: - return cls.string_to_index[string] - else: - idx = len(cls.strings) - cls.string_to_index[string] = idx - cls.strings.append(string) - return idx - - @classmethod - def strings_in_order(cls): - return cls.strings - - - -# langid must be the 0th string descriptor -LANGID_INDEX = StringIndex.index("\u0409") -assert LANGID_INDEX == 0 -SERIAL_NUMBER_INDEX = StringIndex.index("S" * args.serial_number_length) - -device = standard.DeviceDescriptor( - description="top", - idVendor=args.vid, - idProduct=args.pid, - iManufacturer=StringIndex.index(args.manufacturer), - iProduct=StringIndex.index(args.product), - iSerialNumber=SERIAL_NUMBER_INDEX) - -# Interface numbers are interface-set local and endpoints are interface local -# until util.join_interfaces renumbers them. - -cdc_union = cdc.Union( - description="CDC comm", - bMasterInterface=0x00, # Adjust this after interfaces are renumbered. - bSlaveInterface_list=[0x01]) # Adjust this after interfaces are renumbered. - -cdc_call_management = cdc.CallManagement( - description="CDC comm", - bmCapabilities=0x01, - bDataInterface=0x01) # Adjust this after interfaces are renumbered. - -cdc_comm_interface = standard.InterfaceDescriptor( - description="CDC comm", - bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class - bInterfaceSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model - bInterfaceProtocol=cdc.CDC_PROTOCOL_NONE, - iInterface=StringIndex.index("CircuitPython CDC control"), - subdescriptors=[ - cdc.Header( - description="CDC comm", - bcdCDC=0x0110), - cdc_call_management, - cdc.AbstractControlManagement( - description="CDC comm", - bmCapabilities=0x02), - cdc_union, - standard.EndpointDescriptor( - description="CDC comm in", - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - wMaxPacketSize=0x0040, - bInterval=0x10) - ]) - -cdc_data_interface = standard.InterfaceDescriptor( - description="CDC data", - bInterfaceClass=cdc.CDC_CLASS_DATA, - iInterface=StringIndex.index("CircuitPython CDC data"), - subdescriptors=[ - standard.EndpointDescriptor( - description="CDC data out", - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK), - standard.EndpointDescriptor( - description="CDC data in", - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK), - ]) - -cdc_interfaces = [cdc_comm_interface, cdc_data_interface] - -msc_interfaces = [ - standard.InterfaceDescriptor( - description="MSC", - bInterfaceClass=msc.MSC_CLASS, - bInterfaceSubClass=msc.MSC_SUBCLASS_TRANSPARENT, - bInterfaceProtocol=msc.MSC_PROTOCOL_BULK, - iInterface=StringIndex.index("CircuitPython Mass Storage"), - subdescriptors=[ - standard.EndpointDescriptor( - description="MSC in", - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0), - standard.EndpointDescriptor( - description="MSC out", - bEndpointAddress=0x1 | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0) - ] - ) -] - -# Include only these HID devices. -# DIGITIZER works on Linux but conflicts with MOUSE, so leave it out for now. -hid_devices = ("KEYBOARD", "MOUSE", "CONSUMER", "GAMEPAD") - -combined_hid_report_descriptor = hid.ReportDescriptor( - description="MULTIDEVICE", - report_descriptor=b''.join( - hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor for name in hid_devices )) - -hid_report_ids_dict = { name: hid_report_descriptors.REPORT_IDS[name] for name in hid_devices } -hid_report_lengths_dict = { name: hid_report_descriptors.REPORT_LENGTHS[name] for name in hid_devices } -hid_max_report_length = max(hid_report_lengths_dict.values()) - -# ASF4 expects keyboard and generic devices to have both in and out endpoints, -# and will fail (possibly silently) if both are not supplied. -hid_endpoint_in_descriptor = standard.EndpointDescriptor( - description="HID in", - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - bInterval=10) - -hid_endpoint_out_descriptor = standard.EndpointDescriptor( - description="HID out", - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - bInterval=10) - -hid_interfaces = [ - standard.InterfaceDescriptor( - description="HID Multiple Devices", - bInterfaceClass=hid.HID_CLASS, - bInterfaceSubClass=hid.HID_SUBCLASS_NOBOOT, - bInterfaceProtocol=hid.HID_PROTOCOL_NONE, - iInterface=StringIndex.index("CircuitPython HID"), - subdescriptors=[ - hid.HIDDescriptor( - description="HID", - wDescriptorLength=len(bytes(combined_hid_report_descriptor))), - hid_endpoint_in_descriptor, - hid_endpoint_out_descriptor, - ] - ), - ] - -# This will renumber the endpoints to make them unique across descriptors, -# and renumber the interfaces in order. But we still need to fix up certain -# interface cross-references. -interfaces = util.join_interfaces(cdc_interfaces, msc_interfaces, hid_interfaces) - -# Now adjust the CDC interface cross-references. - -cdc_union.bMasterInterface = cdc_comm_interface.bInterfaceNumber -cdc_union.bSlaveInterface_list = [cdc_data_interface.bInterfaceNumber] - -cdc_call_management.bDataInterface = cdc_data_interface.bInterfaceNumber - -cdc_iad = standard.InterfaceAssociationDescriptor( - description="CDC IAD", - bFirstInterface=cdc_comm_interface.bInterfaceNumber, - bInterfaceCount=len(cdc_interfaces), - bFunctionClass=0x2, # Communications Device Class - bFunctionSubClass=0x2, # Abstract control model - bFunctionProtocol=0x1) - -configuration = standard.ConfigurationDescriptor( - description="Composite configuration", - wTotalLength=(standard.ConfigurationDescriptor.bLength + - cdc_iad.bLength + - sum([len(bytes(x)) for x in interfaces])), - bNumInterfaces=len(interfaces)) - -descriptor_list = [] -descriptor_list.append(device) -descriptor_list.append(configuration) -descriptor_list.append(cdc_iad) -descriptor_list.extend(cdc_interfaces) -descriptor_list.extend(msc_interfaces) -# Put the CDC IAD just before the CDC interfaces. -# There appears to be a bug in the Windows composite USB driver that requests the -# HID report descriptor with the wrong interface number if the HID interface is not given -# first. However, it still fetches the descriptor anyway. We could reorder the interfaces but -# the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it -# there for backwards compatibility. -descriptor_list.extend(hid_interfaces) - -string_descriptors = [standard.StringDescriptor(string) for string in StringIndex.strings_in_order()] -serial_number_descriptor = string_descriptors[SERIAL_NUMBER_INDEX] -descriptor_list.extend(string_descriptors) - -c_file = args.output_c_file -h_file = args.output_h_file - - -c_file.write("""\ -#include - -#include "{H_FILE_NAME}" - -#include "usb/device/usbdc.h" - -""".format(H_FILE_NAME=h_file.name)) - -c_file.write("""\ -uint8_t usb_descriptors[] = { -""") - -# Write out all the regular descriptors as one long array (that's how ASF4 does it). -descriptor_length = 0 -serial_number_offset = None -for descriptor in descriptor_list: - c_file.write("""\ -// {DESCRIPTION} : {CLASS} -""".format(DESCRIPTION=descriptor.description, - CLASS=descriptor.__class__)) - - b = bytes(descriptor) - i = 0 - - if descriptor == serial_number_descriptor: - # Add two for bLength and bDescriptorType. - serial_number_offset = descriptor_length + 2 - - # This prints each subdescriptor on a separate line. - while i < len(b): - length = b[i] - for j in range(length): - c_file.write("0x{:02x}, ".format(b[i + j])) - c_file.write("\n") - i += length - descriptor_length += length - - -c_file.write("""\ -}; -""") - -# Now we values we need for the .h file. -h_file.write("""\ -#ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H -#define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H - -#define SERIAL_NUMBER_OFFSET {SERIAL_NUMBER_OFFSET} -#define SERIAL_NUMBER_LENGTH {SERIAL_NUMBER_LENGTH} -uint8_t* serial_number; - -uint8_t hid_report_descriptor[{HID_REPORT_DESCRIPTOR_LENGTH}]; -#define USB_HID_ENDPOINT_IN {HID_ENDPOINT_IN_ADDRESS} -#define USB_HID_ENDPOINT_OUT {HID_ENDPOINT_OUT_ADDRESS} - -""" -.format(SERIAL_NUMBER_OFFSET=serial_number_offset, - SERIAL_NUMBER_LENGTH=args.serial_number_length, - HID_REPORT_DESCRIPTOR_LENGTH=len(bytes(combined_hid_report_descriptor)), - HID_ENDPOINT_IN_ADDRESS=hex(hid_endpoint_in_descriptor.bEndpointAddress), - HID_ENDPOINT_OUT_ADDRESS=hex(hid_endpoint_out_descriptor.bEndpointAddress))) - -# Write out #define's that declare which endpoints are in use. -# These provide information for declaring cache sizes and perhaps other things at compile time -for interface in interfaces: - for subdescriptor in interface.subdescriptors: - if isinstance(subdescriptor, standard.EndpointDescriptor): - endpoint_num = subdescriptor.bEndpointAddress & standard.EndpointDescriptor.NUMBER_MASK - endpoint_in = ((subdescriptor.bEndpointAddress & standard.EndpointDescriptor.DIRECTION_MASK) == - standard.EndpointDescriptor.DIRECTION_IN) - h_file.write("""\ -#define USB_ENDPOINT_{NUMBER}_{DIRECTION}_USED 1 -""".format(NUMBER=endpoint_num, - DIRECTION="IN" if endpoint_in else "OUT")) - -h_file.write("\n") - -# #define the report ID's used in the combined HID descriptor -for name, id in hid_report_ids_dict.items(): - h_file.write("""\ -#define USB_HID_REPORT_ID_{NAME} {ID} -""".format(NAME=name, - ID = id)) - -h_file.write("\n") - -# #define the report sizes used in the combined HID descriptor -for name, length in hid_report_lengths_dict.items(): - h_file.write("""\ -#define USB_HID_REPORT_LENGTH_{NAME} {LENGTH} -""".format(NAME=name, - LENGTH=length)) - -h_file.write("\n") - -h_file.write("""\ -#define USB_HID_NUM_DEVICES {NUM_DEVICES} -#define USB_HID_MAX_REPORT_LENGTH {MAX_LENGTH} -""".format(NUM_DEVICES=len(hid_report_lengths_dict), - MAX_LENGTH=hid_max_report_length)) - - - -# Write out the report descriptor and info -c_file.write("""\ -uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ -""".format(HID_DESCRIPTOR_LENGTH=len(bytes(combined_hid_report_descriptor)))) - -for b in bytes(combined_hid_report_descriptor): - c_file.write("0x{:02x}, ".format(b)) -c_file.write(""" -}; -""") - -c_file.write("""\ - -struct usbd_descriptors descriptor_bounds = {{usb_descriptors, usb_descriptors + sizeof(usb_descriptors)}}; -uint8_t* serial_number = usb_descriptors + {SERIAL_NUMBER_OFFSET}; -""".format(SERIAL_NUMBER_OFFSET=serial_number_offset)) - -h_file.write("""\ -#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H -""") diff --git a/ports/atmel-samd/tools/hid_report_descriptors.py b/ports/atmel-samd/tools/hid_report_descriptors.py deleted file mode 100644 index f3b28ebcf..000000000 --- a/ports/atmel-samd/tools/hid_report_descriptors.py +++ /dev/null @@ -1,239 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2018 Dan Halbert for Adafruit Industries -# -# 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. - -import struct - -""" -HID specific descriptors -======================== - -* Author(s): Dan Halbert -""" - -from adafruit_usb_descriptor import hid - -REPORT_IDS = { - "KEYBOARD" : 1, - "MOUSE" : 2, - "CONSUMER" : 3, - "SYS_CONTROL" : 4, - "GAMEPAD" : 5, - "DIGITIZER" : 6, - } - -# Byte count for each kind of report. Length does not include report ID in first byte. -REPORT_LENGTHS = { - "KEYBOARD" : 8, - "MOUSE" : 4, - "CONSUMER" : 2, - "SYS_CONTROL" : 1, - "GAMEPAD" : 6, - "DIGITIZER" : 5, - } - -KEYBOARD_WITH_ID = hid.ReportDescriptor( - description="KEYBOARD", - report_descriptor=bytes([ - # Regular keyboard - 0x05, 0x01, # Usage Page (Generic Desktop) - 0x09, 0x06, # Usage (Keyboard) - 0xA1, 0x01, # Collection (Application) - 0x85, REPORT_IDS["KEYBOARD"], # Report ID (1) - 0x05, 0x07, # Usage Page (Keyboard) - 0x19, 224, # Usage Minimum (224) - 0x29, 231, # Usage Maximum (231) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x01, # Logical Maximum (1) - 0x75, 0x01, # Report Size (1) - 0x95, 0x08, # Report Count (8) - 0x81, 0x02, # Input (Data, Variable, Absolute) - 0x81, 0x01, # Input (Constant) - 0x19, 0x00, # Usage Minimum (0) - 0x29, 101, # Usage Maximum (101) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 101, # Logical Maximum (101) - 0x75, 0x08, # Report Size (8) - 0x95, 0x06, # Report Count (6) - 0x81, 0x00, # Input (Data, Array) - 0x05, 0x08, # Usage Page (LED) - 0x19, 0x01, # Usage Minimum (1) - 0x29, 0x05, # Usage Maximum (5) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x01, # Logical Maximum (1) - 0x75, 0x01, # Report Size (1) - 0x95, 0x05, # Report Count (5) - 0x91, 0x02, # Output (Data, Variable, Absolute) - 0x95, 0x03, # Report Count (3) - 0x91, 0x01, # Output (Constant) - 0xC0, # End Collection - ])) - -MOUSE_WITH_ID = hid.ReportDescriptor( - description="MOUSE", - report_descriptor=bytes([ - # Regular mouse - 0x05, 0x01, # Usage Page (Generic Desktop) - 0x09, 0x02, # Usage (Mouse) - 0xA1, 0x01, # Collection (Application) - 0x09, 0x01, # Usage (Pointer) - 0xA1, 0x00, # Collection (Physical) - 0x85, REPORT_IDS["MOUSE"], # Report ID (n) - 0x05, 0x09, # Usage Page (Button) - 0x19, 0x01, # Usage Minimum (0x01) - 0x29, 0x05, # Usage Maximum (0x05) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x01, # Logical Maximum (1) - 0x95, 0x05, # Report Count (5) - 0x75, 0x01, # Report Size (1) - 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x95, 0x01, # Report Count (1) - 0x75, 0x03, # Report Size (3) - 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) - 0x09, 0x30, # Usage (X) - 0x09, 0x31, # Usage (Y) - 0x15, 0x81, # Logical Minimum (-127) - 0x25, 0x7F, # Logical Maximum (127) - 0x75, 0x08, # Report Size (8) - 0x95, 0x02, # Report Count (2) - 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 0x09, 0x38, # Usage (Wheel) - 0x15, 0x81, # Logical Minimum (-127) - 0x25, 0x7F, # Logical Maximum (127) - 0x75, 0x08, # Report Size (8) - 0x95, 0x01, # Report Count (1) - 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, # End Collection - 0xC0, # End Collection - ])) - -CONSUMER_WITH_ID = hid.ReportDescriptor( - description="CONSUMER", - report_descriptor=bytes([ - # Consumer ("multimedia") keys - 0x05, 0x0C, # Usage Page (Consumer) - 0x09, 0x01, # Usage (Consumer Control) - 0xA1, 0x01, # Collection (Application) - 0x85, REPORT_IDS["CONSUMER"], # Report ID (n) - 0x75, 0x10, # Report Size (16) - 0x95, 0x01, # Report Count (1) - 0x15, 0x01, # Logical Minimum (1) - 0x26, 0x8C, 0x02, # Logical Maximum (652) - 0x19, 0x01, # Usage Minimum (Consumer Control) - 0x2A, 0x8C, 0x02, # Usage Maximum (AC Send) - 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, # End Collection - ])) - -SYS_CONTROL_WITH_ID = hid.ReportDescriptor( - description="SYS_CONTROL", - report_descriptor=bytes([ - # Power controls - 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) - 0x09, 0x80, # Usage (Sys Control) - 0xA1, 0x01, # Collection (Application) - 0x85, REPORT_IDS["SYS_CONTROL"], # Report ID (n) - 0x75, 0x02, # Report Size (2) - 0x95, 0x01, # Report Count (1) - 0x15, 0x01, # Logical Minimum (1) - 0x25, 0x03, # Logical Maximum (3) - 0x09, 0x82, # Usage (Sys Sleep) - 0x09, 0x81, # Usage (Sys Power Down) - 0x09, 0x83, # Usage (Sys Wake Up) - 0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State) - 0x75, 0x06, # Report Size (6) - 0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, # End Collection - ])) - -GAMEPAD_WITH_ID = hid.ReportDescriptor( - description="GAMEPAD", - report_descriptor=bytes([ - # Gamepad with 16 buttons and two joysticks - 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) - 0x09, 0x05, # Usage (Game Pad) - 0xA1, 0x01, # Collection (Application) - 0x85, REPORT_IDS["GAMEPAD"], # Report ID (n) - 0x05, 0x09, # Usage Page (Button) - 0x19, 0x01, # Usage Minimum (Button 1) - 0x29, 0x10, # Usage Maximum (Button 16) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x01, # Logical Maximum (1) - 0x75, 0x01, # Report Size (1) - 0x95, 0x10, # Report Count (16) - 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) - 0x15, 0x81, # Logical Minimum (-127) - 0x25, 0x7F, # Logical Maximum (127) - 0x09, 0x30, # Usage (X) - 0x09, 0x31, # Usage (Y) - 0x09, 0x32, # Usage (Z) - 0x09, 0x35, # Usage (Rz) - 0x75, 0x08, # Report Size (8) - 0x95, 0x04, # Report Count (4) - 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, # End Collection - ])) - -DIGITIZER_WITH_ID = hid.ReportDescriptor( - description="DIGITIZER", - report_descriptor=bytes([ - # Digitizer (used as an absolute pointer) - 0x05, 0x0D, # Usage Page (Digitizers) - 0x09, 0x02, # Usage (Pen) - 0xA1, 0x01, # Collection (Application) - 0x85, REPORT_IDS["DIGITIZER"], # Report ID (n) - 0x09, 0x01, # Usage (Stylus) - 0xA1, 0x00, # Collection (Physical) - 0x09, 0x32, # Usage (In-Range) - 0x09, 0x42, # Usage (Tip Switch) - 0x09, 0x44, # Usage (Barrel Switch) - 0x09, 0x45, # Usage (Eraser Switch) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x01, # Logical Maximum (1) - 0x75, 0x01, # Report Size (1) - 0x95, 0x04, # Report Count (4) - 0x81, 0x02, # Input (Data,Var,Abs) - 0x75, 0x04, # Report Size (4) -- Filler - 0x95, 0x01, # Report Count (1) -- Filler - 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) - 0x15, 0x00, # Logical Minimum (0) - 0x26, 0xff, 0x7f, # Logical Maximum (32767) - 0x09, 0x30, # Usage (X) - 0x09, 0x31, # Usage (Y) - 0x75, 0x10, # Report Size (16) - 0x95, 0x02, # Report Count (2) - 0x81, 0x02, # Input (Data,Var,Abs) - 0xC0, # End Collection - 0xC0, # End Collection - ])) - -# Byte count for each kind of report. Length does not include report ID in first byte. -REPORT_DESCRIPTORS = { - "KEYBOARD" : KEYBOARD_WITH_ID, - "MOUSE" : MOUSE_WITH_ID, - "CONSUMER" : CONSUMER_WITH_ID, - "SYS_CONTROL" : SYS_CONTROL_WITH_ID, - "GAMEPAD" : GAMEPAD_WITH_ID, - "DIGITIZER" : DIGITIZER_WITH_ID, - } diff --git a/ports/atmel-samd/usb.c b/ports/atmel-samd/usb.c deleted file mode 100644 index 1f1ba550f..000000000 --- a/ports/atmel-samd/usb.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "usb.h" - -#include - -// We must include this early because it sets values used in the ASF4 includes -// below. -#include "py/mpconfig.h" - -#include "hal/include/hal_gpio.h" -#include "usb/class/cdc/device/cdcdf_acm.h" -#include "usb/class/hid/device/hiddf_generic.h" -#include "usb/class/composite/device/composite_desc.h" -#include "usb/class/msc/device/mscdf.h" -#include "peripheral_clk_config.h" -#include "hpl/pm/hpl_pm_base.h" -#include "hpl/gclk/hpl_gclk_base.h" - -#include "lib/utils/interrupt_char.h" -#include "genhdr/autogen_usb_descriptor.h" -#include "reset.h" -#include "usb_mass_storage.h" - -#include "supervisor/shared/autoreload.h" - -extern struct usbd_descriptors descriptor_bounds; - -// Store received characters on our own so that we can filter control characters -// and act immediately on CTRL-C for example. - -// Receive buffer -static uint8_t usb_rx_buf[USB_RX_BUF_SIZE]; - -// Receive buffer head -static volatile uint8_t usb_rx_buf_head = 0; - -// Receive buffer tail -static volatile uint8_t usb_rx_buf_tail = 0; - -// Number of bytes in receive buffer -volatile uint8_t usb_rx_count = 0; - -volatile bool mp_cdc_enabled = false; -volatile bool usb_transmitting = false; - -/** Ctrl endpoint buffer */ -COMPILER_ALIGNED(4) static uint8_t ctrl_buffer[64]; - -static void init_hardware(void) { - #ifdef SAMD21 - _pm_enable_bus_clock(PM_BUS_APBB, USB); - _pm_enable_bus_clock(PM_BUS_AHB, USB); - _gclk_enable_channel(USB_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val); - #endif - - #ifdef SAMD51 - hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, CONF_GCLK_USB_SRC | GCLK_PCHCTRL_CHEN); - hri_mclk_set_AHBMASK_USB_bit(MCLK); - hri_mclk_set_APBBMASK_USB_bit(MCLK); - #endif - - usb_d_init(); - - gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT); - gpio_set_pin_level(PIN_PA24, false); - gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF); - gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT); - gpio_set_pin_level(PIN_PA25, false); - gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF); - #ifdef SAMD21 - gpio_set_pin_function(PIN_PA24, PINMUX_PA24G_USB_DM); - gpio_set_pin_function(PIN_PA25, PINMUX_PA25G_USB_DP); - #endif - #ifdef SAMD51 - gpio_set_pin_function(PIN_PA24, PINMUX_PA24H_USB_DM); - gpio_set_pin_function(PIN_PA25, PINMUX_PA25H_USB_DP); - #endif -} - -#define CDC_BULKOUT_SIZE CONF_USB_COMPOSITE_CDC_ACM_DATA_BULKOUT_MAXPKSZ -COMPILER_ALIGNED(4) uint8_t cdc_packet_buffer[CDC_BULKOUT_SIZE]; -static volatile bool pending_read; - -static int32_t start_read(void) { - pending_read = true; - int32_t result = cdcdf_acm_read(cdc_packet_buffer, CDC_BULKOUT_SIZE); - if (result != ERR_NONE) { - pending_read = false; - } - return result; -} - -static bool read_complete(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count) { - if (rc != USB_XFER_DONE) { - return false; // No errors. - } - pending_read = false; - volatile hal_atomic_t flags; - atomic_enter_critical(&flags); - // If our buffer can't fit the data received, then error out. - if (count > (uint8_t) (USB_RX_BUF_SIZE - usb_rx_count)) { - atomic_leave_critical(&flags); - return true; - } - - for (uint16_t i = 0; i < count; i++) { - uint8_t c = cdc_packet_buffer[i]; - if (c == mp_interrupt_char) { - mp_keyboard_interrupt(); - // If interrupted, flush all the input. - usb_rx_count = 0; - usb_rx_buf_head = 0; - usb_rx_buf_tail = 0; - break; - } else { - // The count of characters present in receive buffer is - // incremented. - usb_rx_count++; - usb_rx_buf[usb_rx_buf_tail] = c; - usb_rx_buf_tail++; - if (usb_rx_buf_tail == USB_RX_BUF_SIZE) { - // Reached the end of buffer, revert back to beginning of - // buffer. - usb_rx_buf_tail = 0; - } - } - } - atomic_leave_critical(&flags); - - /* No error. */ - return false; -} - -static bool write_complete(const uint8_t ep, - const enum usb_xfer_code rc, - const uint32_t count) { - if (rc != USB_XFER_DONE) { - return false; // No errors. - } - // This is called after writes are finished. - - usb_transmitting = false; - - /* No error. */ - return false; -} - -volatile bool reset_on_disconnect = false; -volatile bool cdc_connected = false; - -static bool usb_device_cb_state_c(usb_cdc_control_signal_t state) -{ - cdc_connected = state.rs232.DTR; - if (state.rs232.DTR) { - } else if (!state.rs232.DTR && reset_on_disconnect) { - reset_to_bootloader(); - } - - /* No error. */ - return false; -} - -static bool usb_device_cb_line_coding_c(const usb_cdc_line_coding_t* coding) -{ - reset_on_disconnect = coding->dwDTERate == 1200; - /* Ok to change. */ - return true; -} - -void init_usb(void) { - init_hardware(); - - mp_cdc_enabled = false; - - usbdc_init(ctrl_buffer); - - /* usbdc_register_function inside */ - cdcdf_acm_init(); - pending_read = false; - - mscdf_init(1); - mscdf_register_callback(MSCDF_CB_INQUIRY_DISK, (FUNC_PTR)usb_msc_inquiry_info); - mscdf_register_callback(MSCDF_CB_GET_DISK_CAPACITY, (FUNC_PTR)usb_msc_get_capacity); - mscdf_register_callback(MSCDF_CB_START_READ_DISK, (FUNC_PTR)usb_msc_new_read); - mscdf_register_callback(MSCDF_CB_START_WRITE_DISK, (FUNC_PTR)usb_msc_new_write); - mscdf_register_callback(MSCDF_CB_EJECT_DISK, (FUNC_PTR)usb_msc_disk_eject); - mscdf_register_callback(MSCDF_CB_TEST_DISK_READY, (FUNC_PTR)usb_msc_disk_is_ready); - mscdf_register_callback(MSCDF_CB_XFER_BLOCKS_DONE, (FUNC_PTR)usb_msc_xfer_done); - mscdf_register_callback(MSCDF_CB_IS_WRITABLE, (FUNC_PTR)usb_msc_disk_is_writable); - - hiddf_generic_init(hid_report_descriptor, sizeof(hid_report_descriptor)); - - usbdc_start(&descriptor_bounds); - - usbdc_attach(); - - -} - -static bool cdc_enabled(void) { - if (!cdcdf_acm_is_enabled()) { - mp_cdc_enabled = false; - return false; - } - if (!mp_cdc_enabled) { - cdcdf_acm_register_callback(CDCDF_ACM_CB_READ, (FUNC_PTR)read_complete); - cdcdf_acm_register_callback(CDCDF_ACM_CB_WRITE, (FUNC_PTR)write_complete); - cdcdf_acm_register_callback(CDCDF_ACM_CB_STATE_C, (FUNC_PTR)usb_device_cb_state_c); - cdcdf_acm_register_callback(CDCDF_ACM_CB_LINE_CODING_C, (FUNC_PTR)usb_device_cb_line_coding_c); - mp_cdc_enabled = true; - } - - return true; -} - -bool usb_bytes_available(void) { - // Check if the buffer has data, but not enough - // space to hold another read. - if (usb_rx_count > USB_RX_BUF_SIZE - CDC_BULKOUT_SIZE) { - return true; - } - // Buffer has enough room - if (cdc_enabled() && !pending_read) { - start_read(); - } - // Buffer is empty and/or no new data is available - if (usb_rx_count == 0) { - return false; - } - return usb_rx_count > 0; -} - -int usb_read(void) { - if (!cdc_enabled() || usb_rx_count == 0) { - return 0; - } - - // Copy from head. - int data; - CRITICAL_SECTION_ENTER(); - data = usb_rx_buf[usb_rx_buf_head]; - usb_rx_buf_head++; - usb_rx_count--; - if (usb_rx_buf_head == USB_RX_BUF_SIZE) { - usb_rx_buf_head = 0; - } - CRITICAL_SECTION_LEAVE(); - - return data; -} - -// TODO(tannewt): See if we can disable the internal CDC IN cache since we -// we manage this one ourselves. -#define CDC_BULKIN_SIZE CONF_USB_COMPOSITE_CDC_ACM_DATA_BULKIN_MAXPKSZ -COMPILER_ALIGNED(4) uint8_t cdc_output_buffer[CDC_BULKIN_SIZE]; - -void usb_write(const char* buffer, uint32_t len) { - if (!cdc_enabled()) { - return; - } - if (!cdc_connected) { - // TODO(tannewt): Should we write to a file instead? - return; - } - uint8_t * output_buffer; - uint32_t output_len; - while (len > 0) { - while (usb_transmitting) {} - output_buffer = (uint8_t *) buffer; - output_len = len; - // Use our own cache in two different cases: - // * When we're at the end of a transmission and we'll return before - // the given buffer is actually transferred to the USB device. - // * When our given buffer isn't aligned on word boundaries. - if (output_len <= CDC_BULKIN_SIZE || ((uint32_t) buffer) % 4 != 0) { - output_buffer = cdc_output_buffer; - output_len = output_len > CDC_BULKIN_SIZE ? CDC_BULKIN_SIZE : output_len; - memcpy(cdc_output_buffer, buffer, output_len); - } else { - output_len = CDC_BULKIN_SIZE; - } - usb_transmitting = true; - cdcdf_acm_write(output_buffer, output_len); - buffer += output_len * sizeof(char); - len -= output_len; - } -} - -bool usb_connected(void) { - return cdc_enabled(); -} - -// Poll for input if keyboard interrupts are enabled, -// so that we can check for the interrupt char. read_complete() does the checking. -// also make sure we have enough room in the local buffer -void usb_cdc_background() { - // - if (mp_interrupt_char != -1 && cdc_enabled() && !pending_read && (usb_rx_count < USB_RX_BUF_SIZE - CDC_BULKOUT_SIZE)) { - start_read(); - } -} diff --git a/ports/atmel-samd/usb.h b/ports/atmel-samd/usb.h deleted file mode 100644 index f01c5ebfb..000000000 --- a/ports/atmel-samd/usb.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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. - */ - -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_USB_H -#define MICROPY_INCLUDED_ATMEL_SAMD_USB_H - -#include -#include - -#define USB_RX_BUF_SIZE 128 - -void init_usb(void); -int usb_read(void); -void usb_write(const char* buffer, uint32_t len); -bool usb_bytes_available(void); -bool usb_connected(void); -void usb_cdc_background(void); - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_USB_H diff --git a/ports/atmel-samd/usb_mass_storage.c b/ports/atmel-samd/usb_mass_storage.c deleted file mode 100644 index 9b6715158..000000000 --- a/ports/atmel-samd/usb_mass_storage.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries - * - * 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 - -#include "usb_mass_storage.h" -#include "supervisor/shared/autoreload.h" - -#include "hal/utils/include/err_codes.h" -#include "hal/utils/include/utils.h" -#include "usb/class/msc/device/mscdf.h" - -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "lib/oofatfs/ff.h" -#include "lib/oofatfs/diskio.h" -#include "lib/oofatfs/ffconf.h" -#include "py/mpconfig.h" -#include "py/mphal.h" -#include "py/mpstate.h" -#include "py/misc.h" - -// The root FS is always at the end of the list. -static fs_user_mount_t* get_vfs(int lun) { - // TODO(tannewt): Return the mount which matches the lun where 0 is the end - // and is counted in reverse. - if (lun > 0) { - return NULL; - } - mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table); - if (current_mount == NULL) { - return NULL; - } - while (current_mount->next != NULL) { - current_mount = current_mount->next; - } - return current_mount->obj; -} - -/* Inquiry Information */ -// This is designed to handle the common case where we have an internal file -// system and an optional SD card. -COMPILER_ALIGNED(4) static uint8_t inquiry_info[2][36]; - -/* Capacities of Disk */ -COMPILER_ALIGNED(4) static uint8_t format_capa[2][8]; - -/** - * \brief Eject Disk - * \param[in] lun logic unit number - * \return Operation status. - */ -int32_t usb_msc_disk_eject(uint8_t lun) { - if (lun > 1) { - return ERR_NOT_FOUND; - } - fs_user_mount_t* current_mount = get_vfs(lun); - // Return ERR_NOT_READY if not ready, otherwise ERR_NONE. - if (current_mount == NULL) { - return ERR_NOT_FOUND; - } - // TODO(tannewt): Should we flush here? - return ERR_NONE; -} - -/** - * \brief Inquiry whether Disk is writable. ERR_DENIED if it is not writable. - * ERR_NONE if it is. ERR_NOT_FOUND if its missing. - * \param[in] lun logic unit number - * \return Operation status. - */ -int32_t usb_msc_disk_is_writable(uint8_t lun) { - if (lun > 1) { - return ERR_NOT_FOUND; - } - - fs_user_mount_t* vfs = get_vfs(lun); - if (vfs == NULL) { - return ERR_NOT_FOUND; - } - if (vfs->writeblocks[0] == MP_OBJ_NULL || - (vfs->flags & FSUSER_USB_WRITABLE) == 0) { - return ERR_DENIED; - } - return ERR_NONE; -} - -/** - * \brief Inquiry whether Disk is ready - * \param[in] lun logic unit number - * \return Operation status. - */ -int32_t usb_msc_disk_is_ready(uint8_t lun) { - if (lun > 1) { - return ERR_NOT_FOUND; - } - - fs_user_mount_t* current_mount = get_vfs(lun); - if (current_mount == NULL) { - return ERR_NOT_FOUND; - } - return ERR_NONE; -} - -/** - * \brief Callback invoked when inquiry data command received - * \param[in] lun logic unit number - * \return Operation status. - */ -uint8_t *usb_msc_inquiry_info(uint8_t lun) { - if (lun > 1) { - return NULL; - } else { - for (uint8_t i = 0; i < 36; i++) { - inquiry_info[lun][i] = 0; - } - inquiry_info[lun][0] = SCSI_INQ_PQ_CONNECTED | SCSI_INQ_DT_DIR_ACCESS; - // connected, direct access - inquiry_info[lun][1] = SCSI_INQ_RMB; // removable medium - inquiry_info[lun][2] = SCSI_INQ_VER_SPC; // SBC version of SCSI primary commands - inquiry_info[lun][3] = SCSI_INQ_RSP_SPC2;// SPC-2 response format - inquiry_info[lun][4] = 31; // 31 bytes following - return &inquiry_info[lun][0]; - } -} - -/** - * \brief Callback invoked when read format capacities command received - * \param[in] lun logic unit number - */ -uint8_t *usb_msc_get_capacity(uint8_t lun) { - if (lun > 1) { - return NULL; - } else { - fs_user_mount_t * vfs = get_vfs(lun); - uint32_t last_valid_sector = 0; - uint32_t sector_size = 0; - if (vfs == NULL || - disk_ioctl(vfs, GET_SECTOR_COUNT, &last_valid_sector) != RES_OK || - disk_ioctl(vfs, GET_SECTOR_SIZE, §or_size) != RES_OK) { - return NULL; - } - // Subtract one from the sector count to get the last valid sector. - last_valid_sector--; - - format_capa[lun][0] = (uint8_t)(last_valid_sector >> 24); - format_capa[lun][1] = (uint8_t)(last_valid_sector >> 16); - format_capa[lun][2] = (uint8_t)(last_valid_sector >> 8); - format_capa[lun][3] = (uint8_t)(last_valid_sector >> 0); - format_capa[lun][4] = (uint8_t)(sector_size >> 24); - format_capa[lun][5] = (uint8_t)(sector_size >> 16); - format_capa[lun][6] = (uint8_t)(sector_size >> 8); - format_capa[lun][7] = (uint8_t)(sector_size >> 0); - - // 8 byte response. First 4 bytes are last block address. Second 4 - // bytes are sector size. - return &format_capa[lun][0]; - } -} - -// USB transfer state. -volatile bool usb_busy; -volatile bool active_read; -volatile bool active_write; -volatile uint8_t active_lun; -volatile uint32_t active_addr; -volatile uint32_t active_nblocks; -volatile bool sector_loaded; -COMPILER_ALIGNED(4) uint8_t sector_buffer[512]; - -/** - * \brief Callback invoked when a new read blocks command received - * \param[in] lun logic unit number - * \param[in] addr start address of disk to be read - * \param[in] nblocks block amount to be read - * \return Operation status. - */ -int32_t usb_msc_new_read(uint8_t lun, uint32_t addr, uint32_t nblocks) { - if (lun > 1) { - return ERR_NOT_FOUND; - } - - // Store transfer info so we can service it in the "background". - active_lun = lun; - active_addr = addr; - active_nblocks = nblocks; - active_read = true; - - return ERR_NONE; -} - -/** - * \brief Callback invoked when a new write blocks command received - * \param[in] lun logic unit number - * \param[in] addr start address of disk to be written - * \param[in] nblocks block amount to be written - * \return Operation status. - */ -int32_t usb_msc_new_write(uint8_t lun, uint32_t addr, uint32_t nblocks) { - if (lun > 1) { - return ERR_NOT_FOUND; - } - - fs_user_mount_t * vfs = get_vfs(lun); - // This is used to determine the writeability of the disk from USB. - if (vfs == NULL) { - return ERR_NOT_FOUND; - } - if (vfs->writeblocks[0] == MP_OBJ_NULL || - (vfs->flags & FSUSER_USB_WRITABLE) == 0) { - return ERR_DENIED; - } - - // Store transfer info so we can service it in the "background". - active_lun = lun; - active_addr = addr; - active_nblocks = nblocks; - active_write = true; - sector_loaded = false; - - // Return ERR_DENIED when the file system is read-only to the USB host. - - return ERR_NONE; -} - -/** - * \brief Callback invoked when a blocks transfer is done - * \param[in] lun logic unit number - * \return Operation status. - */ -int32_t usb_msc_xfer_done(uint8_t lun) { - if (lun > 1) { - return ERR_DENIED; - } - - CRITICAL_SECTION_ENTER(); - if (active_read) { - active_addr += 1; - active_nblocks--; - if (active_nblocks == 0) { - active_read = false; - } - } - - if (active_write) { - sector_loaded = true; - } - usb_busy = false; - CRITICAL_SECTION_LEAVE(); - - return ERR_NONE; -} - -// The start_read callback begins a read transaction which we accept -// but delay our response until the "main thread" calls -// usb_msc_background. Once it does, we read immediately from the -// drive into our cache and trigger the USB DMA to output the -// sector. Once the sector is transmitted, xfer_done will be called. -void usb_msc_background(void) { - // Check USB busy first because we never want to queue another transfer if it is. Checking - // active_read or active_write first leaves the possibility that they are true, an xfer done - // interrupt occurs (setting them false), turning off usb_busy and causing us to queue a - // spurious transfer. - if (usb_busy) { - return; - } - if (active_read) { - fs_user_mount_t * vfs = get_vfs(active_lun); - disk_read(vfs, sector_buffer, active_addr, 1); - CRITICAL_SECTION_ENTER(); - int32_t result = mscdf_xfer_blocks(true, sector_buffer, 1); - usb_busy = result == ERR_NONE; - CRITICAL_SECTION_LEAVE(); - } - if (active_write) { - if (sector_loaded) { - fs_user_mount_t * vfs = get_vfs(active_lun); - disk_write(vfs, sector_buffer, active_addr, 1); - // Since by getting here we assume the mount is read-only to - // MicroPython let's update the cached FatFs sector if it's the one - // we just wrote. - #if _MAX_SS != _MIN_SS - if (vfs->ssize == FILESYSTEM_BLOCK_SIZE) { - #else - // The compiler can optimize this away. - if (_MAX_SS == FILESYSTEM_BLOCK_SIZE) { - #endif - if (active_addr == vfs->fatfs.winsect && active_addr > 0) { - memcpy(vfs->fatfs.win, - sector_buffer, - FILESYSTEM_BLOCK_SIZE); - } - } - sector_loaded = false; - active_addr += 1; - active_nblocks--; - } - // Load more blocks from USB if they are needed. - if (active_nblocks > 0) { - // Turn off interrupts because with them on, - // usb_msc_xfer_done could be called before we update - // usb_busy. If that happened, we'd overwrite the fact that - // the transfer actually already finished. - CRITICAL_SECTION_ENTER(); - int32_t result = mscdf_xfer_blocks(false, sector_buffer, 1); - usb_busy = result == ERR_NONE; - CRITICAL_SECTION_LEAVE(); - } else { - mscdf_xfer_blocks(false, NULL, 0); - active_write = false; - // This write is complete, start the autoreload clock. - autoreload_start(); - } - } -} diff --git a/ports/atmel-samd/usb_mass_storage.h b/ports/atmel-samd/usb_mass_storage.h deleted file mode 100644 index 767a6779c..000000000 --- a/ports/atmel-samd/usb_mass_storage.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries - * - * 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. - */ - -// This adapts the ASF4 USB mass storage API to MicroPython's VFS API so we can -// expose all VFS block devices as Lun's over USB mass storage control. - -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_USB_MASS_STORAGE_H -#define MICROPY_INCLUDED_ATMEL_SAMD_USB_MASS_STORAGE_H - -#include - -// "background" task that actually manages loading to and from the file systems. -void usb_msc_background(void); - -// Callbacks that hook into ASF4's USB stack. -int32_t usb_msc_disk_eject(uint8_t lun); -int32_t usb_msc_disk_is_writable(uint8_t lun); -int32_t usb_msc_disk_is_ready(uint8_t lun); -int32_t usb_msc_new_read(uint8_t lun, uint32_t addr, uint32_t nblocks); -int32_t usb_msc_new_write(uint8_t lun, uint32_t addr, uint32_t nblocks); -int32_t usb_msc_xfer_done(uint8_t lun); -uint8_t *usb_msc_inquiry_info(uint8_t lun); -uint8_t *usb_msc_get_capacity(uint8_t lun); - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_USB_MASS_STORAGE_H diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 3b6cc1fb4..03d5a65cb 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -119,7 +119,6 @@ SRC_COMMON_HAL = \ multiterminal/__init__.c \ neopixel_write/__init__.c \ os/__init__.c \ - storage/__init__.c \ time/__init__.c \ board/__init__.c @@ -147,7 +146,6 @@ SRC_SHARED_MODULE = \ multiterminal/__init__.c \ os/__init__.c \ random/__init__.c \ - storage/__init__.c \ struct/__init__.c SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index eca3f525d..435f2c5cd 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -162,7 +162,6 @@ extern const struct _mp_obj_module_t esp_module; extern const struct _mp_obj_module_t network_module; extern const struct _mp_obj_module_t os_module; extern const struct _mp_obj_module_t random_module; -extern const struct _mp_obj_module_t storage_module; extern const struct _mp_obj_module_t struct_module; extern const struct _mp_obj_module_t mp_module_lwip; extern const struct _mp_obj_module_t mp_module_machine; @@ -194,7 +193,6 @@ extern const struct _mp_obj_module_t neopixel_write_module; { MP_OBJ_NEW_QSTR(MP_QSTR_pulseio), (mp_obj_t)&pulseio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_busio), (mp_obj_t)&busio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_bitbangio), (mp_obj_t)&bitbangio_module }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&struct_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_math), (mp_obj_t)&math_module }, \ diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 25ddebc57..8d0d6d000 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -29,6 +29,10 @@ FROZEN_MPY_DIR = freeze # include py core make definitions include ../../py/py.mk +ifneq ($(MCU_SUB_VARIANT),nrf52840) +USB = FALSE +endif + include $(TOP)/supervisor/supervisor.mk FATFS_DIR = lib/oofatfs @@ -51,7 +55,7 @@ INC += -I./bluetooth INC += -I./peripherals INC += -I../../lib/mp-readline INC += -I../../lib/tinyusb/src -INC += -I./usb +INC += -I../../supervisor/shared/usb NRF_DEFINES += -DCONFIG_GPIO_AS_PINRESET @@ -68,6 +72,9 @@ LDFLAGS += -Xlinker -Map=$(@:.elf=.map) LDFLAGS += -mthumb -mabi=aapcs -T $(LD_FILE) -L boards/ LDFLAGS += -Wl,--gc-sections +# TinyUSB defines +CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_NRF5X -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_CDC_TX_BUFSIZE=1024 -DCFG_TUD_MSC_BUFSIZE=4096 + #Debugging/Optimization ifeq ($(DEBUG), 1) #ASMFLAGS += -g -gtabs+ @@ -94,10 +101,16 @@ SRC_NRFX = $(addprefix nrfx/,\ drivers/src/nrfx_uarte.c \ ) +ifdef EXTERNAL_FLASH_DEVICES + ifeq ($(QSPI_FLASH_FILESYSTEM),1) + SRC_NRFX += nrfx/drivers/src/nrfx_qspi.c + endif +endif + + SRC_C += \ background.c \ fatfs_port.c \ - internal_flash.c \ mphalport.c \ tick.c \ board_busses.c \ @@ -147,7 +160,6 @@ SRC_COMMON_HAL += \ pulseio/PulseIn.c \ pulseio/PulseOut.c \ pulseio/__init__.c \ - storage/__init__.c \ supervisor/Runtime.c \ supervisor/__init__.c \ time/__init__.c \ @@ -185,14 +197,9 @@ SRC_BINDINGS_ENUMS += \ bleio/UUIDType.c endif -SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ - $(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \ - $(addprefix common-hal/, $(SRC_COMMON_HAL)) - SRC_SHARED_MODULE = \ os/__init__.c \ random/__init__.c \ - storage/__init__.c \ struct/__init__.c \ gamepad/__init__.c \ gamepad/GamePad.c \ @@ -201,44 +208,36 @@ SRC_SHARED_MODULE = \ bitbangio/OneWire.c \ bitbangio/SPI.c \ busio/OneWire.c \ + storage/__init__.c # uheap/__init__.c \ ustack/__init__.c -SRC_SHARED_BINDINGS = \ - struct/__init__.c \ - gamepad/__init__.c \ - gamepad/GamePad.c \ - bitbangio/__init__.c \ - bitbangio/I2C.c \ - bitbangio/SPI.c \ - bitbangio/OneWire.c \ - random/__init__.c \ - # USB source files for nrf52840 ifeq ($(MCU_SUB_VARIANT),nrf52840) SRC_C += \ - usb/usb.c \ - usb/usb_msc_flash.c \ - usb/usb_desc.c \ lib/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c \ lib/tinyusb/src/portable/nordic/nrf5x/hal_nrf5x.c \ lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/control.c \ lib/tinyusb/src/device/usbd.c \ lib/tinyusb/src/class/msc/msc_device.c \ lib/tinyusb/src/class/cdc/cdc_device.c \ lib/tinyusb/src/class/hid/hid_device.c \ lib/tinyusb/src/tusb.c \ -SRC_COMMON_HAL += \ +SRC_SHARED_MODULE += \ usb_hid/__init__.c \ usb_hid/Device.c \ endif +SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ + $(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \ + $(addprefix common-hal/, $(SRC_COMMON_HAL)) -SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_BINDINGS)) \ +SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ $(addprefix shared-module/, $(SRC_SHARED_MODULE)) SRC_S = supervisor/cpu.s @@ -346,9 +345,10 @@ dfu-gen: $(BUILD)/dfu-package.zip $(BUILD)/dfu-package.zip: $(BUILD)/$(OUTPUT_FILENAME).hex $(NRFUTIL) dfu genpkg --sd-req 0xFFFE --dev-type 0x0052 --application $^ $(BUILD)/dfu-package.zip +# You must have $^ here because it deduplicates entries in $(OBJ) $(BUILD)/$(OUTPUT_FILENAME).elf: $(OBJ) $(ECHO) "LINK $@" - $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) -Wl,--start-group $(LIBS) -Wl,--end-group + $(Q)$(CC) $(LDFLAGS) -o $@ $^ -Wl,--start-group $(LIBS) -Wl,--end-group $(Q)$(SIZE) $@ # List of sources for qstr extraction diff --git a/ports/nrf/README.md b/ports/nrf/README.md index 4bd865400..34e58cb78 100644 --- a/ports/nrf/README.md +++ b/ports/nrf/README.md @@ -61,7 +61,7 @@ To build and flash issue the following command inside the ports/nrf/ folder: First prepare the bluetooth folder by downloading Bluetooth LE stacks and headers: - ./drivers/bluetooth/download_ble_stack.sh + ./bluetooth/download_ble_stack.sh If the Bluetooth stacks has been downloaded, compile the target with the following command: diff --git a/ports/nrf/background.c b/ports/nrf/background.c index d19604f68..f614b6bd0 100644 --- a/ports/nrf/background.c +++ b/ports/nrf/background.c @@ -24,11 +24,12 @@ * THE SOFTWARE. */ -#include "tusb.h" +#ifdef NRF52840 +#include "supervisor/usb.h" +#endif void run_background_tasks(void) { -#ifdef NRF52840_XXAA - tusb_task(); - tud_cdc_write_flush(); -#endif + #ifdef NRF52840 + usb_background(); + #endif } diff --git a/ports/nrf/board_busses.c b/ports/nrf/board_busses.c index 91f0def6d..6d5cbd041 100644 --- a/ports/nrf/board_busses.c +++ b/ports/nrf/board_busses.c @@ -30,8 +30,8 @@ #include "shared-bindings/microcontroller/Pin.h" #include "supervisor/shared/translate.h" -#include "mpconfigboard.h" #include "nrf/pins.h" +#include "py/mpconfig.h" #include "py/runtime.h" #if !defined(DEFAULT_I2C_BUS_SDA) || !defined(DEFAULT_I2C_BUS_SCL) diff --git a/ports/nrf/boards/feather_nrf52840_express/board.c b/ports/nrf/boards/feather_nrf52840_express/board.c index a6d050fce..4421970ee 100644 --- a/ports/nrf/boards/feather_nrf52840_express/board.c +++ b/ports/nrf/boards/feather_nrf52840_express/board.c @@ -25,10 +25,8 @@ */ #include "boards/board.h" -#include "usb.h" void board_init(void) { - usb_init(); } bool board_requests_safe_mode(void) { diff --git a/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.h b/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.h index bd6d4b244..68914c626 100644 --- a/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.h +++ b/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.h @@ -25,6 +25,8 @@ * THE SOFTWARE. */ +#include "nrfx/hal/nrf_gpio.h" + #define FEATHER52840 #define MICROPY_HW_BOARD_NAME "Adafruit Feather nRF52840 Express" @@ -33,12 +35,12 @@ #define MICROPY_HW_NEOPIXEL (&pin_P0_13) -#define MICROPY_QSPI_DATA0 (&pin_P1_09) -#define MICROPY_QSPI_DATA1 (&pin_P0_11) -#define MICROPY_QSPI_DATA2 (&pin_P0_12) -#define MICROPY_QSPI_DATA3 (&pin_P0_14) -#define MICROPY_QSPI_SCK (&pin_P0_08) -#define MICROPY_QSPI_CS (&pin_P1_08) +#define MICROPY_QSPI_DATA0 NRF_GPIO_PIN_MAP(1, 9) +#define MICROPY_QSPI_DATA1 NRF_GPIO_PIN_MAP(0, 11) +#define MICROPY_QSPI_DATA2 NRF_GPIO_PIN_MAP(0, 12) +#define MICROPY_QSPI_DATA3 NRF_GPIO_PIN_MAP(0, 14) +#define MICROPY_QSPI_SCK NRF_GPIO_PIN_MAP(0, 8) +#define MICROPY_QSPI_CS NRF_GPIO_PIN_MAP(1, 8) #define CIRCUITPY_AUTORELOAD_DELAY_MS 500 @@ -49,14 +51,7 @@ #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) -// TODO #include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -#define EXTERNAL_FLASH_DEVICES GD25Q16C - -#define EXTERNAL_FLASH_QSPI_DUAL - -// TODO include "external_flash/external_flash.h" +#define EXTERNAL_FLASH_QSPI_DUAL (1) #define BOARD_HAS_CRYSTAL 1 diff --git a/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.mk b/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.mk index caf580ec4..f03109319 100644 --- a/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.mk +++ b/ports/nrf/boards/feather_nrf52840_express/mpconfigboard.mk @@ -1,3 +1,8 @@ +USB_VID = 0x239A +USB_PID = 0x802A +USB_PRODUCT = "Feather nRF52840 Express" +USB_MANUFACTURER = "Adafruit Industries LLC" + MCU_SERIES = m4 MCU_VARIANT = nrf52 MCU_SUB_VARIANT = nrf52840 @@ -14,3 +19,7 @@ else endif NRF_DEFINES += -DNRF52840_XXAA -DNRF52840 + +QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = "GD25Q64C" diff --git a/ports/nrf/boards/makerdiary_nrf52840_mdk/board.c b/ports/nrf/boards/makerdiary_nrf52840_mdk/board.c index a6d050fce..4421970ee 100644 --- a/ports/nrf/boards/makerdiary_nrf52840_mdk/board.c +++ b/ports/nrf/boards/makerdiary_nrf52840_mdk/board.c @@ -25,10 +25,8 @@ */ #include "boards/board.h" -#include "usb.h" void board_init(void) { - usb_init(); } bool board_requests_safe_mode(void) { diff --git a/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.h b/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.h index b33fb2dd6..40b6487ec 100644 --- a/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.h +++ b/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.h @@ -25,18 +25,20 @@ * THE SOFTWARE. */ +#include "nrfx/hal/nrf_gpio.h" + #define MAKERDIARYNRF52840MDK #define MICROPY_HW_BOARD_NAME "MakerDiary nRF52840 MDK" #define MICROPY_HW_MCU_NAME "nRF52840" #define MICROPY_PY_SYS_PLATFORM "MakerDiary52840MDK" -#define MICROPY_QSPI_DATA0 (&pin_P1_05) -#define MICROPY_QSPI_DATA1 (&pin_P1_04) -#define MICROPY_QSPI_DATA2 (&pin_P1_02) -#define MICROPY_QSPI_DATA3 (&pin_P1_01) -#define MICROPY_QSPI_SCK (&pin_P1_03) -#define MICROPY_QSPI_CS (&pin_P1_06) +#define MICROPY_QSPI_DATA0 NRF_GPIO_PIN_MAP(1, 5) +#define MICROPY_QSPI_DATA1 NRF_GPIO_PIN_MAP(1, 4) +#define MICROPY_QSPI_DATA2 NRF_GPIO_PIN_MAP(1, 2) +#define MICROPY_QSPI_DATA3 NRF_GPIO_PIN_MAP(1, 1) +#define MICROPY_QSPI_SCK NRF_GPIO_PIN_MAP(1, 3) +#define MICROPY_QSPI_CS NRF_GPIO_PIN_MAP(1, 8) #define CIRCUITPY_AUTORELOAD_DELAY_MS 500 @@ -47,17 +49,6 @@ #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) -// TODO #include "external_flash/devices.h" - -#define EXTERNAL_FLASH_DEVICE_COUNT 1 -// Datasheet for when this is implemented: -// http://www.mxic.com.tw/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf -#define EXTERNAL_FLASH_DEVICES MX25R6435F - -#define EXTERNAL_FLASH_QSPI_DUAL - -// TODO include "external_flash/external_flash.h" - #define BOARD_HAS_CRYSTAL 0 #define DEFAULT_UART_BUS_RX (&pin_P0_19) diff --git a/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.mk b/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.mk index caf580ec4..84dff0bc4 100644 --- a/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.mk +++ b/ports/nrf/boards/makerdiary_nrf52840_mdk/mpconfigboard.mk @@ -1,3 +1,8 @@ +USB_VID = 0x239A +USB_PID = 0x802A +USB_PRODUCT = "nRF52840-MDK" +USB_MANUFACTURER = "makerdiary" + MCU_SERIES = m4 MCU_VARIANT = nrf52 MCU_SUB_VARIANT = nrf52840 @@ -14,3 +19,7 @@ else endif NRF_DEFINES += -DNRF52840_XXAA -DNRF52840 + +QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = "MX25R6435F" diff --git a/ports/nrf/boards/pca10056/board.c b/ports/nrf/boards/pca10056/board.c index a6d050fce..4421970ee 100644 --- a/ports/nrf/boards/pca10056/board.c +++ b/ports/nrf/boards/pca10056/board.c @@ -25,10 +25,8 @@ */ #include "boards/board.h" -#include "usb.h" void board_init(void) { - usb_init(); } bool board_requests_safe_mode(void) { diff --git a/ports/nrf/boards/pca10056/mpconfigboard.h b/ports/nrf/boards/pca10056/mpconfigboard.h index 00a73005c..83c3f66c1 100644 --- a/ports/nrf/boards/pca10056/mpconfigboard.h +++ b/ports/nrf/boards/pca10056/mpconfigboard.h @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include "nrfx/hal/nrf_gpio.h" + #define MICROPY_HW_BOARD_NAME "PCA10056 nRF52840-DK" #define MICROPY_HW_MCU_NAME "nRF52840" #define MICROPY_PY_SYS_PLATFORM "nRF52840-DK" @@ -40,3 +42,24 @@ #define DEFAULT_UART_BUS_RX (&pin_P1_01) #define DEFAULT_UART_BUS_TX (&pin_P1_02) + +// Flash operation mode is determined by MICROPY_QSPI_DATAn pin configuration. +// A pin config is valid if it is defined and its value is not 0xFF. +// Quad mode: If all DATA0 --> DATA3 are valid +// Dual mode: If DATA0 and DATA1 are valid while either DATA2 and/or DATA3 are invalid +// Single mode: If only DATA0 is valid +#ifdef QSPI_FLASH_FILESYSTEM +#define MICROPY_QSPI_DATA0 NRF_GPIO_PIN_MAP(0, 20) +#define MICROPY_QSPI_DATA1 NRF_GPIO_PIN_MAP(0, 21) +#define MICROPY_QSPI_DATA2 NRF_GPIO_PIN_MAP(0, 22) +#define MICROPY_QSPI_DATA3 NRF_GPIO_PIN_MAP(0, 23) +#define MICROPY_QSPI_SCK NRF_GPIO_PIN_MAP(0, 19) +#define MICROPY_QSPI_CS NRF_GPIO_PIN_MAP(0, 17) +#endif + +#ifdef SPI_FLASH_FILESYSTEM +#define SPI_FLASH_MOSI_PIN &pin_P0_20 +#define SPI_FLASH_MISO_PIN &pin_P0_21 +#define SPI_FLASH_SCK_PIN &pin_P0_19 +#define SPI_FLASH_CS_PIN &pin_P0_17 +#endif diff --git a/ports/nrf/boards/pca10056/mpconfigboard.mk b/ports/nrf/boards/pca10056/mpconfigboard.mk index caf580ec4..49c708cff 100644 --- a/ports/nrf/boards/pca10056/mpconfigboard.mk +++ b/ports/nrf/boards/pca10056/mpconfigboard.mk @@ -1,3 +1,8 @@ +USB_VID = 0x239A +USB_PID = 0x802A +USB_PRODUCT = "PCA10056" +USB_MANUFACTURER = "Nordic Semiconductor" + MCU_SERIES = m4 MCU_VARIANT = nrf52 MCU_SUB_VARIANT = nrf52840 @@ -14,3 +19,7 @@ else endif NRF_DEFINES += -DNRF52840_XXAA -DNRF52840 + +QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = "MX25R6435F" diff --git a/ports/nrf/boards/pca10059/board.c b/ports/nrf/boards/pca10059/board.c index cbb4ef3b6..4421970ee 100644 --- a/ports/nrf/boards/pca10059/board.c +++ b/ports/nrf/boards/pca10059/board.c @@ -24,14 +24,9 @@ * THE SOFTWARE. */ -#include -#include #include "boards/board.h" -#include "nrfx.h" -#include "usb.h" void board_init(void) { - usb_init(); } bool board_requests_safe_mode(void) { diff --git a/ports/nrf/boards/pca10059/mpconfigboard.mk b/ports/nrf/boards/pca10059/mpconfigboard.mk index f399927fd..030c9a823 100644 --- a/ports/nrf/boards/pca10059/mpconfigboard.mk +++ b/ports/nrf/boards/pca10059/mpconfigboard.mk @@ -1,3 +1,8 @@ +USB_VID = 0x239A +USB_PID = 0x802A +USB_PRODUCT = "PCA10059" +USB_MANUFACTURER = "Nordic Semiconductor" + MCU_SERIES = m4 MCU_VARIANT = nrf52 MCU_SUB_VARIANT = nrf52840 diff --git a/ports/nrf/common-hal/busio/SPI.c b/ports/nrf/common-hal/busio/SPI.c index f094b7f24..19bd4d1ba 100644 --- a/ports/nrf/common-hal/busio/SPI.c +++ b/ports/nrf/common-hal/busio/SPI.c @@ -59,12 +59,30 @@ STATIC spim_peripheral_t spim_peripherals[] = { #endif }; +STATIC bool never_reset[4]; + void spi_reset(void) { for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) { + if (never_reset[i]) { + continue; + } nrf_spim_disable(spim_peripherals[i].spim.p_reg); } } +void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { + for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) { + if (self->spim_peripheral == &spim_peripherals[i]) { + never_reset[i] = true; + + never_reset_pin_number(self->clock_pin_number); + never_reset_pin_number(self->MOSI_pin_number); + never_reset_pin_number(self->MISO_pin_number); + break; + } + } +} + // Convert frequency to clock-speed-dependent value. Choose the next lower baudrate if in between // available baudrates. static nrf_spim_frequency_t baudrate_to_spim_frequency(const uint32_t baudrate) { diff --git a/ports/nrf/common-hal/busio/UART.c b/ports/nrf/common-hal/busio/UART.c index b9e79381a..516a7747d 100644 --- a/ports/nrf/common-hal/busio/UART.c +++ b/ports/nrf/common-hal/busio/UART.c @@ -27,7 +27,7 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/busio/UART.h" -#include "mpconfigport.h" +#include "py/mpconfig.h" #include "py/gc.h" #include "py/mperrno.h" #include "py/runtime.h" diff --git a/ports/nrf/common-hal/digitalio/DigitalInOut.c b/ports/nrf/common-hal/digitalio/DigitalInOut.c index f05cf568f..0836962c6 100644 --- a/ports/nrf/common-hal/digitalio/DigitalInOut.c +++ b/ports/nrf/common-hal/digitalio/DigitalInOut.c @@ -30,6 +30,11 @@ #include "nrf_gpio.h" +void common_hal_digitalio_digitalinout_never_reset( + digitalio_digitalinout_obj_t *self) { + never_reset_pin_number(self->pin->number); +} + digitalinout_result_t common_hal_digitalio_digitalinout_construct( digitalio_digitalinout_obj_t *self, const mcu_pin_obj_t *pin) { claim_pin(pin); diff --git a/ports/nrf/common-hal/microcontroller/Pin.c b/ports/nrf/common-hal/microcontroller/Pin.c index 7a069eeda..0522bfef3 100644 --- a/ports/nrf/common-hal/microcontroller/Pin.c +++ b/ports/nrf/common-hal/microcontroller/Pin.c @@ -45,13 +45,18 @@ bool speaker_enable_in_use; // Bit mask of claimed pins on each of up to two ports. nrf52832 has one port; nrf52840 has two. STATIC uint32_t claimed_pins[GPIO_COUNT]; +STATIC uint32_t never_reset_pins[GPIO_COUNT]; void reset_all_pins(void) { + return; for (size_t i = 0; i < GPIO_COUNT; i++) { - claimed_pins[i] = 0; + claimed_pins[i] = never_reset_pins[i]; } for (uint32_t pin = 0; pin < NUMBER_OF_PINS; ++pin) { + if (!(never_reset_pins[nrf_pin_port(pin)] & (1 << nrf_relative_pin_number(pin)))) { + continue; + } nrf_gpio_cfg_default(pin); } @@ -72,6 +77,7 @@ void reset_all_pins(void) { // Mark pin as free and return it to a quiescent state. void reset_pin_number(uint8_t pin_number) { + return; if (pin_number == NO_PIN) { return; } @@ -108,6 +114,11 @@ void reset_pin_number(uint8_t pin_number) { #endif } + +void never_reset_pin_number(uint8_t pin_number) { + never_reset_pins[nrf_pin_port(pin_number)] |= 1 << nrf_relative_pin_number(pin_number); +} + void claim_pin(const mcu_pin_obj_t* pin) { // Set bit in claimed_pins bitmask. claimed_pins[nrf_pin_port(pin->number)] |= 1 << nrf_relative_pin_number(pin->number); diff --git a/ports/nrf/common-hal/microcontroller/Pin.h b/ports/nrf/common-hal/microcontroller/Pin.h index 2cdf440b5..61be8bc35 100644 --- a/ports/nrf/common-hal/microcontroller/Pin.h +++ b/ports/nrf/common-hal/microcontroller/Pin.h @@ -44,6 +44,7 @@ void reset_all_pins(void); // need to store a full pointer. void reset_pin_number(uint8_t pin); void claim_pin(const mcu_pin_obj_t* pin); +void never_reset_pin_number(uint8_t pin_number); // Lower 5 bits of a pin number are the pin number in a port. // upper bits (just one bit for current chips) is port number. diff --git a/ports/nrf/common-hal/microcontroller/Processor.c b/ports/nrf/common-hal/microcontroller/Processor.c index 2ce3e8bf5..e4d9439c9 100644 --- a/ports/nrf/common-hal/microcontroller/Processor.c +++ b/ports/nrf/common-hal/microcontroller/Processor.c @@ -72,12 +72,7 @@ uint32_t common_hal_mcu_processor_get_frequency(void) { } void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { - - uint32_t* id_addresses[2] = {(uint32_t *) 0x060, (uint32_t *) 0x064}; - for (int i=0; i<2; i++) { - for (int k=0; k<4; k++) { - raw_id[4 * i + k] = (*(id_addresses[i]) >> k * 8) & 0xff; - } + ((uint32_t*) raw_id)[i] = NRF_FICR->DEVICEID[i]; } } diff --git a/ports/nrf/common-hal/pulseio/PulseIn.c b/ports/nrf/common-hal/pulseio/PulseIn.c index 950cc877d..3d6d266af 100644 --- a/ports/nrf/common-hal/pulseio/PulseIn.c +++ b/ports/nrf/common-hal/pulseio/PulseIn.c @@ -28,7 +28,7 @@ #include -#include "mpconfigport.h" +#include "py/mpconfig.h" #include "py/gc.h" #include "py/runtime.h" diff --git a/ports/nrf/common-hal/pulseio/PulseOut.c b/ports/nrf/common-hal/pulseio/PulseOut.c index 044c4d3a8..be5deca9f 100644 --- a/ports/nrf/common-hal/pulseio/PulseOut.c +++ b/ports/nrf/common-hal/pulseio/PulseOut.c @@ -28,7 +28,7 @@ #include -#include "mpconfigport.h" +#include "py/mpconfig.h" #include "nrf/pins.h" #include "nrf/timers.h" #include "py/gc.h" diff --git a/ports/nrf/common-hal/storage/__init__.c b/ports/nrf/common-hal/storage/__init__.c deleted file mode 100644 index 4ff5f0dac..000000000 --- a/ports/nrf/common-hal/storage/__init__.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 - -#include "py/mperrno.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/__init__.h" -#include "shared-bindings/storage/__init__.h" -#include "supervisor/filesystem.h" - -extern volatile bool mp_msc_enabled; - -void common_hal_storage_remount(const char *mount_path, bool readonly) { - if (strcmp(mount_path, "/") != 0) { - mp_raise_OSError(MP_EINVAL); - } -} - -void common_hal_storage_erase_filesystem(void) { - filesystem_init(false, true); // Force a re-format. - common_hal_mcu_reset(); - // We won't actually get here, since we're resetting. -} diff --git a/ports/nrf/common-hal/usb_hid/Device.c b/ports/nrf/common-hal/usb_hid/Device.c deleted file mode 100644 index 6acb2717f..000000000 --- a/ports/nrf/common-hal/usb_hid/Device.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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 -#include "tick.h" -#include "common-hal/usb_hid/Device.h" -#include "py/runtime.h" -#include "shared-bindings/usb_hid/Device.h" -#include "supervisor/shared/translate.h" -#include "tusb.h" - -uint8_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self) { - return self->usage_page; -} - -uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) { - return self->usage; -} - -void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { - if (len != self->report_length) { - mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->report_length); - } - - // Wait until interface is ready, timeout = 2 seconds - uint64_t end_ticks = ticks_ms + 2000; - while ( (ticks_ms < end_ticks) && !tud_hid_generic_ready() ) { } - - if ( !tud_hid_generic_ready() ) { - mp_raise_msg(&mp_type_OSError, translate("USB Busy")); - } - - memcpy(self->report_buffer, report, len); - - if ( !tud_hid_generic_report(self->report_id, self->report_buffer, len) ) { - mp_raise_msg(&mp_type_OSError, translate("USB Error")); - } -} - -// Callbacks invoked when receive Get_Report request through control endpoint -uint16_t tud_hid_generic_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { - // only support Input Report - if ( report_type != HID_REPORT_TYPE_INPUT ) return 0; - - // index is ID-1 - uint8_t idx = ( report_id ? (report_id-1) : 0 ); - - // fill buffer with current report - memcpy(buffer, usb_hid_devices[idx].report_buffer, reqlen); - return reqlen; -} - -// Callbacks invoked when receive Set_Report request through control endpoint -void tud_hid_generic_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { - // index is ID-1 - uint8_t idx = ( report_id ? (report_id-1) : 0 ); - - if ( report_type == HID_REPORT_TYPE_OUTPUT ) { - // Check if it is Keyboard device - if ( (usb_hid_devices[idx].usage_page == HID_USAGE_PAGE_DESKTOP) && (usb_hid_devices[idx].usage == HID_USAGE_DESKTOP_KEYBOARD) ) { - // This is LED indicator (CapsLock, NumLock) - // TODO Light up some LED here - } - } -} diff --git a/ports/nrf/common-hal/usb_hid/Device.h b/ports/nrf/common-hal/usb_hid/Device.h deleted file mode 100644 index 612423715..000000000 --- a/ports/nrf/common-hal/usb_hid/Device.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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. - */ - -#ifndef COMMON_HAL_USB_HID_DEVICE_H -#define COMMON_HAL_USB_HID_DEVICE_H - -#include -#include - -#include "py/obj.h" - -#ifdef __cplusplus - extern "C" { -#endif - -// 1 to enable device, 0 to disable -#define USB_HID_DEVICE_KEYBOARD 1 -#define USB_HID_DEVICE_MOUSE 1 -#define USB_HID_DEVICE_CONSUMER 1 -#define USB_HID_DEVICE_SYS_CONTROL 1 -#define USB_HID_DEVICE_GAMEPAD 1 -#define USB_HID_DEVICE_DIGITIZER 0 // not supported yet - -enum { - USB_HID_REPORT_ID_UNUSED = 0, - -#if USB_HID_DEVICE_KEYBOARD - USB_HID_REPORT_ID_KEYBOARD, -#endif - -#if USB_HID_DEVICE_MOUSE - USB_HID_REPORT_ID_MOUSE, -#endif - -#if USB_HID_DEVICE_CONSUMER - USB_HID_REPORT_ID_CONSUMER, -#endif - -#if USB_HID_DEVICE_SYS_CONTROL - USB_HID_REPORT_ID_SYS_CONTROL, -#endif - -#if USB_HID_DEVICE_GAMEPAD - USB_HID_REPORT_ID_GAMEPAD, -#endif - -#if USB_HID_DEVICE_DIGITIZER - USB_HID_REPORT_ID_DIGITIZER, -#endif -}; - -#define USB_HID_NUM_DEVICES (USB_HID_DEVICE_KEYBOARD + USB_HID_DEVICE_MOUSE + USB_HID_DEVICE_CONSUMER + \ - USB_HID_DEVICE_SYS_CONTROL + USB_HID_DEVICE_GAMEPAD + USB_HID_DEVICE_DIGITIZER ) - -typedef struct { - mp_obj_base_t base; - uint8_t* report_buffer; - uint8_t report_id; - uint8_t report_length; - uint8_t usage_page; - uint8_t usage; -} usb_hid_device_obj_t; - - -extern usb_hid_device_obj_t usb_hid_devices[]; - -#ifdef __cplusplus - } -#endif - -#endif /* COMMON_HAL_USB_HID_DEVICE_H */ diff --git a/ports/nrf/common-hal/usb_hid/__init__.c b/ports/nrf/common-hal/usb_hid/__init__.c deleted file mode 100644 index 00fc8bd00..000000000 --- a/ports/nrf/common-hal/usb_hid/__init__.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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 "py/obj.h" -#include "py/mphal.h" -#include "py/runtime.h" - -#include "common-hal/usb_hid/Device.h" -#include "shared-bindings/usb_hid/Device.h" -#include "tusb.h" - -#define USB_HID_REPORT_LENGTH_KEYBOARD 8 -#define USB_HID_REPORT_LENGTH_MOUSE 4 -#define USB_HID_REPORT_LENGTH_CONSUMER 2 -#define USB_HID_REPORT_LENGTH_SYS_CONTROL 1 -#define USB_HID_REPORT_LENGTH_GAMEPAD 6 -#define USB_HID_REPORT_LENGTH_DIGITIZER 5 - -#if USB_HID_DEVICE_KEYBOARD -static uint8_t keyboard_report_buffer[USB_HID_REPORT_LENGTH_KEYBOARD]; -#endif - -#if USB_HID_DEVICE_MOUSE -static uint8_t mouse_report_buffer[USB_HID_REPORT_LENGTH_MOUSE]; -#endif - -#if USB_HID_DEVICE_CONSUMER -static uint8_t consumer_report_buffer[USB_HID_REPORT_LENGTH_CONSUMER]; -#endif - -#if USB_HID_DEVICE_SYS_CONTROL -static uint8_t sys_control_report_buffer[USB_HID_REPORT_LENGTH_SYS_CONTROL]; -#endif - -#if USB_HID_DEVICE_GAMEPAD -static uint8_t gamepad_report_buffer[USB_HID_REPORT_LENGTH_GAMEPAD]; -#endif - -#if USB_HID_DEVICE_DIGITIZER -static uint8_t digitizer_report_buffer[USB_HID_REPORT_LENGTH_DIGITIZER]; -#endif - -usb_hid_device_obj_t usb_hid_devices[] = { -#if USB_HID_DEVICE_KEYBOARD - { - .base = { .type = &usb_hid_device_type } , - .report_buffer = keyboard_report_buffer , - .report_id = USB_HID_REPORT_ID_KEYBOARD , - .report_length = USB_HID_REPORT_LENGTH_KEYBOARD , - .usage_page = HID_USAGE_PAGE_DESKTOP , - .usage = HID_USAGE_DESKTOP_KEYBOARD , - }, -#endif - -#if USB_HID_DEVICE_MOUSE - { - .base = { .type = &usb_hid_device_type } , - .report_buffer = mouse_report_buffer , - .report_id = USB_HID_REPORT_ID_MOUSE , - .report_length = USB_HID_REPORT_LENGTH_MOUSE , - .usage_page = HID_USAGE_PAGE_DESKTOP , - .usage = HID_USAGE_DESKTOP_MOUSE , - }, -#endif - -#if USB_HID_DEVICE_CONSUMER - { - .base = { .type = &usb_hid_device_type } , - .report_buffer = consumer_report_buffer , - .report_id = USB_HID_REPORT_ID_CONSUMER , - .report_length = USB_HID_REPORT_LENGTH_CONSUMER , - .usage_page = HID_USAGE_PAGE_CONSUMER , - .usage = HID_USAGE_CONSUMER_CONTROL , - }, -#endif - -#if USB_HID_DEVICE_SYS_CONTROL - { - .base = { .type = &usb_hid_device_type } , - .report_buffer = sys_control_report_buffer , - .report_id = USB_HID_REPORT_ID_SYS_CONTROL , - .report_length = USB_HID_REPORT_LENGTH_SYS_CONTROL , - .usage_page = HID_USAGE_PAGE_DESKTOP , - .usage = HID_USAGE_DESKTOP_SYSTEM_CONTROL , - }, -#endif - -#if USB_HID_DEVICE_GAMEPAD - { - .base = { .type = &usb_hid_device_type } , - .report_buffer = gamepad_report_buffer , - .report_id = USB_HID_REPORT_ID_GAMEPAD , - .report_length = USB_HID_REPORT_LENGTH_GAMEPAD , - .usage_page = HID_USAGE_PAGE_DESKTOP , - .usage = HID_USAGE_DESKTOP_GAMEPAD , - }, -#endif - -#if USB_HID_DEVICE_DIGITIZER - { - .base = { .type = &usb_hid_device_type } , - .report_buffer = digitizer_report_buffer , - .report_id = USB_HID_REPORT_ID_DIGITIZER , - .report_length = USB_HID_REPORT_LENGTH_DIGITIZER , - .usage_page = 0x0D , - .usage = 0x02 , - }, -#endif -}; - - -mp_obj_tuple_t common_hal_usb_hid_devices = { - .base = { - .type = &mp_type_tuple, - }, - .len = USB_HID_NUM_DEVICES, - .items = { -#if USB_HID_NUM_DEVICES >= 1 - (mp_obj_t) &usb_hid_devices[0], -#endif -#if USB_HID_NUM_DEVICES >= 2 - (mp_obj_t) &usb_hid_devices[1], -#endif -#if USB_HID_NUM_DEVICES >= 3 - (mp_obj_t) &usb_hid_devices[2], -#endif -#if USB_HID_NUM_DEVICES >= 4 - (mp_obj_t) &usb_hid_devices[3], -#endif -#if USB_HID_NUM_DEVICES >= 5 - (mp_obj_t) &usb_hid_devices[4], -#endif -#if USB_HID_NUM_DEVICES >= 6 - (mp_obj_t) &usb_hid_devices[5], -#endif - } -}; diff --git a/ports/nrf/internal_flash.c b/ports/nrf/internal_flash.c deleted file mode 100644 index 812ef4d3f..000000000 --- a/ports/nrf/internal_flash.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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 "internal_flash.h" - -#include -#include - -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "py/mphal.h" -#include "py/obj.h" -#include "py/runtime.h" -#include "lib/oofatfs/ff.h" -#include "supervisor/shared/rgb_led_status.h" - -#include "nrf_nvmc.h" - -#ifdef BLUETOOTH_SD -#include "nrf_sdm.h" -#endif - -// defined in linker -extern uint32_t __fatfs_flash_start_addr[]; -extern uint32_t __fatfs_flash_length[]; - -#define NO_CACHE 0xffffffff -#define FL_PAGE_SZ 4096 - -uint8_t _flash_cache[FL_PAGE_SZ] __attribute__((aligned(4))); -uint32_t _flash_page_addr = NO_CACHE; - - -/*------------------------------------------------------------------*/ -/* Internal Flash API - *------------------------------------------------------------------*/ -static inline uint32_t lba2addr(uint32_t block) { - return ((uint32_t)__fatfs_flash_start_addr) + block * FILESYSTEM_BLOCK_SIZE; -} - -void internal_flash_init(void) { - // Activity LED for flash writes. -#ifdef MICROPY_HW_LED_MSC - struct port_config pin_conf; - port_get_config_defaults(&pin_conf); - - pin_conf.direction = PORT_PIN_DIR_OUTPUT; - port_pin_set_config(MICROPY_HW_LED_MSC, &pin_conf); - port_pin_set_output_level(MICROPY_HW_LED_MSC, false); -#endif -} - -uint32_t internal_flash_get_block_size(void) { - return FILESYSTEM_BLOCK_SIZE; -} - -uint32_t internal_flash_get_block_count(void) { - return ((uint32_t) __fatfs_flash_length) / FILESYSTEM_BLOCK_SIZE ; -} - -// TODO support flashing with SD enabled -void internal_flash_flush(void) { - if (_flash_page_addr == NO_CACHE) return; - - // Skip if data is the same - if (memcmp(_flash_cache, (void *)_flash_page_addr, FL_PAGE_SZ) != 0) { -// _is_flashing = true; - nrf_nvmc_page_erase(_flash_page_addr); - nrf_nvmc_write_words(_flash_page_addr, (uint32_t *)_flash_cache, FL_PAGE_SZ / sizeof(uint32_t)); - } - - _flash_page_addr = NO_CACHE; -} - -mp_uint_t internal_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) { - uint32_t src = lba2addr(block); - memcpy(dest, (uint8_t*) src, FILESYSTEM_BLOCK_SIZE*num_blocks); - return 0; // success -} - -mp_uint_t internal_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32_t num_blocks) { - -#ifdef MICROPY_HW_LED_MSC - port_pin_set_output_level(MICROPY_HW_LED_MSC, true); -#endif - - while (num_blocks) { - uint32_t const addr = lba2addr(lba); - uint32_t const page_addr = addr & ~(FL_PAGE_SZ - 1); - - uint32_t count = 8 - (lba % 8); // up to page boundary - count = MIN(num_blocks, count); - - if (page_addr != _flash_page_addr) { - internal_flash_flush(); - - // writing previous cached data, skip current data until flashing is done - // tinyusb stack will invoke write_block() with the same parameters later on - // if ( _is_flashing ) return; - - _flash_page_addr = page_addr; - memcpy(_flash_cache, (void *)page_addr, FL_PAGE_SZ); - } - - memcpy(_flash_cache + (addr & (FL_PAGE_SZ - 1)), src, count * FILESYSTEM_BLOCK_SIZE); - - // adjust for next run - lba += count; - src += count * FILESYSTEM_BLOCK_SIZE; - num_blocks -= count; - } - -#ifdef MICROPY_HW_LED_MSC - port_pin_set_output_level(MICROPY_HW_LED_MSC, false); -#endif - - return 0; // success -} - -/******************************************************************************/ -// MicroPython bindings -// -// Expose the flash as an object with the block protocol. - -// there is a singleton Flash object -STATIC const mp_obj_base_t internal_flash_obj = {&internal_flash_type}; - -STATIC mp_obj_t internal_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - // check arguments - mp_arg_check_num(n_args, n_kw, 0, 0, false); - - // return singleton object - return (mp_obj_t)&internal_flash_obj; -} - -STATIC mp_obj_t internal_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); - mp_uint_t ret = internal_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); - return MP_OBJ_NEW_SMALL_INT(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_readblocks_obj, internal_flash_obj_readblocks); - -STATIC mp_obj_t internal_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); - mp_uint_t ret = internal_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); - return MP_OBJ_NEW_SMALL_INT(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_writeblocks_obj, internal_flash_obj_writeblocks); - -STATIC mp_obj_t internal_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { - mp_int_t cmd = mp_obj_get_int(cmd_in); - switch (cmd) { - case BP_IOCTL_INIT: internal_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_DEINIT: internal_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly - case BP_IOCTL_SYNC: internal_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(internal_flash_get_block_count()); - case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(internal_flash_get_block_size()); - default: return mp_const_none; - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_ioctl_obj, internal_flash_obj_ioctl); - -STATIC const mp_rom_map_elem_t internal_flash_obj_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&internal_flash_obj_readblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&internal_flash_obj_writeblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&internal_flash_obj_ioctl_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(internal_flash_obj_locals_dict, internal_flash_obj_locals_dict_table); - -const mp_obj_type_t internal_flash_type = { - { &mp_type_type }, - .name = MP_QSTR_InternalFlash, - .make_new = internal_flash_obj_make_new, - .locals_dict = (mp_obj_t)&internal_flash_obj_locals_dict, -}; - -/*------------------------------------------------------------------*/ -/* Flash API - *------------------------------------------------------------------*/ - -void flash_init_vfs(fs_user_mount_t *vfs) { - vfs->base.type = &mp_fat_vfs_type; - vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; - vfs->fatfs.drv = vfs; - -// vfs->fatfs.part = 1; // flash filesystem lives on first partition - vfs->readblocks[0] = (mp_obj_t)&internal_flash_obj_readblocks_obj; - vfs->readblocks[1] = (mp_obj_t)&internal_flash_obj; - vfs->readblocks[2] = (mp_obj_t)internal_flash_read_blocks; // native version - - vfs->writeblocks[0] = (mp_obj_t)&internal_flash_obj_writeblocks_obj; - vfs->writeblocks[1] = (mp_obj_t)&internal_flash_obj; - vfs->writeblocks[2] = (mp_obj_t)internal_flash_write_blocks; // native version - - vfs->u.ioctl[0] = (mp_obj_t)&internal_flash_obj_ioctl_obj; - vfs->u.ioctl[1] = (mp_obj_t)&internal_flash_obj; -} - -void flash_flush(void) { - internal_flash_flush(); -} diff --git a/ports/nrf/internal_flash.h b/ports/nrf/internal_flash.h deleted file mode 100644 index 3811815e2..000000000 --- a/ports/nrf/internal_flash.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * 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. - */ -#ifndef MICROPY_INCLUDED_NRF_INTERNAL_FLASH_H -#define MICROPY_INCLUDED_NRF_INTERNAL_FLASH_H - -#include -#include - -#include "mpconfigport.h" - -#define FLASH_ROOT_POINTERS - -#define FLASH_PAGE_SIZE 0x1000 -#define CIRCUITPY_INTERNAL_NVM_SIZE 0 - -#define INTERNAL_FLASH_SYSTICK_MASK (0x1ff) // 512ms -#define INTERNAL_FLASH_IDLE_TICK(tick) (((tick) & INTERNAL_FLASH_SYSTICK_MASK) == 2) - -void internal_flash_init(void); -uint32_t internal_flash_get_block_size(void); -uint32_t internal_flash_get_block_count(void); -void internal_flash_irq_handler(void); -void internal_flash_flush(void); - -// these return 0 on success, non-zero on error -mp_uint_t internal_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); -mp_uint_t internal_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); - -extern const struct _mp_obj_type_t internal_flash_type; - -struct _fs_user_mount_t; - -void flash_init_vfs(struct _fs_user_mount_t *vfs); -void flash_flush(void); - -#endif // MICROPY_INCLUDED_NRF_INTERNAL_FLASH_H diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 8b193fb59..c9c03427f 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -58,7 +58,7 @@ #define MICROPY_FATFS_LFN_CODE_PAGE (437) /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_RPATH (2) -#define MICROPY_FATFS_MULTI_PARTITION (0) +#define MICROPY_FATFS_MULTI_PARTITION (1) #define MICROPY_FATFS_NUM_PERSISTENT (1) //#define MICROPY_FATFS_MAX_SS (4096) @@ -216,9 +216,12 @@ extern const struct _mp_obj_module_t bleio_module; #define MP_STATE_PORT MP_STATE_VM +#include "supervisor/flash_root_pointers.h" + #define MICROPY_PORT_ROOT_POINTERS \ const char *readline_hist[8]; \ mp_obj_t gamepad_singleton; \ + FLASH_ROOT_POINTERS \ // We need to provide a declaration/definition of alloca() #include diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index 17304b783..f43e29fce 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -5,3 +5,4 @@ MPY_TOOL_LONGINT_IMPL = -mlongint-impl=mpz INTERNAL_LIBM = (1) +USB_SERIAL_NUMBER_LENGTH = 16 diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index e9a8722c6..73f38bada 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -31,86 +31,6 @@ #include "py/mpstate.h" #include "py/gc.h" -#if (MICROPY_PY_BLE_NUS == 0) - -#if !defined( NRF52840_XXAA) -int mp_hal_stdin_rx_chr(void) { - uint8_t data; - nrfx_uarte_rx(&serial_instance, &data, 1); - return data; -} - -bool mp_hal_stdin_any(void) { - return nrf_uarte_event_check(serial_instance.p_reg, NRF_UARTE_EVENT_RXDRDY); -} - -void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { - if (len == 0) { - return; - } - - // EasyDMA can only access SRAM - uint8_t * tx_buf = (uint8_t*) str; - if ( !nrfx_is_in_ram(str) ) { - tx_buf = (uint8_t *) gc_alloc(len, false, false); - memcpy(tx_buf, str, len); - } - - nrfx_uarte_tx(&serial_instance, tx_buf, len); - - if ( !nrfx_is_in_ram(str) ) { - gc_free(tx_buf); - } -} - -#else - -#include "tusb.h" - -int mp_hal_stdin_rx_chr(void) { - for (;;) { - #ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP - #endif - // if (reload_requested) { - // return CHAR_CTRL_D; - // } - - if (tud_cdc_available()) { - #ifdef MICROPY_HW_LED_RX - gpio_toggle_pin_level(MICROPY_HW_LED_RX); - #endif - return tud_cdc_read_char(); - } - } - - return 0; -} - -bool mp_hal_stdin_any(void) { - return tud_cdc_available() > 0; -} - -void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { - - #ifdef MICROPY_HW_LED_TX - gpio_toggle_pin_level(MICROPY_HW_LED_TX); - #endif - - #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - if (boot_output_file != NULL) { - UINT bytes_written = 0; - f_write(boot_output_file, str, len, &bytes_written); - } - #endif - - tud_cdc_write(str, len); -} - -#endif // USB - -#endif // MICROPY_PY_BLE_NUS - /*------------------------------------------------------------------*/ /* delay *------------------------------------------------------------------*/ @@ -129,4 +49,3 @@ void mp_hal_delay_ms(mp_uint_t delay) { // TODO(tannewt): Go to sleep for a little while while we wait. } } - diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index 69b8e6a0c..1d3085e2b 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -37,6 +37,9 @@ #define NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY 7 #define NRFX_SPIM_MISO_PULL_CFG 1 +// QSPI +#define NRFX_QSPI_ENABLED 1 + // TWI aka. I2C; enable TWIM0 and TWIM1 (no conflict with SPIM choices) #define NRFX_TWIM_ENABLED 1 #define NRFX_TWIM0_ENABLED 1 diff --git a/ports/nrf/peripherals/nrf/timers.c b/ports/nrf/peripherals/nrf/timers.c index 0027d526f..7f7a003d3 100644 --- a/ports/nrf/peripherals/nrf/timers.c +++ b/ports/nrf/peripherals/nrf/timers.c @@ -31,7 +31,7 @@ #include "nrfx.h" #include "nrfx_timer.h" -#include "mpconfigport.h" +#include "py/mpconfig.h" #include "py/runtime.h" STATIC nrfx_timer_t nrfx_timers[] = { diff --git a/ports/nrf/supervisor/filesystem.c b/ports/nrf/supervisor/filesystem.c deleted file mode 100644 index b6611417d..000000000 --- a/ports/nrf/supervisor/filesystem.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * 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 "extmod/vfs_fat.h" -#include "lib/oofatfs/ff.h" -#include "lib/oofatfs/diskio.h" - -#include "py/mpstate.h" - -#include "internal_flash.h" - -static mp_vfs_mount_t _mp_vfs; -static fs_user_mount_t _internal_vfs; - - -void filesystem_init(bool create_allowed, bool force_create) { - // init the vfs object - fs_user_mount_t *int_vfs = &_internal_vfs; - int_vfs->flags = 0; - flash_init_vfs(int_vfs); - - // try to mount the flash - FRESULT res = f_mount(&int_vfs->fatfs); - - if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) { - // no filesystem so create a fresh one - uint8_t working_buf[_MAX_SS]; - res = f_mkfs(&int_vfs->fatfs, FM_FAT | FM_SFD, 4096, working_buf, sizeof(working_buf)); - // Flush the new file system to make sure its repaired immediately. - flash_flush(); - if (res != FR_OK) { - return; - } - - // set label - f_setlabel(&int_vfs->fatfs, "CIRCUITPY"); - - // create lib folder - f_mkdir(&int_vfs->fatfs, "/lib"); - - flash_flush(); - } else if (res != FR_OK) { - return; - } - - mp_vfs_mount_t *mp_vfs = &_mp_vfs; - mp_vfs->str = "/"; - mp_vfs->len = 1; - mp_vfs->obj = MP_OBJ_FROM_PTR(int_vfs); - mp_vfs->next = NULL; - MP_STATE_VM(vfs_mount_table) = mp_vfs; - - // The current directory is used as the boot up directory. - // It is set to the internal flash filesystem by default. - MP_STATE_PORT(vfs_cur) = mp_vfs; -} - -void filesystem_flush(void) { - flash_flush(); -} - -void filesystem_writable_by_python(bool writable) { - fs_user_mount_t *vfs = &_internal_vfs; - - if (writable) { - vfs->flags |= FSUSER_USB_WRITABLE; - } else { - vfs->flags &= ~FSUSER_USB_WRITABLE; - } -} - -bool filesystem_present(void) { - return true; -} diff --git a/ports/nrf/supervisor/internal_flash.c b/ports/nrf/supervisor/internal_flash.c new file mode 100644 index 000000000..a648afa17 --- /dev/null +++ b/ports/nrf/supervisor/internal_flash.c @@ -0,0 +1,121 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "supervisor/flash.h" + +#include +#include + +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "py/mphal.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "lib/oofatfs/ff.h" + +#include "nrf_nvmc.h" + +#ifdef BLUETOOTH_SD +#include "nrf_sdm.h" +#endif + +// defined in linker +extern uint32_t __fatfs_flash_start_addr[]; +extern uint32_t __fatfs_flash_length[]; + +#define NO_CACHE 0xffffffff +#define FL_PAGE_SZ 4096 + +uint8_t _flash_cache[FL_PAGE_SZ] __attribute__((aligned(4))); +uint32_t _flash_page_addr = NO_CACHE; + + +/*------------------------------------------------------------------*/ +/* Internal Flash API + *------------------------------------------------------------------*/ +static inline uint32_t lba2addr(uint32_t block) { + return ((uint32_t)__fatfs_flash_start_addr) + block * FILESYSTEM_BLOCK_SIZE; +} + +void supervisor_flash_init(void) { +} + +uint32_t supervisor_flash_get_block_size(void) { + return FILESYSTEM_BLOCK_SIZE; +} + +uint32_t supervisor_flash_get_block_count(void) { + return ((uint32_t) __fatfs_flash_length) / FILESYSTEM_BLOCK_SIZE ; +} + +// TODO support flashing with SD enabled +void supervisor_flash_flush(void) { + if (_flash_page_addr == NO_CACHE) return; + + // Skip if data is the same + if (memcmp(_flash_cache, (void *)_flash_page_addr, FL_PAGE_SZ) != 0) { +// _is_flashing = true; + nrf_nvmc_page_erase(_flash_page_addr); + nrf_nvmc_write_words(_flash_page_addr, (uint32_t *)_flash_cache, FL_PAGE_SZ / sizeof(uint32_t)); + } + + _flash_page_addr = NO_CACHE; +} + +mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) { + uint32_t src = lba2addr(block); + memcpy(dest, (uint8_t*) src, FILESYSTEM_BLOCK_SIZE*num_blocks); + return 0; // success +} + +mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32_t num_blocks) { + while (num_blocks) { + uint32_t const addr = lba2addr(lba); + uint32_t const page_addr = addr & ~(FL_PAGE_SZ - 1); + + uint32_t count = 8 - (lba % 8); // up to page boundary + count = MIN(num_blocks, count); + + if (page_addr != _flash_page_addr) { + supervisor_flash_flush(); + + // writing previous cached data, skip current data until flashing is done + // tinyusb stack will invoke write_block() with the same parameters later on + // if ( _is_flashing ) return; + + _flash_page_addr = page_addr; + memcpy(_flash_cache, (void *)page_addr, FL_PAGE_SZ); + } + + memcpy(_flash_cache + (addr & (FL_PAGE_SZ - 1)), src, count * FILESYSTEM_BLOCK_SIZE); + + // adjust for next run + lba += count; + src += count * FILESYSTEM_BLOCK_SIZE; + num_blocks -= count; + } + + return 0; // success +} diff --git a/ports/nrf/supervisor/internal_flash.h b/ports/nrf/supervisor/internal_flash.h new file mode 100644 index 000000000..adcb9bbc2 --- /dev/null +++ b/ports/nrf/supervisor/internal_flash.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_NRF_INTERNAL_FLASH_H +#define MICROPY_INCLUDED_NRF_INTERNAL_FLASH_H + +#include +#include + +#include "py/mpconfig.h" + +#define FLASH_ROOT_POINTERS + +#define FLASH_PAGE_SIZE 0x1000 +#define CIRCUITPY_INTERNAL_NVM_SIZE 0 + +#define INTERNAL_FLASH_SYSTICK_MASK (0x1ff) // 512ms +#define INTERNAL_FLASH_IDLE_TICK(tick) (((tick) & INTERNAL_FLASH_SYSTICK_MASK) == 2) + +#endif // MICROPY_INCLUDED_NRF_INTERNAL_FLASH_H diff --git a/ports/nrf/supervisor/internal_flash_root_pointers.h b/ports/nrf/supervisor/internal_flash_root_pointers.h new file mode 100644 index 000000000..cc6074585 --- /dev/null +++ b/ports/nrf/supervisor/internal_flash_root_pointers.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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. + */ +#ifndef MICROPY_INCLUDED_NRF_INTERNAL_FLASH_ROOT_POINTERS_H +#define MICROPY_INCLUDED_NRF_INTERNAL_FLASH_ROOT_POINTERS_H + +#define FLASH_ROOT_POINTERS + +#endif // MICROPY_INCLUDED_NRF_INTERNAL_FLASH_ROOT_POINTERS_H diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index f98b05f5a..fcb3ae55c 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -89,6 +89,13 @@ void reset_port(void) { reset_all_pins(); } +void reset_to_bootloader(void) { + enum { DFU_MAGIC_SERIAL = 0x4e }; + + NRF_POWER->GPREGRET = DFU_MAGIC_SERIAL; + NVIC_SystemReset(); +} + void HardFault_Handler(void) { diff --git a/ports/nrf/supervisor/qspi_flash.c b/ports/nrf/supervisor/qspi_flash.c new file mode 100644 index 000000000..24b06c420 --- /dev/null +++ b/ports/nrf/supervisor/qspi_flash.c @@ -0,0 +1,146 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/spi_flash_api.h" + +#include +#include + +#include "py/mpconfig.h" // for EXTERNAL_FLASH_QSPI_DUAL +#include "nrfx_qspi.h" + +#include "shared-bindings/microcontroller/__init__.h" + +#include "supervisor/shared/external_flash/common_commands.h" +#include "supervisor/shared/external_flash/qspi_flash.h" + +bool spi_flash_command(uint8_t command) { + nrf_qspi_cinstr_conf_t cinstr_cfg = { + .opcode = 0, + .length = 0, + .io2_level = true, + .io3_level = true, + .wipwait = false, + .wren = false + }; + cinstr_cfg.opcode = command; + cinstr_cfg.length = 1; + nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL); + return true; +} + +bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) { + nrf_qspi_cinstr_conf_t cinstr_cfg = { + .opcode = command, + .length = length + 1, + .io2_level = true, + .io3_level = true, + .wipwait = false, + .wren = false + }; + nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, response); + return true; +} + +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { + nrf_qspi_cinstr_conf_t cinstr_cfg = { + .opcode = command, + .length = length + 1, + .io2_level = true, + .io3_level = true, + .wipwait = false, + .wren = false // We do this manually. + }; + nrfx_qspi_cinstr_xfer(&cinstr_cfg, data, NULL); + return true; +} + +bool spi_flash_sector_command(uint8_t command, uint32_t address) { + if (command != CMD_SECTOR_ERASE) { + return false; + } + return nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_4KB, address) == NRFX_SUCCESS; +} + +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { + return nrfx_qspi_write(data, length, address) == NRFX_SUCCESS; +} + +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { + nrfx_qspi_read(data, length, address); + return true; +} + +void spi_flash_init(void) { + // Init QSPI flash + nrfx_qspi_config_t qspi_cfg = { + .xip_offset = 0, + .pins = { + .sck_pin = MICROPY_QSPI_SCK, + .csn_pin = MICROPY_QSPI_CS, + .io0_pin = MICROPY_QSPI_DATA0, + .io1_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io2_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io3_pin = NRF_QSPI_PIN_NOT_CONNECTED, + + }, + .prot_if = { + .readoc = NRF_QSPI_READOC_FASTREAD, + .writeoc = NRF_QSPI_WRITEOC_PP, + .addrmode = NRF_QSPI_ADDRMODE_24BIT, + .dpmconfig = false + }, + .phy_if = { + .sck_freq = NRF_QSPI_FREQ_32MDIV16, + .sck_delay = 10, // min time CS must stay high before going low again. in unit of 62.5 ns + .spi_mode = NRF_QSPI_MODE_0, + .dpmen = false + }, + .irq_priority = 7, + }; + +#if EXTERNAL_FLASH_QSPI_DUAL + qspi_cfg.pins.io1_pin = MICROPY_QSPI_DATA1; + qspi_cfg.prot_if.readoc = NRF_QSPI_READOC_READ2O; + qspi_cfg.prot_if.writeoc = NRF_QSPI_WRITEOC_PP2O; +#else + qspi_cfg.pins.io1_pin = MICROPY_QSPI_DATA1; + qspi_cfg.pins.io2_pin = MICROPY_QSPI_DATA2; + qspi_cfg.pins.io3_pin = MICROPY_QSPI_DATA3; + qspi_cfg.prot_if.readoc = NRF_QSPI_READOC_READ4IO; + qspi_cfg.prot_if.writeoc = NRF_QSPI_WRITEOC_PP4IO; +#endif + + // No callback for blocking API + nrfx_qspi_init(&qspi_cfg, NULL, NULL); +} + +void spi_flash_init_device(const external_flash_device* device) { + check_quad_enable(device); + + // TODO(tannewt): Adjust the speed for the found device. +} diff --git a/ports/nrf/supervisor/serial.c b/ports/nrf/supervisor/serial.c index c7744fd79..6fd89eb3e 100644 --- a/ports/nrf/supervisor/serial.c +++ b/ports/nrf/supervisor/serial.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,9 +26,12 @@ #include "py/mphal.h" +#include "supervisor/serial.h" + #if (MICROPY_PY_BLE_NUS == 1) #include "ble_uart.h" #else +#include #include "nrf_gpio.h" #include "nrfx_uarte.h" #endif @@ -89,39 +92,36 @@ bool serial_connected(void) { } char serial_read(void) { - return (char) mp_hal_stdin_rx_chr(); + uint8_t data; + nrfx_uarte_rx(&serial_instance, &data, 1); + return data; } bool serial_bytes_available(void) { - return mp_hal_stdin_any(); + return nrf_uarte_event_check(serial_instance.p_reg, NRF_UARTE_EVENT_RXDRDY); } -void serial_write(const char *text) { - mp_hal_stdout_tx_str(text); -} - -#else - -#include "tusb.h" - -void serial_init(void) { - // usb is already initialized in board_init() +void serial_write(const char* text) { + serial_write_substring(text, strlen(text)); } -bool serial_connected(void) { - return tud_cdc_connected(); -} +void serial_write_substring(const char *text, uint32_t len) { + if (len == 0) { + return; + } -char serial_read(void) { - return (char) tud_cdc_read_char(); -} + // EasyDMA can only access SRAM + uint8_t * tx_buf = (uint8_t*) text; + if ( !nrfx_is_in_ram(text) ) { + tx_buf = (uint8_t *) m_malloc(len, false); + memcpy(tx_buf, text, len); + } -bool serial_bytes_available(void) { - return tud_cdc_available() > 0; -} + nrfx_uarte_tx(&serial_instance, tx_buf, len); -void serial_write(const char* text) { - tud_cdc_write(text, strlen(text)); + if ( !nrfx_is_in_ram(text) ) { + m_free(tx_buf); + } } #endif diff --git a/ports/nrf/supervisor/usb.c b/ports/nrf/supervisor/usb.c new file mode 100644 index 000000000..a67c9311a --- /dev/null +++ b/ports/nrf/supervisor/usb.c @@ -0,0 +1,83 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "nrfx.h" +#include "nrfx_power.h" +#include "tick.h" +#include "supervisor/usb.h" +#include "lib/utils/interrupt_char.h" +#include "lib/mp-readline/readline.h" + +#ifdef SOFTDEVICE_PRESENT +#include "nrf_sdm.h" +#include "nrf_soc.h" +#endif + +/* tinyusb function that handles power event (detected, ready, removed) + * We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled. + */ +extern void tusb_hal_nrf_power_event(uint32_t event); + +void init_usb_hardware(void) { + + // USB power may already be ready at this time -> no event generated + // We need to invoke the handler based on the status initially + uint32_t usb_reg; + +#ifdef SOFTDEVICE_PRESENT + uint8_t sd_en = false; + (void) sd_softdevice_is_enabled(&sd_en); + + if ( sd_en ) { + sd_power_usbdetected_enable(true); + sd_power_usbpwrrdy_enable(true); + sd_power_usbremoved_enable(true); + + sd_power_usbregstatus_get(&usb_reg); + }else +#endif + { + // Power module init + const nrfx_power_config_t pwr_cfg = { 0 }; + nrfx_power_init(&pwr_cfg); + + // Register tusb function as USB power handler + const nrfx_power_usbevt_config_t config = { .handler = (nrfx_power_usb_event_handler_t) tusb_hal_nrf_power_event }; + nrfx_power_usbevt_init(&config); + + nrfx_power_usbevt_enable(); + + usb_reg = NRF_POWER->USBREGSTATUS; + } + + if ( usb_reg & POWER_USBREGSTATUS_VBUSDETECT_Msk ) { + tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_DETECTED); + } + + if ( usb_reg & POWER_USBREGSTATUS_OUTPUTRDY_Msk ) { + tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_READY); + } +} diff --git a/ports/nrf/tick.h b/ports/nrf/tick.h index 73f17d703..838e9fbea 100644 --- a/ports/nrf/tick.h +++ b/ports/nrf/tick.h @@ -26,7 +26,7 @@ #ifndef MICROPY_INCLUDED_NRF_TICK_H #define MICROPY_INCLUDED_NRF_TICK_H -#include "mpconfigport.h" +#include "py/mpconfig.h" #include diff --git a/ports/nrf/usb/tusb_config.h b/ports/nrf/usb/tusb_config.h deleted file mode 100644 index 626b99eeb..000000000 --- a/ports/nrf/usb/tusb_config.h +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************/ -/*! - @file tusb_config.h - @author hathach (tinyusb.org) - - @section LICENSE - - Software License Agreement (BSD License) - - Copyright (c) 2013, hathach (tinyusb.org) - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ -/**************************************************************************/ - -#ifndef _TUSB_CONFIG_H_ -#define _TUSB_CONFIG_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// COMMON CONFIGURATION -//--------------------------------------------------------------------+ -#define CFG_TUSB_MCU OPT_MCU_NRF5X -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE - -#define CFG_TUSB_DEBUG 0 - -/*------------- RTOS -------------*/ -#define CFG_TUSB_OS OPT_OS_NONE -//#define CFG_TUD_TASK_QUEUE_SZ 16 -//#define CFG_TUD_TASK_PRIO 0 -//#define CFG_TUD_TASK_STACK_SZ 150 - -//--------------------------------------------------------------------+ -// DEVICE CONFIGURATION -//--------------------------------------------------------------------+ - -#define CFG_TUD_ENDOINT0_SIZE 64 - -/*------------- Descriptors -------------*/ -/* Enable auto generated descriptor, tinyusb will try its best to create - * descriptor ( device, configuration, hid ) that matches enabled CFG_* in this file - * - * Note: All CFG_TUD_DESC_* are relevant only if CFG_TUD_DESC_AUTO is enabled - */ -#define CFG_TUD_DESC_AUTO 0 - -//------------- CLASS -------------// -#define CFG_TUD_CDC 1 -#define CFG_TUD_MSC 1 -#define CFG_TUD_HID 1 - -/*------------------------------------------------------------------*/ -/* CLASS DRIVER - *------------------------------------------------------------------*/ - -/*------------- CDC -------------*/ -// FIFO size of CDC TX and RX -#define CFG_TUD_CDC_RX_BUFSIZE 1024 -#define CFG_TUD_CDC_TX_BUFSIZE 1024 - -/* TX is sent automatically on every Start of Frame event ~ 1ms. - * If not enabled, application must call tud_cdc_flush() periodically - * Note: Enabled this could overflow device task, if it does, define - * CFG_TUD_TASK_QUEUE_SZ with large value - */ -#define CFG_TUD_CDC_FLUSH_ON_SOF 0 - - -/*------------- MSC -------------*/ -// Number of supported Logical Unit Number (At least 1) -#define CFG_TUD_MSC_MAXLUN 1 - -// Number of Blocks -#define CFG_TUD_MSC_BLOCK_NUM (256*1024)/512 - -// Block size -#define CFG_TUD_MSC_BLOCK_SZ 512 - -// Buffer size for each read/write transfer, the more the better -#define CFG_TUD_MSC_BUFSIZE (4*1024) - -// Vendor name included in Inquiry response, max 8 bytes -#define CFG_TUD_MSC_VENDOR "Adafruit" - -// Product name included in Inquiry response, max 16 bytes -#define CFG_TUD_MSC_PRODUCT "CircuitPY nRF52" - -// Product revision string included in Inquiry response, max 4 bytes -#define CFG_TUD_MSC_PRODUCT_REV "1.0" - - -//--------------------------------------------------------------------+ -// USB RAM PLACEMENT -//--------------------------------------------------------------------+ -#define CFG_TUSB_ATTR_USBRAM -#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4) - - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_CONFIG_H_ */ diff --git a/ports/nrf/usb/usb.c b/ports/nrf/usb/usb.c deleted file mode 100644 index 248d31e21..000000000 --- a/ports/nrf/usb/usb.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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 "nrfx.h" -#include "nrfx_power.h" -#include "tick.h" -#include "usb.h" -#include "lib/utils/interrupt_char.h" -#include "lib/mp-readline/readline.h" - -#ifdef SOFTDEVICE_PRESENT -#include "nrf_sdm.h" -#include "nrf_soc.h" -#endif - -/* tinyusb function that handles power event (detected, ready, removed) - * We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled. - */ -extern void tusb_hal_nrf_power_event(uint32_t event); - -void usb_init(void) { - - // USB power may already be ready at this time -> no event generated - // We need to invoke the handler based on the status initially - uint32_t usb_reg; - -#ifdef SOFTDEVICE_PRESENT - uint8_t sd_en = false; - (void) sd_softdevice_is_enabled(&sd_en); - - if ( sd_en ) { - sd_power_usbdetected_enable(true); - sd_power_usbpwrrdy_enable(true); - sd_power_usbremoved_enable(true); - - sd_power_usbregstatus_get(&usb_reg); - }else -#endif - { - // Power module init - const nrfx_power_config_t pwr_cfg = { 0 }; - nrfx_power_init(&pwr_cfg); - - // Register tusb function as USB power handler - const nrfx_power_usbevt_config_t config = { .handler = (nrfx_power_usb_event_handler_t) tusb_hal_nrf_power_event }; - nrfx_power_usbevt_init(&config); - - nrfx_power_usbevt_enable(); - - usb_reg = NRF_POWER->USBREGSTATUS; - } - - if ( usb_reg & POWER_USBREGSTATUS_VBUSDETECT_Msk ) { - tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_DETECTED); - } - - if ( usb_reg & POWER_USBREGSTATUS_OUTPUTRDY_Msk ) { - tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_READY); - } - - // create serial number based on device unique id - extern uint16_t usb_desc_str_serial[1 + 16]; - - char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 8; j++) { - uint8_t nibble = (NRF_FICR->DEVICEID[i] >> j * 4) & 0xf; - - // Invert order since it is LE, +1 for skipping descriptor header - uint8_t const idx = (15 - (i * 8 + j)) + 1; - usb_desc_str_serial[idx] = nibble_to_hex[nibble]; - } - } - - tusb_init(); - -#if MICROPY_KBD_EXCEPTION - // Set Ctrl+C as wanted char, tud_cdc_rx_wanted_cb() callback will be invoked when Ctrl+C is received - // This callback always got invoked regardless of mp_interrupt_char value since we only set it once here - tud_cdc_set_wanted_char(CHAR_CTRL_C); -#endif -} - -//--------------------------------------------------------------------+ -// tinyusb callbacks -//--------------------------------------------------------------------+ - -// Invoked when device is mounted -void tud_mount_cb(void) { -} - -// Invoked when device is unmounted -void tud_umount_cb(void) { -} - -uint32_t tusb_hal_millis(void) { - uint64_t ms; - uint32_t us; - current_tick(&ms, &us); - return (uint32_t) ms; -} - - -// Invoked when cdc when line state changed e.g connected/disconnected -// Use to reset to DFU when disconnect with 1200 bps -void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { - (void) itf; // interface ID, not used - - // DTR = false is counted as disconnected - if ( !dtr ) - { - cdc_line_coding_t coding; - tud_cdc_get_line_coding(&coding); - - if ( coding.bit_rate == 1200 ) - { - enum { DFU_MAGIC_SERIAL = 0x4e }; - - NRF_POWER->GPREGRET = DFU_MAGIC_SERIAL; - NVIC_SystemReset(); - } - } -} - -#if MICROPY_KBD_EXCEPTION - -/** - * Callback invoked when received an "wanted" char. - * @param itf Interface index (for multiple cdc interfaces) - * @param wanted_char The wanted char (set previously) - */ -void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) -{ - (void) itf; // not used - - // Workaround for using lib/utils/interrupt_char.c - // Compare mp_interrupt_char with wanted_char and ignore if not matched - if (mp_interrupt_char == wanted_char) { - tud_cdc_read_flush(); // flush read fifo - mp_keyboard_interrupt(); - } -} - -#endif - diff --git a/ports/nrf/usb/usb.h b/ports/nrf/usb/usb.h deleted file mode 100644 index 203260874..000000000 --- a/ports/nrf/usb/usb.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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. - */ - -#ifndef MICROPY_INCLUDED_NRF_USB_H -#define MICROPY_INCLUDED_NRF_USB_H - -#include "tusb.h" - -void usb_init(void); - -#endif // MICROPY_INCLUDED_NRF_USB_H diff --git a/ports/nrf/usb/usb_desc.c b/ports/nrf/usb/usb_desc.c deleted file mode 100644 index 10d603528..000000000 --- a/ports/nrf/usb/usb_desc.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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 "usb_desc.h" -#include "common-hal/usb_hid/Device.h" - -//--------------------------------------------------------------------+ -// MACRO TYPEDEF CONSTANT ENUM DECLARATION -//--------------------------------------------------------------------+ -#define USB_VID 0x239A - -/* Note: different class combination e.g CDC and (CDC + MSC) should have different - * PID since Host OS will "remembered" device driver after the first plug */ -#define USB_PID 0x802A - -/*------------- Interface Numbering -------------*/ -enum { - ITF_NUM_CDC = 0 , - ITF_NUM_CDC_DATA , - ITF_NUM_MSC , - ITF_NUM_HID_GEN , - ITF_NUM_TOTAL -}; - -enum { - ITF_STR_LANGUAGE = 0 , - ITF_STR_MANUFACTURER , - ITF_STR_PRODUCT , - ITF_STR_SERIAL , - ITF_STR_CDC , - ITF_STR_MSC , - ITF_STR_HID -}; - -/*------------- Endpoint Numbering & Size -------------*/ -#define _EP_IN(x) (0x80 | (x)) -#define _EP_OUT(x) (x) - -// CDC -#define EP_CDC_NOTIF _EP_IN ( ITF_NUM_CDC+1 ) -#define EP_CDC_NOTIF_SIZE 8 - -#define EP_CDC_OUT _EP_OUT( ITF_NUM_CDC+2 ) -#define EP_CDC_IN _EP_IN ( ITF_NUM_CDC+2 ) - -// Mass Storage -#define EP_MSC_OUT _EP_OUT( ITF_NUM_MSC+1 ) -#define EP_MSC_IN _EP_IN ( ITF_NUM_MSC+1 ) - -// HID composite = keyboard + mouse + gamepad + etc ... -#define EP_HID_GEN _EP_IN ( ITF_NUM_HID_GEN+1 ) -#define EP_HID_GEN_SIZE 16 - -//--------------------------------------------------------------------+ -// STRING DESCRIPTORS -//--------------------------------------------------------------------+ - -uint16_t usb_desc_str_serial[1+16] = { TUD_DESC_STR_HEADER(16) }; - -// array of pointer to string descriptors -uint16_t const * const string_desc_arr [] = -{ - // 0 index is supported language = English - TUD_DESC_STRCONV(0x0409), - - // 1 Manufacturer - TUD_DESC_STRCONV('A','d','a','f','r','u','i','t',' ','I','n','d','u','s','t','r','i','e','s'), - - // 2 Product - TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','n','R','F','5','2'), - - // 3 Serials TODO use chip ID - usb_desc_str_serial, - - // 4 CDC Interface - TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','S','e','r','i','a','l'), - - // 5 MSC Interface - TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','S','t','o','r','a','g','e'), - - // 6 HID Interface - TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','H','I','D'), - - // Custom Interface -// TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','C','u','s','t','o','m') -}; - - -//--------------------------------------------------------------------+ -// Device Descriptor -//--------------------------------------------------------------------+ -tusb_desc_device_t const usb_desc_dev = -{ - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - - #if CFG_TUD_CDC - // Use Interface Association Descriptor (IAD) for CDC - // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - #else - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - #endif - - .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, - - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = 0x0100, - - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - - .bNumConfigurations = 0x01 // TODO multiple configurations -}; - -//--------------------------------------------------------------------+ -// HID Report Descriptor -//--------------------------------------------------------------------+ -uint8_t const usb_desc_hid_generic_report[] = -{ -#if USB_HID_DEVICE_KEYBOARD - HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(USB_HID_REPORT_ID_KEYBOARD), ), -#endif - -#if USB_HID_DEVICE_MOUSE - HID_REPORT_DESC_MOUSE( HID_REPORT_ID(USB_HID_REPORT_ID_MOUSE), ), -#endif - -#if USB_HID_DEVICE_CONSUMER - HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(USB_HID_REPORT_ID_CONSUMER), ), -#endif - -#if USB_HID_DEVICE_SYS_CONTROL - HID_REPORT_DESC_SYSTEM_CONTROL( HID_REPORT_ID(USB_HID_REPORT_ID_SYS_CONTROL ), ), -#endif - -#if USB_HID_DEVICE_GAMEPAD - HID_REPORT_DESC_GAMEPAD( HID_REPORT_ID(USB_HID_REPORT_ID_GAMEPAD ), ) -#endif - -}; - -//--------------------------------------------------------------------+ -// Configuration Descriptor -//--------------------------------------------------------------------+ -usb_desc_cfg_t const usb_desc_cfg = -{ - .config = - { - .bLength = sizeof(tusb_desc_configuration_t), - .bDescriptorType = TUSB_DESC_CONFIGURATION, - .wTotalLength = sizeof(usb_desc_cfg_t), - .bNumInterfaces = ITF_NUM_TOTAL, - .bConfigurationValue = 1, - .iConfiguration = 0x00, - .bmAttributes = TUSB_DESC_CONFIG_ATT_BUS_POWER, - .bMaxPower = TUSB_DESC_CONFIG_POWER_MA(100) - }, - - // IAD points to CDC Interfaces - .cdc = - { - .iad = - { - .bLength = sizeof(tusb_desc_interface_assoc_t), - .bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION, - - .bFirstInterface = ITF_NUM_CDC, - .bInterfaceCount = 2, - - .bFunctionClass = TUSB_CLASS_CDC, - .bFunctionSubClass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, - .bFunctionProtocol = CDC_COMM_PROTOCOL_ATCOMMAND, - .iFunction = 0 - }, - - //------------- CDC Communication Interface -------------// - .comm_itf = - { - .bLength = sizeof(tusb_desc_interface_t), - .bDescriptorType = TUSB_DESC_INTERFACE, - .bInterfaceNumber = ITF_NUM_CDC, - .bAlternateSetting = 0, - .bNumEndpoints = 1, - .bInterfaceClass = TUSB_CLASS_CDC, - .bInterfaceSubClass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, - .bInterfaceProtocol = CDC_COMM_PROTOCOL_ATCOMMAND, - .iInterface = ITF_STR_CDC - }, - - .header = - { - .bLength = sizeof(cdc_desc_func_header_t), - .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, - .bDescriptorSubType = CDC_FUNC_DESC_HEADER, - .bcdCDC = 0x0120 - }, - - .call = - { - .bLength = sizeof(cdc_desc_func_call_management_t), - .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, - .bDescriptorSubType = CDC_FUNC_DESC_CALL_MANAGEMENT, - .bmCapabilities = { 0 }, - .bDataInterface = ITF_NUM_CDC+1, - }, - - .acm = - { - .bLength = sizeof(cdc_desc_func_acm_t), - .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, - .bDescriptorSubType = CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, - .bmCapabilities = { // 0x02 - .support_line_request = 1, - } - }, - - .union_func = - { - .bLength = sizeof(cdc_desc_func_union_t), // plus number of - .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, - .bDescriptorSubType = CDC_FUNC_DESC_UNION, - .bControlInterface = ITF_NUM_CDC, - .bSubordinateInterface = ITF_NUM_CDC+1, - }, - - .ep_notif = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = EP_CDC_NOTIF, - .bmAttributes = { .xfer = TUSB_XFER_INTERRUPT }, - .wMaxPacketSize = { .size = EP_CDC_NOTIF_SIZE }, - .bInterval = 0x10 - }, - - //------------- CDC Data Interface -------------// - .data_itf = - { - .bLength = sizeof(tusb_desc_interface_t), - .bDescriptorType = TUSB_DESC_INTERFACE, - .bInterfaceNumber = ITF_NUM_CDC+1, - .bAlternateSetting = 0x00, - .bNumEndpoints = 2, - .bInterfaceClass = TUSB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0x00 - }, - - .ep_out = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = EP_CDC_OUT, - .bmAttributes = { .xfer = TUSB_XFER_BULK }, - .wMaxPacketSize = { .size = CFG_TUD_CDC_EPSIZE }, - .bInterval = 0 - }, - - .ep_in = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = EP_CDC_IN, - .bmAttributes = { .xfer = TUSB_XFER_BULK }, - .wMaxPacketSize = { .size = CFG_TUD_CDC_EPSIZE }, - .bInterval = 0 - }, - }, - - //------------- Mass Storage-------------// - .msc = - { - .itf = - { - .bLength = sizeof(tusb_desc_interface_t), - .bDescriptorType = TUSB_DESC_INTERFACE, - .bInterfaceNumber = ITF_NUM_MSC, - .bAlternateSetting = 0x00, - .bNumEndpoints = 2, - .bInterfaceClass = TUSB_CLASS_MSC, - .bInterfaceSubClass = MSC_SUBCLASS_SCSI, - .bInterfaceProtocol = MSC_PROTOCOL_BOT, - .iInterface = ITF_STR_MSC - }, - - .ep_out = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = EP_MSC_OUT, - .bmAttributes = { .xfer = TUSB_XFER_BULK }, - .wMaxPacketSize = { .size = CFG_TUD_MSC_EPSIZE}, - .bInterval = 1 - }, - - .ep_in = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = EP_MSC_IN, - .bmAttributes = { .xfer = TUSB_XFER_BULK }, - .wMaxPacketSize = { .size = CFG_TUD_MSC_EPSIZE}, - .bInterval = 1 - } - }, - - //------------- HID Generic Multiple report -------------// - .hid_generic = - { - .itf = - { - .bLength = sizeof(tusb_desc_interface_t), - .bDescriptorType = TUSB_DESC_INTERFACE, - .bInterfaceNumber = ITF_NUM_HID_GEN, - .bAlternateSetting = 0x00, - .bNumEndpoints = 1, - .bInterfaceClass = TUSB_CLASS_HID, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = ITF_STR_HID - }, - - .hid_desc = - { - .bLength = sizeof(tusb_hid_descriptor_hid_t), - .bDescriptorType = HID_DESC_TYPE_HID, - .bcdHID = 0x0111, - .bCountryCode = HID_Local_NotSupported, - .bNumDescriptors = 1, - .bReportType = HID_DESC_TYPE_REPORT, - .wReportLength = sizeof(usb_desc_hid_generic_report) - }, - - .ep_in = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = EP_HID_GEN, - .bmAttributes = { .xfer = TUSB_XFER_INTERRUPT }, - .wMaxPacketSize = { .size = EP_HID_GEN_SIZE }, - .bInterval = 0x0A - } - } -}; - - -// tud_desc_set is required by tinyusb stack -tud_desc_set_t tud_desc_set = -{ - .device = &usb_desc_dev, - .config = &usb_desc_cfg, - .string_arr = (uint8_t const **) string_desc_arr, - .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]), - - .hid_report = - { - .generic = usb_desc_hid_generic_report, - .boot_keyboard = NULL, - .boot_mouse = NULL - } -}; diff --git a/ports/nrf/usb/usb_desc.h b/ports/nrf/usb/usb_desc.h deleted file mode 100644 index 19f3f6d5b..000000000 --- a/ports/nrf/usb/usb_desc.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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. - */ - -#ifndef USB_DESC_H_ -#define USB_DESC_H_ - -#include "tusb.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/*------------- Configuration Descriptor -------------*/ -typedef struct ATTR_PACKED -{ - tusb_desc_configuration_t config; - - //------------- CDC -------------// - struct ATTR_PACKED - { - tusb_desc_interface_assoc_t iad; - - //CDC Control Interface - tusb_desc_interface_t comm_itf; - cdc_desc_func_header_t header; - cdc_desc_func_call_management_t call; - cdc_desc_func_acm_t acm; - cdc_desc_func_union_t union_func; - tusb_desc_endpoint_t ep_notif; - - //CDC Data Interface - tusb_desc_interface_t data_itf; - tusb_desc_endpoint_t ep_out; - tusb_desc_endpoint_t ep_in; - }cdc; - - //------------- Mass Storage -------------// - struct ATTR_PACKED - { - tusb_desc_interface_t itf; - tusb_desc_endpoint_t ep_out; - tusb_desc_endpoint_t ep_in; - } msc; - - //------------- HID -------------// - struct ATTR_PACKED - { - tusb_desc_interface_t itf; - tusb_hid_descriptor_hid_t hid_desc; - tusb_desc_endpoint_t ep_in; - } hid_generic; - -} usb_desc_cfg_t; - - -// Descriptors set used by tinyusb stack -extern tud_desc_set_t tud_desc_set; - - -#ifdef __cplusplus - } -#endif - -#endif /* USB_DESC_H_ */ diff --git a/ports/nrf/usb/usb_msc_flash.c b/ports/nrf/usb/usb_msc_flash.c deleted file mode 100644 index f78c24145..000000000 --- a/ports/nrf/usb/usb_msc_flash.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 hathach for Adafruit Industries - * - * 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 "tusb.h" -#include "internal_flash.h" - -// For updating fatfs's cache -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "lib/oofatfs/ff.h" -#include "py/mpstate.h" - -#include "supervisor/shared/autoreload.h" - -/*------------------------------------------------------------------*/ -/* MACRO TYPEDEF CONSTANT ENUM - *------------------------------------------------------------------*/ -#define MSC_FLASH_ADDR_END 0xED000 -#define MSC_FLASH_SIZE (256*1024) -#define MSC_FLASH_ADDR_START (MSC_FLASH_ADDR_END-MSC_FLASH_SIZE) -#define MSC_FLASH_BLOCK_SIZE 512 - -#define FL_PAGE_SZ 4096 - -// Callback invoked when received an SCSI command not in built-in list below -// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE -// - READ10 and WRITE10 has their own callbacks -int32_t tud_msc_scsi_cb (uint8_t lun, const uint8_t scsi_cmd[16], void* buffer, uint16_t bufsize) { - const void* response = NULL; - uint16_t resplen = 0; - - switch ( scsi_cmd[0] ) { - case SCSI_CMD_TEST_UNIT_READY: - // Command that host uses to check our readiness before sending other commands - resplen = 0; - break; - - case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: - // Host is about to read/write etc ... better not to disconnect disk - resplen = 0; - break; - - case SCSI_CMD_START_STOP_UNIT: - // Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power - /* scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd; - // Start bit = 0 : low power mode, if load_eject = 1 : unmount disk storage as well - // Start bit = 1 : Ready mode, if load_eject = 1 : mount disk storage - start_stop->start; - start_stop->load_eject; - */ - resplen = 0; - break; - - default: - // Set Sense = Invalid Command Operation - tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); - - // negative means error -> tinyusb could stall and/or response with failed status - resplen = -1; - break; - } - - // return len must not larger than bufsize - if ( resplen > bufsize ) { - resplen = bufsize; - } - - // copy response to stack's buffer if any - if ( response && resplen ) { - memcpy(buffer, response, resplen); - } - - return resplen; -} - -// Callback invoked when received READ10 command. -// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. -int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { - (void) lun; - (void) offset; - - const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE; - - internal_flash_read_blocks(buffer, lba, block_count); - - return block_count * MSC_FLASH_BLOCK_SIZE; -} - -// Callback invoked when received WRITE10 command. -// Process data in buffer to disk's storage and return number of written bytes -int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { - (void) lun; - (void) offset; - - const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE; - - // bufsize <= CFG_TUD_MSC_BUFSIZE (4096) - internal_flash_write_blocks(buffer, lba, block_count); - - // update fatfs's cache if address matches - fs_user_mount_t* vfs = MP_STATE_VM(vfs_mount_table)->obj; - - if ( (lba <= vfs->fatfs.winsect) && (vfs->fatfs.winsect <= (lba + bufsize / MSC_FLASH_BLOCK_SIZE)) ) { - memcpy(vfs->fatfs.win, buffer + MSC_FLASH_BLOCK_SIZE * (vfs->fatfs.winsect - lba), MSC_FLASH_BLOCK_SIZE); - } - - return block_count * MSC_FLASH_BLOCK_SIZE; -} - -// Callback invoked when WRITE10 command is completed (status received and accepted by host). -// used to flush any pending cache. -void tud_msc_write10_complete_cb (uint8_t lun) { - (void) lun; - - // flush pending cache when write10 is complete - internal_flash_flush(); - - // This write is complete, start the autoreload clock. - autoreload_start(); -} diff --git a/shared-bindings/busio/SPI.h b/shared-bindings/busio/SPI.h index 555f32c92..2d12b8b76 100644 --- a/shared-bindings/busio/SPI.h +++ b/shared-bindings/busio/SPI.h @@ -61,4 +61,7 @@ extern bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_o // Return actual SPI bus frequency. uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t* self); +// This is used by the supervisor to claim SPI devices indefinitely. +extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H diff --git a/shared-bindings/usb_hid/Device.h b/shared-bindings/usb_hid/Device.h index 2bc553c4a..cb9a64b5e 100644 --- a/shared-bindings/usb_hid/Device.h +++ b/shared-bindings/usb_hid/Device.h @@ -27,7 +27,7 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H #define MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H -#include "common-hal/usb_hid/Device.h" +#include "shared-module/usb_hid/Device.h" const mp_obj_type_t usb_hid_device_type; diff --git a/shared-module/bitbangio/SPI.c b/shared-module/bitbangio/SPI.c index 9e4508355..6d3a28523 100644 --- a/shared-module/bitbangio/SPI.c +++ b/shared-module/bitbangio/SPI.c @@ -24,8 +24,7 @@ * THE SOFTWARE. */ -#include "mpconfigport.h" - +#include "py/mpconfig.h" #include "py/obj.h" #include "py/runtime.h" diff --git a/shared-module/storage/__init__.c b/shared-module/storage/__init__.c index edce286b1..f09edd785 100644 --- a/shared-module/storage/__init__.c +++ b/shared-module/storage/__init__.c @@ -32,8 +32,12 @@ #include "py/mperrno.h" #include "py/obj.h" #include "py/runtime.h" +#include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/os/__init__.h" #include "shared-bindings/storage/__init__.h" +#include "supervisor/filesystem.h" +#include "supervisor/flash.h" +#include "supervisor/usb.h" STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) { if (vfs == MP_VFS_NONE) { @@ -138,3 +142,25 @@ void common_hal_storage_umount_path(const char* mount_path) { mp_obj_t common_hal_storage_getmount(const char *mount_path) { return storage_object_from_path(mount_path); } + +void common_hal_storage_remount(const char *mount_path, bool readonly) { + if (strcmp(mount_path, "/") != 0) { + mp_raise_OSError(MP_EINVAL); + } + + #ifdef USB_AVAILABLE + // TODO(dhalbert): is this is a good enough check? It checks for + // CDC enabled. There is no "MSC enabled" check. + if (usb_enabled()) { + mp_raise_RuntimeError(translate("Cannot remount '/' when USB is active.")); + } + #endif + + supervisor_flash_set_usb_writable(readonly); +} + +void common_hal_storage_erase_filesystem(void) { + filesystem_init(false, true); // Force a re-format. + common_hal_mcu_reset(); + // We won't actually get here, since we're resetting. +} diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c new file mode 100644 index 000000000..820e14ad0 --- /dev/null +++ b/shared-module/usb_hid/Device.c @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 +#include "tick.h" +#include "py/runtime.h" +#include "shared-bindings/usb_hid/Device.h" +#include "shared-module/usb_hid/Device.h" +#include "supervisor/shared/translate.h" +#include "tusb.h" + +uint8_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self) { + return self->usage_page; +} + +uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) { + return self->usage; +} + +void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { + if (len != self->report_length) { + mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->report_length); + } + + // Wait until interface is ready, timeout = 2 seconds + uint64_t end_ticks = ticks_ms + 2000; + while ( (ticks_ms < end_ticks) && !tud_hid_generic_ready() ) { } + + if ( !tud_hid_generic_ready() ) { + mp_raise_msg(&mp_type_OSError, translate("USB Busy")); + } + + memcpy(self->report_buffer, report, len); + + if ( !tud_hid_generic_report(self->report_id, self->report_buffer, len) ) { + mp_raise_msg(&mp_type_OSError, translate("USB Error")); + } +} + +// Callbacks invoked when receive Get_Report request through control endpoint +uint16_t tud_hid_generic_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + // only support Input Report + if ( report_type != HID_REPORT_TYPE_INPUT ) return 0; + + // index is ID-1 + uint8_t idx = ( report_id ? (report_id-1) : 0 ); + + // fill buffer with current report + memcpy(buffer, usb_hid_devices[idx].report_buffer, reqlen); + return reqlen; +} + +// Callbacks invoked when receive Set_Report request through control endpoint +void tud_hid_generic_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { + // index is ID-1 + uint8_t idx = ( report_id ? (report_id-1) : 0 ); + + if ( report_type == HID_REPORT_TYPE_OUTPUT ) { + // Check if it is Keyboard device + if ( (usb_hid_devices[idx].usage_page == HID_USAGE_PAGE_DESKTOP) && (usb_hid_devices[idx].usage == HID_USAGE_DESKTOP_KEYBOARD) ) { + // This is LED indicator (CapsLock, NumLock) + // TODO Light up some LED here + } + } +} diff --git a/shared-module/usb_hid/Device.h b/shared-module/usb_hid/Device.h new file mode 100644 index 000000000..10f2ee897 --- /dev/null +++ b/shared-module/usb_hid/Device.h @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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. + */ + +#ifndef SHARED_MODULE_USB_HID_DEVICE_H +#define SHARED_MODULE_USB_HID_DEVICE_H + +#include +#include + +#include "py/obj.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct { + mp_obj_base_t base; + uint8_t* report_buffer; + uint8_t report_id; + uint8_t report_length; + uint8_t usage_page; + uint8_t usage; +} usb_hid_device_obj_t; + + +extern usb_hid_device_obj_t usb_hid_devices[]; + +#ifdef __cplusplus + } +#endif + +#endif /* SHARED_MODULE_USB_HID_DEVICE_H */ diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c new file mode 100644 index 000000000..c0f9c897c --- /dev/null +++ b/shared-module/usb_hid/__init__.c @@ -0,0 +1,154 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "genhdr/autogen_usb_descriptor.h" +#include "shared-module/usb_hid/Device.h" +#include "shared-bindings/usb_hid/Device.h" +#include "tusb.h" + +#ifdef USB_HID_REPORT_ID_KEYBOARD +static uint8_t keyboard_report_buffer[USB_HID_REPORT_LENGTH_KEYBOARD]; +#endif + +#ifdef USB_HID_REPORT_ID_MOUSE +static uint8_t mouse_report_buffer[USB_HID_REPORT_LENGTH_MOUSE]; +#endif + +#ifdef USB_HID_REPORT_ID_CONSUMER +static uint8_t consumer_report_buffer[USB_HID_REPORT_LENGTH_CONSUMER]; +#endif + +#ifdef USB_HID_REPORT_ID_SYS_CONTROL +static uint8_t sys_control_report_buffer[USB_HID_REPORT_LENGTH_SYS_CONTROL]; +#endif + +#ifdef USB_HID_REPORT_ID_GAMEPAD +static uint8_t gamepad_report_buffer[USB_HID_REPORT_LENGTH_GAMEPAD]; +#endif + +#ifdef USB_HID_REPORT_ID_DIGITIZER +static uint8_t digitizer_report_buffer[USB_HID_REPORT_LENGTH_DIGITIZER]; +#endif + +usb_hid_device_obj_t usb_hid_devices[] = { +#if USB_HID_REPORT_ID_KEYBOARD + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = keyboard_report_buffer , + .report_id = USB_HID_REPORT_ID_KEYBOARD , + .report_length = USB_HID_REPORT_LENGTH_KEYBOARD , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_KEYBOARD , + }, +#endif + +#if USB_HID_REPORT_ID_MOUSE + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = mouse_report_buffer , + .report_id = USB_HID_REPORT_ID_MOUSE , + .report_length = USB_HID_REPORT_LENGTH_MOUSE , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_MOUSE , + }, +#endif + +#if USB_HID_REPORT_ID_CONSUMER + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = consumer_report_buffer , + .report_id = USB_HID_REPORT_ID_CONSUMER , + .report_length = USB_HID_REPORT_LENGTH_CONSUMER , + .usage_page = HID_USAGE_PAGE_CONSUMER , + .usage = HID_USAGE_CONSUMER_CONTROL , + }, +#endif + +#if USB_HID_REPORT_ID_SYS_CONTROL + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = sys_control_report_buffer , + .report_id = USB_HID_REPORT_ID_SYS_CONTROL , + .report_length = USB_HID_REPORT_LENGTH_SYS_CONTROL , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_SYSTEM_CONTROL , + }, +#endif + +#if USB_HID_REPORT_ID_GAMEPAD + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = gamepad_report_buffer , + .report_id = USB_HID_REPORT_ID_GAMEPAD , + .report_length = USB_HID_REPORT_LENGTH_GAMEPAD , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_GAMEPAD , + }, +#endif + +#if USB_HID_REPORT_ID_DIGITIZER + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = digitizer_report_buffer , + .report_id = USB_HID_REPORT_ID_DIGITIZER , + .report_length = USB_HID_REPORT_LENGTH_DIGITIZER , + .usage_page = 0x0D , + .usage = 0x02 , + }, +#endif +}; + + +mp_obj_tuple_t common_hal_usb_hid_devices = { + .base = { + .type = &mp_type_tuple, + }, + .len = USB_HID_NUM_DEVICES, + .items = { +#if USB_HID_NUM_DEVICES >= 1 + (mp_obj_t) &usb_hid_devices[0], +#endif +#if USB_HID_NUM_DEVICES >= 2 + (mp_obj_t) &usb_hid_devices[1], +#endif +#if USB_HID_NUM_DEVICES >= 3 + (mp_obj_t) &usb_hid_devices[2], +#endif +#if USB_HID_NUM_DEVICES >= 4 + (mp_obj_t) &usb_hid_devices[3], +#endif +#if USB_HID_NUM_DEVICES >= 5 + (mp_obj_t) &usb_hid_devices[4], +#endif +#if USB_HID_NUM_DEVICES >= 6 + (mp_obj_t) &usb_hid_devices[5], +#endif + } +}; diff --git a/supervisor/flash.h b/supervisor/flash.h new file mode 100644 index 000000000..ae6a44fd0 --- /dev/null +++ b/supervisor/flash.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_SUPERVISOR_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_FLASH_H + +#include +#include + +#include "py/mpconfig.h" + +#ifdef EXTERNAL_FLASH_DEVICE_COUNT +#include "supervisor/shared/external_flash/external_flash.h" +#else +#include "supervisor/internal_flash.h" +#endif + +void supervisor_flash_set_usb_writable(bool usb_writable); +void supervisor_flash_init(void); +uint32_t supervisor_flash_get_block_size(void); +uint32_t supervisor_flash_get_block_count(void); +void supervisor_flash_flush(void); + +// these return 0 on success, non-zero on error +mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); +mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); + +struct _fs_user_mount_t; +void supervisor_flash_init_vfs(struct _fs_user_mount_t *vfs); +void supervisor_flash_flush(void); + +#endif // MICROPY_INCLUDED_SUPERVISOR_FLASH_H diff --git a/supervisor/flash_root_pointers.h b/supervisor/flash_root_pointers.h new file mode 100644 index 000000000..634ae58d3 --- /dev/null +++ b/supervisor/flash_root_pointers.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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. + */ +#ifndef MICROPY_INCLUDED_SUPERVISOR_FLASH_ROOT_POINTERS_H +#define MICROPY_INCLUDED_SUPERVISOR_FLASH_ROOT_POINTERS_H + +#ifdef EXTERNAL_FLASH_DEVICE_COUNT +#include "supervisor/shared/external_flash/external_flash_root_pointers.h" +#else +#include "supervisor/internal_flash_root_pointers.h" +#endif + +#endif // MICROPY_INCLUDED_SUPERVISOR_FLASH_ROOT_POINTERS_H diff --git a/supervisor/port.h b/supervisor/port.h index f30d87b47..dd4a82209 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -57,6 +57,9 @@ void reset_port(void); // Reset the rest of the board. void reset_board(void); +// Reset to the bootloader +void reset_to_bootloader(void); + #ifdef NRF52_SERIES void HardFault_Handler(void); #endif diff --git a/supervisor/serial.h b/supervisor/serial.h index 9446ee71c..84b3062a3 100644 --- a/supervisor/serial.h +++ b/supervisor/serial.h @@ -30,8 +30,18 @@ #include #include +#include "py/mpconfig.h" + +#ifdef CIRCUITPY_BOOT_OUTPUT_FILE +#include "lib/oofatfs/ff.h" + +FIL* boot_output_file; +#endif + void serial_init(void); void serial_write(const char* text); +// Only writes up to given length. Does not check for null termination at all. +void serial_write_substring(const char* text, uint32_t length); char serial_read(void); bool serial_bytes_available(void); bool serial_connected(void); diff --git a/supervisor/shared/external_flash/common_commands.h b/supervisor/shared/external_flash/common_commands.h new file mode 100644 index 000000000..cc0da2175 --- /dev/null +++ b/supervisor/shared/external_flash/common_commands.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H +#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H + +#define CMD_READ_JEDEC_ID 0x9f +#define CMD_READ_DATA 0x03 +#define CMD_FAST_READ_DATA 0x0B +#define CMD_SECTOR_ERASE 0x20 +// #define CMD_SECTOR_ERASE CMD_READ_JEDEC_ID +#define CMD_DISABLE_WRITE 0x04 +#define CMD_ENABLE_WRITE 0x06 +#define CMD_PAGE_PROGRAM 0x02 +// #define CMD_PAGE_PROGRAM CMD_READ_JEDEC_ID +#define CMD_READ_STATUS 0x05 +#define CMD_READ_STATUS2 0x35 +#define CMD_WRITE_STATUS_BYTE1 0x01 +#define CMD_WRITE_STATUS_BYTE2 0x31 +#define CMD_DUAL_READ 0x3b +#define CMD_QUAD_READ 0x6b +#define CMD_ENABLE_RESET 0x66 +#define CMD_RESET 0x99 + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H diff --git a/supervisor/shared/external_flash/devices.h b/supervisor/shared/external_flash/devices.h new file mode 100644 index 000000000..06fc178cb --- /dev/null +++ b/supervisor/shared/external_flash/devices.h @@ -0,0 +1,375 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H +#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H + +#include +#include + +typedef struct { + uint32_t total_size; + uint16_t start_up_time_us; + + // Three response bytes to 0x9f JEDEC ID command. + uint8_t manufacturer_id; + uint8_t memory_type; + uint8_t capacity; + + // Max clock speed for all operations and the fastest read mode. + uint8_t max_clock_speed_mhz; + + // Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the highest byte in the + // status register. + uint8_t quad_enable_bit_mask; + + bool has_sector_protection : 1; + + // Supports the 0x0b fast read command with 8 dummy cycles. + bool supports_fast_read : 1; + + // Supports the fast read, quad output command 0x6b with 8 dummy cycles. + bool supports_qspi : 1; + + // Supports the quad input page program command 0x32. This is known as 1-1-4 because it only + // uses all four lines for data. + bool supports_qspi_writes: 1; + + // Requires a separate command 0x31 to write to the second byte of the status register. + // Otherwise two byte are written via 0x01. + bool write_status_register_split: 1; + + // True when the status register is a single byte. This implies the Quad Enable bit is in the + // first byte and the Read Status Register 2 command (0x35) is unsupported. + bool single_status_byte: 1; +} external_flash_device; + +// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21 +// Xplained board. +// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf +#define AT25DF081A {\ + .total_size = (1 << 20), /* 1 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0x1f, \ + .memory_type = 0x45, \ + .capacity = 0x01, \ + .max_clock_speed_mhz = 85, \ + .quad_enable_bit_mask = 0x00, \ + .has_sector_protection = true, \ + .supports_fast_read = true, \ + .supports_qspi = false, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Gigadevice GD25Q16C 2MiB SPI flash. +// Datasheet: http://www.gigadevice.com/wp-content/uploads/2017/12/DS-00086-GD25Q16C-Rev2.6.pdf +#define GD25Q16C {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xc8, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Gigadevice GD25Q64C 8MiB SPI flash. +// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf +#define GD25Q64C {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xc8, \ + .memory_type = 0x40, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = true, \ + .single_status_byte = false, \ +} + +// Settings for the Cypress (was Spansion) S25FL064L 8MiB SPI flash. +// Datasheet: http://www.cypress.com/file/316661/download +#define S25FL064L {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 300, \ + .manufacturer_id = 0x01, \ + .memory_type = 0x60, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 108, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Cypress (was Spansion) S25FL116K 2MiB SPI flash. +// Datasheet: http://www.cypress.com/file/196886/download +#define S25FL116K {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0x01, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 108, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Cypress (was Spansion) S25FL216K 2MiB SPI flash. +// Datasheet: http://www.cypress.com/file/197346/download +#define S25FL216K {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0x01, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 65, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = false, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q16FW 2MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q16fw%20revj%2005182017%20sfdp.pdf +#define W25Q16FW {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x60, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf +#define W25Q16JV_IQ {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q16JV-IM 2MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40) +// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf +#define W25Q16JV_IM {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 133, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ +} + +// Settings for the Winbond W25Q32BV 4MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf +#define W25Q32BV {\ + .total_size = (1 << 22), /* 4 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x60, \ + .capacity = 0x16, \ + .max_clock_speed_mhz = 104, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} +// Settings for the Winbond W25Q32JV-IM 4MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q32jv%20revg%2003272018%20plus.pdf +#define W25Q32JV_IM {\ + .total_size = (1 << 22), /* 4 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x16, \ + .max_clock_speed_mhz = 133, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ +} + +// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40) +// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf +#define W25Q64JV_IM {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q64JV-IQ 8MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf +#define W25Q64JV_IQ {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x40, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q80DL 1MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf +#define W25Q80DL {\ + .total_size = (1 << 20), /* 1 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x60, \ + .capacity = 0x14, \ + .max_clock_speed_mhz = 104, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + + +// Settings for the Winbond W25Q128JV-SQ 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf +#define W25Q128JV_SQ {\ + .total_size = (1 << 24), /* 16 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x40, \ + .capacity = 0x18, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Macronix MX25R6435F 8MiB SPI flash. +// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf +// By default its in lower power mode which can only do 8mhz. In high power mode it can do 80mhz. +#define MX25R6435F {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xc2, \ + .memory_type = 0x28, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 8, \ + .quad_enable_bit_mask = 0x40, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = true, \ +} + +// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf +#define W25Q128JV_PM {\ + .total_size = (1 << 24), /* 16 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x18, \ + .max_clock_speed_mhz = 133, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ +} + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H diff --git a/supervisor/shared/external_flash/external_flash.c b/supervisor/shared/external_flash/external_flash.c new file mode 100644 index 000000000..defdfa393 --- /dev/null +++ b/supervisor/shared/external_flash/external_flash.c @@ -0,0 +1,615 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "external_flash.h" + +#include +#include + +#include "supervisor/spi_flash_api.h" +#include "supervisor/shared/external_flash/common_commands.h" +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "py/misc.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "lib/oofatfs/ff.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "supervisor/memory.h" +#include "supervisor/shared/rgb_led_status.h" + +#define SPI_FLASH_PART1_START_BLOCK (0x1) + +#define NO_SECTOR_LOADED 0xFFFFFFFF + +// The currently cached sector in the cache, ram or flash based. +static uint32_t current_sector; + +const external_flash_device possible_devices[EXTERNAL_FLASH_DEVICE_COUNT] = {EXTERNAL_FLASH_DEVICES}; + +static const external_flash_device* flash_device = NULL; + +// Track which blocks (up to 32) in the current sector currently live in the +// cache. +static uint32_t dirty_mask; + +static supervisor_allocation* supervisor_cache = NULL; + +// Wait until both the write enable and write in progress bits have cleared. +static bool wait_for_flash_ready(void) { + uint8_t read_status_response[1] = {0x00}; + bool ok = true; + // Both the write enable and write in progress bits should be low. + do { + ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); + } while (ok && (read_status_response[0] & 0x3) != 0); + return ok; +} + +// Turn on the write enable bit so we can program and erase the flash. +static bool write_enable(void) { + return spi_flash_command(CMD_ENABLE_WRITE); +} + +// Read data_length's worth of bytes starting at address into data. +static bool read_flash(uint32_t address, uint8_t* data, uint32_t data_length) { + if (flash_device == NULL) { + return false; + } + if (!wait_for_flash_ready()) { + return false; + } + return spi_flash_read_data(address, data, data_length); +} + +// Writes data_length's worth of bytes starting at address from data. Assumes +// that the sector that address resides in has already been erased. So make sure +// to run erase_sector. +static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_length) { + if (flash_device == NULL) { + return false; + } + // Don't bother writing if the data is all 1s. Thats equivalent to the flash + // state after an erase. + bool all_ones = true; + for (uint16_t i = 0; i < data_length; i++) { + if (data[i] != 0xff) { + all_ones = false; + break; + } + } + if (all_ones) { + return true; + } + + for (uint32_t bytes_written = 0; + bytes_written < data_length; + bytes_written += SPI_FLASH_PAGE_SIZE) { + if (!wait_for_flash_ready() || !write_enable()) { + return false; + } + + if (!spi_flash_write_data(address + bytes_written, (uint8_t*) data + bytes_written, + SPI_FLASH_PAGE_SIZE)) { + return false; + } + } + return true; +} + +static bool page_erased(uint32_t sector_address) { + // Check the first few bytes to catch the common case where there is data + // without using a bunch of memory. + uint8_t short_buffer[4]; + if (read_flash(sector_address, short_buffer, 4)) { + for (uint16_t i = 0; i < 4; i++) { + if (short_buffer[i] != 0xff) { + return false; + } + } + } else { + return false; + } + + // Now check the full length. + uint8_t full_buffer[FILESYSTEM_BLOCK_SIZE]; + if (read_flash(sector_address, full_buffer, FILESYSTEM_BLOCK_SIZE)) { + for (uint16_t i = 0; i < FILESYSTEM_BLOCK_SIZE; i++) { + if (short_buffer[i] != 0xff) { + return false; + } + } + } else { + return false; + } + return true; +} + +// Erases the given sector. Make sure you copied all of the data out of it you +// need! Also note, sector_address is really 24 bits. +static bool erase_sector(uint32_t sector_address) { + // Before we erase the sector we need to wait for any writes to finish and + // and then enable the write again. + if (!wait_for_flash_ready() || !write_enable()) { + return false; + } + + spi_flash_sector_command(CMD_SECTOR_ERASE, sector_address); + return true; +} + +// Sector is really 24 bits. +static bool copy_block(uint32_t src_address, uint32_t dest_address) { + // Copy page by page to minimize RAM buffer. + uint16_t page_size = SPI_FLASH_PAGE_SIZE; + uint8_t buffer[page_size]; + for (uint32_t i = 0; i < FILESYSTEM_BLOCK_SIZE / page_size; i++) { + if (!read_flash(src_address + i * page_size, buffer, page_size)) { + return false; + } + if (!write_flash(dest_address + i * page_size, buffer, page_size)) { + return false; + } + } + return true; +} + +void supervisor_flash_init(void) { + if (flash_device != NULL) { + return; + } + + // Delay to give the SPI Flash time to get going. + // TODO(tannewt): Only do this when we know power was applied vs a reset. + uint16_t max_start_up_delay_us = 0; + for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { + if (possible_devices[i].start_up_time_us > max_start_up_delay_us) { + max_start_up_delay_us = possible_devices[i].start_up_time_us; + } + } + common_hal_mcu_delay_us(max_start_up_delay_us); + + spi_flash_init(); + + // The response will be 0xff if the flash needs more time to start up. + uint8_t jedec_id_response[3] = {0xff, 0xff, 0xff}; + while (jedec_id_response[0] == 0xff) { + spi_flash_read_command(CMD_READ_JEDEC_ID, jedec_id_response, 3); + } + + for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { + const external_flash_device* possible_device = &possible_devices[i]; + if (jedec_id_response[0] == possible_device->manufacturer_id && + jedec_id_response[1] == possible_device->memory_type && + jedec_id_response[2] == possible_device->capacity) { + flash_device = possible_device; + break; + } + } + + if (flash_device == NULL) { + return; + } + + // We don't know what state the flash is in so wait for any remaining writes and then reset. + uint8_t read_status_response[1] = {0x00}; + // The write in progress bit should be low. + do { + spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); + } while ((read_status_response[0] & 0x1) != 0); + // The suspended write/erase bit should be low. + do { + spi_flash_read_command(CMD_READ_STATUS2, read_status_response, 1); + } while ((read_status_response[0] & 0x80) != 0); + + + spi_flash_command(CMD_ENABLE_RESET); + spi_flash_command(CMD_RESET); + + // Wait 30us for the reset + common_hal_mcu_delay_us(30); + + spi_flash_init_device(flash_device); + + // Activity LED for flash writes. +#ifdef MICROPY_HW_LED_MSC + gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF); + gpio_set_pin_direction(MICROPY_HW_LED_MSC, GPIO_DIRECTION_OUT); + // There's already a pull-up on the board. + gpio_set_pin_level(MICROPY_HW_LED_MSC, false); +#endif + + if (flash_device->has_sector_protection) { + write_enable(); + + // Turn off sector protection + uint8_t data[1] = {0x00}; + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, data, 1); + } + + // Turn off writes in case this is a microcontroller only reset. + spi_flash_command(CMD_DISABLE_WRITE); + + wait_for_flash_ready(); + + current_sector = NO_SECTOR_LOADED; + dirty_mask = 0; + MP_STATE_VM(flash_ram_cache) = NULL; +} + +// The size of each individual block. +uint32_t supervisor_flash_get_block_size(void) { + return FILESYSTEM_BLOCK_SIZE; +} + +// The total number of available blocks. +uint32_t supervisor_flash_get_block_count(void) { + // We subtract one erase sector size because we may use it as a staging area + // for writes. + return SPI_FLASH_PART1_START_BLOCK + (flash_device->total_size - SPI_FLASH_ERASE_SIZE) / FILESYSTEM_BLOCK_SIZE; +} + +// Flush the cache that was written to the scratch portion of flash. Only used +// when ram is tight. +static bool flush_scratch_flash(void) { + // First, copy out any blocks that we haven't touched from the sector we've + // cached. + bool copy_to_scratch_ok = true; + uint32_t scratch_sector = flash_device->total_size - SPI_FLASH_ERASE_SIZE; + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + if ((dirty_mask & (1 << i)) == 0) { + copy_to_scratch_ok = copy_to_scratch_ok && + copy_block(current_sector + i * FILESYSTEM_BLOCK_SIZE, + scratch_sector + i * FILESYSTEM_BLOCK_SIZE); + } + } + if (!copy_to_scratch_ok) { + // TODO(tannewt): Do more here. We opted to not erase and copy bad data + // in. We still risk losing the data written to the scratch sector. + return false; + } + // Second, erase the current sector. + erase_sector(current_sector); + // Finally, copy the new version into it. + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + copy_block(scratch_sector + i * FILESYSTEM_BLOCK_SIZE, + current_sector + i * FILESYSTEM_BLOCK_SIZE); + } + return true; +} + +// Attempts to allocate a new set of page buffers for caching a full sector in +// ram. Each page is allocated separately so that the GC doesn't need to provide +// one huge block. We can free it as we write if we want to also. +static bool allocate_ram_cache(void) { + uint8_t blocks_per_sector = SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + + uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t); + // Attempt to allocate outside the heap first. + supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false); + if (supervisor_cache != NULL) { + MP_STATE_VM(flash_ram_cache) = (uint8_t **) supervisor_cache->ptr; + uint8_t* page_start = (uint8_t *) supervisor_cache->ptr + table_size; + + for (uint8_t i = 0; i < blocks_per_sector; i++) { + for (uint8_t j = 0; j < pages_per_block; j++) { + uint32_t offset = i * pages_per_block + j; + MP_STATE_VM(flash_ram_cache)[offset] = page_start + offset * SPI_FLASH_PAGE_SIZE; + } + } + return true; + } + + MP_STATE_VM(flash_ram_cache) = m_malloc_maybe(blocks_per_sector * pages_per_block * sizeof(uint32_t), false); + if (MP_STATE_VM(flash_ram_cache) == NULL) { + return false; + } + // Declare i and j outside the loops in case we fail to allocate everything + // we need. In that case we'll give it back. + uint8_t i = 0; + uint8_t j = 0; + bool success = true; + for (i = 0; i < blocks_per_sector; i++) { + for (j = 0; j < pages_per_block; j++) { + uint8_t *page_cache = m_malloc_maybe(SPI_FLASH_PAGE_SIZE, false); + if (page_cache == NULL) { + success = false; + break; + } + MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j] = page_cache; + } + if (!success) { + break; + } + } + // We couldn't allocate enough so give back what we got. + if (!success) { + // We add 1 so that we delete 0 when i is 1. Going to zero (i >= 0) + // would never stop because i is unsigned. + i++; + for (; i > 0; i--) { + for (; j > 0; j--) { + m_free(MP_STATE_VM(flash_ram_cache)[(i - 1) * pages_per_block + (j - 1)]); + } + j = pages_per_block; + } + m_free(MP_STATE_VM(flash_ram_cache)); + MP_STATE_VM(flash_ram_cache) = NULL; + } + return success; +} + +// Flush the cached sector from ram onto the flash. We'll free the cache unless +// keep_cache is true. +static bool flush_ram_cache(bool keep_cache) { + // First, copy out any blocks that we haven't touched from the sector + // we've cached. If we don't do this we'll erase the data during the sector + // erase below. + bool copy_to_ram_ok = true; + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + if ((dirty_mask & (1 << i)) == 0) { + for (uint8_t j = 0; j < pages_per_block; j++) { + copy_to_ram_ok = read_flash( + current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE, + MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j], + SPI_FLASH_PAGE_SIZE); + if (!copy_to_ram_ok) { + break; + } + } + } + if (!copy_to_ram_ok) { + break; + } + } + + if (!copy_to_ram_ok) { + return false; + } + // Second, erase the current sector. + erase_sector(current_sector); + // Lastly, write all the data in ram that we've cached. + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + for (uint8_t j = 0; j < pages_per_block; j++) { + write_flash(current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE, + MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j], + SPI_FLASH_PAGE_SIZE); + if (!keep_cache && supervisor_cache == NULL) { + m_free(MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j]); + } + } + } + // We're done with the cache for now so give it back. + if (!keep_cache) { + if (supervisor_cache != NULL) { + free_memory(supervisor_cache); + supervisor_cache = NULL; + } else { + m_free(MP_STATE_VM(flash_ram_cache)); + } + MP_STATE_VM(flash_ram_cache) = NULL; + } + return true; +} + +// Delegates to the correct flash flush method depending on the existing cache. +static void spi_flash_flush_keep_cache(bool keep_cache) { + if (current_sector == NO_SECTOR_LOADED) { + return; + } + #ifdef MICROPY_HW_LED_MSC + port_pin_set_output_level(MICROPY_HW_LED_MSC, true); + #endif + temp_status_color(ACTIVE_WRITE); + // If we've cached to the flash itself flush from there. + if (MP_STATE_VM(flash_ram_cache) == NULL) { + flush_scratch_flash(); + } else { + flush_ram_cache(keep_cache); + } + current_sector = NO_SECTOR_LOADED; + clear_temp_status(); + #ifdef MICROPY_HW_LED_MSC + port_pin_set_output_level(MICROPY_HW_LED_MSC, false); + #endif +} + +// External flash function used. If called externally we assume we won't need +// the cache after. +void supervisor_flash_flush(void) { + spi_flash_flush_keep_cache(false); +} + +// Builds a partition entry for the MBR. +static void build_partition(uint8_t *buf, int boot, int type, + uint32_t start_block, uint32_t num_blocks) { + buf[0] = boot; + + if (num_blocks == 0) { + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + } else { + buf[1] = 0xff; + buf[2] = 0xff; + buf[3] = 0xff; + } + + buf[4] = type; + + if (num_blocks == 0) { + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + } else { + buf[5] = 0xff; + buf[6] = 0xff; + buf[7] = 0xff; + } + + buf[8] = start_block; + buf[9] = start_block >> 8; + buf[10] = start_block >> 16; + buf[11] = start_block >> 24; + + buf[12] = num_blocks; + buf[13] = num_blocks >> 8; + buf[14] = num_blocks >> 16; + buf[15] = num_blocks >> 24; +} + +static int32_t convert_block_to_flash_addr(uint32_t block) { + if (SPI_FLASH_PART1_START_BLOCK <= block && block < supervisor_flash_get_block_count()) { + // a block in partition 1 + block -= SPI_FLASH_PART1_START_BLOCK; + return block * FILESYSTEM_BLOCK_SIZE; + } + // bad block + return -1; +} + +bool external_flash_read_block(uint8_t *dest, uint32_t block) { + if (block == 0) { + // Fake the MBR so we can decide on our own partition table + for (int i = 0; i < 446; i++) { + dest[i] = 0; + } + + build_partition(dest + 446, 0, 0x01 /* FAT12 */, + SPI_FLASH_PART1_START_BLOCK, + supervisor_flash_get_block_count() - SPI_FLASH_PART1_START_BLOCK); + build_partition(dest + 462, 0, 0, 0, 0); + build_partition(dest + 478, 0, 0, 0, 0); + build_partition(dest + 494, 0, 0, 0, 0); + + dest[510] = 0x55; + dest[511] = 0xaa; + + return true; + } else if (block < SPI_FLASH_PART1_START_BLOCK) { + memset(dest, 0, FILESYSTEM_BLOCK_SIZE); + return true; + } else { + // Non-MBR block, get data from flash memory. + int32_t address = convert_block_to_flash_addr(block); + if (address == -1) { + // bad block number + return false; + } + + // Mask out the lower bits that designate the address within the sector. + uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1)); + uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE); + uint8_t mask = 1 << (block_index); + // We're reading from the currently cached sector. + if (current_sector == this_sector && (mask & dirty_mask) > 0) { + if (MP_STATE_VM(flash_ram_cache) != NULL) { + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + for (int i = 0; i < pages_per_block; i++) { + memcpy(dest + i * SPI_FLASH_PAGE_SIZE, + MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i], + SPI_FLASH_PAGE_SIZE); + } + return true; + } else { + uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE; + return read_flash(scratch_address, dest, FILESYSTEM_BLOCK_SIZE); + } + } + return read_flash(address, dest, FILESYSTEM_BLOCK_SIZE); + } +} + +bool external_flash_write_block(const uint8_t *data, uint32_t block) { + if (block < SPI_FLASH_PART1_START_BLOCK) { + // Fake writing below the flash partition. + return true; + } else { + // Non-MBR block, copy to cache + int32_t address = convert_block_to_flash_addr(block); + if (address == -1) { + // bad block number + return false; + } + // Wait for any previous writes to finish. + wait_for_flash_ready(); + // Mask out the lower bits that designate the address within the sector. + uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1)); + uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE); + uint8_t mask = 1 << (block_index); + // Flush the cache if we're moving onto a sector or we're writing the + // same block again. + if (current_sector != this_sector || (mask & dirty_mask) > 0) { + // Check to see if we'd write to an erased page. In that case we + // can write directly. + if (page_erased(address)) { + return write_flash(address, data, FILESYSTEM_BLOCK_SIZE); + } + if (current_sector != NO_SECTOR_LOADED) { + spi_flash_flush_keep_cache(true); + } + if (MP_STATE_VM(flash_ram_cache) == NULL && !allocate_ram_cache()) { + erase_sector(flash_device->total_size - SPI_FLASH_ERASE_SIZE); + wait_for_flash_ready(); + } + current_sector = this_sector; + dirty_mask = 0; + } + dirty_mask |= mask; + // Copy the block to the appropriate cache. + if (MP_STATE_VM(flash_ram_cache) != NULL) { + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + for (int i = 0; i < pages_per_block; i++) { + memcpy(MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i], + data + i * SPI_FLASH_PAGE_SIZE, + SPI_FLASH_PAGE_SIZE); + } + return true; + } else { + uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE; + return write_flash(scratch_address, data, FILESYSTEM_BLOCK_SIZE); + } + } +} + +mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + if (!external_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} + +mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + if (!external_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} diff --git a/supervisor/shared/external_flash/external_flash.h b/supervisor/shared/external_flash/external_flash.h new file mode 100644 index 000000000..852d183a7 --- /dev/null +++ b/supervisor/shared/external_flash/external_flash.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H + +#include +#include + +#include "py/mpconfig.h" + +// Erase sector size. +#define SPI_FLASH_SECTOR_SIZE (0x1000 - 100) + +// These are common across all NOR Flash. +#define SPI_FLASH_ERASE_SIZE (1 << 12) +#define SPI_FLASH_PAGE_SIZE (256) + +#define SPI_FLASH_SYSTICK_MASK (0x1ff) // 512ms +#define SPI_FLASH_IDLE_TICK(tick) (((tick) & SPI_FLASH_SYSTICK_MASK) == 2) + +#ifndef SPI_FLASH_MAX_BAUDRATE +#define SPI_FLASH_MAX_BAUDRATE 8000000 +#endif + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H diff --git a/supervisor/shared/external_flash/external_flash_root_pointers.h b/supervisor/shared/external_flash/external_flash_root_pointers.h new file mode 100644 index 000000000..cb1b86d19 --- /dev/null +++ b/supervisor/shared/external_flash/external_flash_root_pointers.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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. + */ +#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H + +#include + +// We use this when we can allocate the whole cache in RAM. +#define FLASH_ROOT_POINTERS \ + uint8_t** flash_ram_cache; \ + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H diff --git a/supervisor/shared/external_flash/qspi_flash.c b/supervisor/shared/external_flash/qspi_flash.c new file mode 100644 index 000000000..48266540c --- /dev/null +++ b/supervisor/shared/external_flash/qspi_flash.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017, 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/spi_flash_api.h" + +#include "supervisor/shared/external_flash/common_commands.h" + +void check_quad_enable(const external_flash_device* device) { + if (device->quad_enable_bit_mask == 0x00) { + return; + } + + // Verify that QSPI mode is enabled. + uint8_t status; + if (device->single_status_byte) { + spi_flash_read_command(CMD_READ_STATUS, &status, 1); + } else { + spi_flash_read_command(CMD_READ_STATUS2, &status, 1); + } + + // Check the quad enable bit. + if ((status & device->quad_enable_bit_mask) == 0) { + uint8_t full_status[2] = {0x00, device->quad_enable_bit_mask}; + spi_flash_command(CMD_ENABLE_WRITE); + if (device->write_status_register_split) { + spi_flash_write_command(CMD_WRITE_STATUS_BYTE2, full_status + 1, 1); + } else if (device->single_status_byte) { + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, full_status + 1, 1); + } else { + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, full_status, 2); + } + } +} diff --git a/supervisor/shared/external_flash/qspi_flash.h b/supervisor/shared/external_flash/qspi_flash.h new file mode 100644 index 000000000..b72e37b26 --- /dev/null +++ b/supervisor/shared/external_flash/qspi_flash.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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. + */ +#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H + +void check_quad_enable(const external_flash_device* device); + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H diff --git a/supervisor/shared/external_flash/spi_flash.c b/supervisor/shared/external_flash/spi_flash.c new file mode 100644 index 000000000..ec7101bc9 --- /dev/null +++ b/supervisor/shared/external_flash/spi_flash.c @@ -0,0 +1,150 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/spi_flash_api.h" + +#include +#include + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "supervisor/shared/external_flash/common_commands.h" +#include "supervisor/shared/external_flash/external_flash.h" +#include "py/mpconfig.h" + +digitalio_digitalinout_obj_t cs_pin; +busio_spi_obj_t spi; + +const external_flash_device* flash_device; +uint32_t spi_flash_baudrate; + +// Enable the flash over SPI. +static void flash_enable(void) { + while (!common_hal_busio_spi_try_lock(&spi)) {} + common_hal_digitalio_digitalinout_set_value(&cs_pin, false); +} + +// Disable the flash over SPI. +static void flash_disable(void) { + common_hal_digitalio_digitalinout_set_value(&cs_pin, true); + common_hal_busio_spi_unlock(&spi); +} + +static bool transfer(uint8_t* command, uint32_t command_length, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { + flash_enable(); + bool status = common_hal_busio_spi_write(&spi, command, command_length); + if (status) { + if (data_in != NULL && data_out != NULL) { + status = common_hal_busio_spi_transfer(&spi, data_out, data_in, data_length); + } else if (data_out != NULL) { + status = common_hal_busio_spi_read(&spi, data_out, data_length, 0xff); + } else if (data_in != NULL) { + status = common_hal_busio_spi_write(&spi, data_in, data_length); + } + } + flash_disable(); + return status; +} + +static bool transfer_command(uint8_t command, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { + return transfer(&command, 1, data_in, data_out, data_length); +} + +bool spi_flash_command(uint8_t command) { + return transfer_command(command, NULL, NULL, 0); +} + +bool spi_flash_read_command(uint8_t command, uint8_t* data, uint32_t data_length) { + return transfer_command(command, NULL, data, data_length); +} + +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t data_length) { + return transfer_command(command, data, NULL, data_length); +} + +// Pack the low 24 bits of the address into a uint8_t array. +static void address_to_bytes(uint32_t address, uint8_t* bytes) { + bytes[0] = (address >> 16) & 0xff; + bytes[1] = (address >> 8) & 0xff; + bytes[2] = address & 0xff; +} + +bool spi_flash_sector_command(uint8_t command, uint32_t address) { + uint8_t request[4] = {command, 0x00, 0x00, 0x00}; + address_to_bytes(address, request + 1); + return transfer(request, 4, NULL, NULL, 0); +} + +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length) { + uint8_t request[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00}; + // Write the SPI flash write address into the bytes following the command byte. + address_to_bytes(address, request + 1); + flash_enable(); + common_hal_busio_spi_configure(&spi, spi_flash_baudrate, 0, 0, 8); + bool status = common_hal_busio_spi_write(&spi, request, 4); + if (status) { + status = common_hal_busio_spi_write(&spi, data, data_length); + } + flash_disable(); + return status; +} + +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length) { + uint8_t request[5] = {CMD_READ_DATA, 0x00, 0x00, 0x00}; + uint8_t command_length = 4; + if (flash_device->supports_fast_read) { + request[0] = CMD_FAST_READ_DATA; + command_length = 5; + } + // Write the SPI flash write address into the bytes following the command byte. + address_to_bytes(address, request + 1); + flash_enable(); + common_hal_busio_spi_configure(&spi, spi_flash_baudrate, 0, 0, 8); + bool status = common_hal_busio_spi_write(&spi, request, command_length); + if (status) { + status = common_hal_busio_spi_read(&spi, data, data_length, 0xff); + } + flash_disable(); + return status; +} + +void spi_flash_init(void) { + common_hal_digitalio_digitalinout_construct(&cs_pin, SPI_FLASH_CS_PIN); + + // Set CS high (disabled). + common_hal_digitalio_digitalinout_switch_to_output(&cs_pin, true, DRIVE_MODE_PUSH_PULL); + + common_hal_busio_spi_construct(&spi, SPI_FLASH_SCK_PIN, SPI_FLASH_MOSI_PIN, SPI_FLASH_MISO_PIN); + common_hal_busio_spi_never_reset(&spi); +} + +void spi_flash_init_device(const external_flash_device* device) { + flash_device = device; + spi_flash_baudrate = device->max_clock_speed_mhz * 1000000; + if (spi_flash_baudrate > SPI_FLASH_MAX_BAUDRATE) { + spi_flash_baudrate = SPI_FLASH_MAX_BAUDRATE; + } +} diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c new file mode 100644 index 000000000..264ba25f0 --- /dev/null +++ b/supervisor/shared/filesystem.c @@ -0,0 +1,109 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "extmod/vfs_fat.h" +#include "lib/oofatfs/ff.h" +#include "lib/oofatfs/diskio.h" + +#include "py/mpstate.h" + +#include "supervisor/flash.h" + +static mp_vfs_mount_t _mp_vfs; +static fs_user_mount_t _internal_vfs; + +static void make_empty_file(FATFS *fatfs, const char *path) { + FIL fp; + f_open(fatfs, &fp, path, FA_WRITE | FA_CREATE_ALWAYS); + f_close(&fp); +} + +// we don't make this function static because it needs a lot of stack and we +// want it to be executed without using stack within main() function +void filesystem_init(bool create_allowed, bool force_create) { + // init the vfs object + fs_user_mount_t *vfs_fat = &_internal_vfs; + vfs_fat->flags = 0; + supervisor_flash_init_vfs(vfs_fat); + + // try to mount the flash + FRESULT res = f_mount(&vfs_fat->fatfs); + + if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) { + // No filesystem so create a fresh one, or reformat has been requested. + uint8_t working_buf[_MAX_SS]; + res = f_mkfs(&vfs_fat->fatfs, FM_FAT, 0, working_buf, sizeof(working_buf)); + // Flush the new file system to make sure it's repaired immediately. + supervisor_flash_flush(); + if (res != FR_OK) { + asm("bkpt"); + return; + } + + // set label + f_setlabel(&vfs_fat->fatfs, "CIRCUITPY"); + + // inhibit file indexing on MacOS + f_mkdir(&vfs_fat->fatfs, "/.fseventsd"); + make_empty_file(&vfs_fat->fatfs, "/.metadata_never_index"); + make_empty_file(&vfs_fat->fatfs, "/.Trashes"); + make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log"); + + // and ensure everything is flushed + supervisor_flash_flush(); + } else if (res != FR_OK) { + asm("bkpt"); + return; + } + mp_vfs_mount_t *vfs = &_mp_vfs; + vfs->str = "/"; + vfs->len = 1; + vfs->obj = MP_OBJ_FROM_PTR(vfs_fat); + vfs->next = NULL; + MP_STATE_VM(vfs_mount_table) = vfs; + + // The current directory is used as the boot up directory. + // It is set to the internal flash filesystem by default. + MP_STATE_PORT(vfs_cur) = vfs; +} + +void filesystem_flush(void) { + supervisor_flash_flush(); +} + +void filesystem_writable_by_python(bool writable) { + fs_user_mount_t *vfs = &_internal_vfs; + + if (writable) { + vfs->flags |= FSUSER_USB_WRITABLE; + } else { + vfs->flags &= ~FSUSER_USB_WRITABLE; + } +} + +bool filesystem_present(void) { + return true; +} diff --git a/supervisor/shared/flash.c b/supervisor/shared/flash.c new file mode 100644 index 000000000..4fa2d8f75 --- /dev/null +++ b/supervisor/shared/flash.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft + * + * 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 "supervisor/flash.h" + +#include "extmod/vfs_fat.h" +#include "py/runtime.h" +#include "lib/oofatfs/ff.h" + +#define VFS_INDEX 0 + +void supervisor_flash_set_usb_writable(bool usb_writable) { + mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table); + for (uint8_t i = 0; current_mount != NULL; i++) { + if (i == VFS_INDEX) { + break; + } + current_mount = current_mount->next; + } + if (current_mount == NULL) { + return; + } + fs_user_mount_t *vfs = (fs_user_mount_t *) current_mount->obj; + + if (usb_writable) { + vfs->flags |= FSUSER_USB_WRITABLE; + } else { + vfs->flags &= ~FSUSER_USB_WRITABLE; + } +} + +// there is a singleton Flash object +const mp_obj_type_t supervisor_flash_type; +STATIC const mp_obj_base_t supervisor_flash_obj = {&supervisor_flash_type}; + +STATIC mp_obj_t supervisor_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // return singleton object + return (mp_obj_t)&supervisor_flash_obj; +} + +STATIC mp_obj_t supervisor_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); + mp_uint_t ret = supervisor_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); + return MP_OBJ_NEW_SMALL_INT(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_readblocks_obj, supervisor_flash_obj_readblocks); + +STATIC mp_obj_t supervisor_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); + mp_uint_t ret = supervisor_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); + return MP_OBJ_NEW_SMALL_INT(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_writeblocks_obj, supervisor_flash_obj_writeblocks); + +STATIC mp_obj_t supervisor_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { + mp_int_t cmd = mp_obj_get_int(cmd_in); + switch (cmd) { + case BP_IOCTL_INIT: supervisor_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_DEINIT: supervisor_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly + case BP_IOCTL_SYNC: supervisor_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(supervisor_flash_get_block_count()); + case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(supervisor_flash_get_block_size()); + default: return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_ioctl_obj, supervisor_flash_obj_ioctl); + +STATIC const mp_rom_map_elem_t supervisor_flash_obj_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&supervisor_flash_obj_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&supervisor_flash_obj_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&supervisor_flash_obj_ioctl_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(supervisor_flash_obj_locals_dict, supervisor_flash_obj_locals_dict_table); + +const mp_obj_type_t supervisor_flash_type = { + { &mp_type_type }, + .name = MP_QSTR_Flash, + .make_new = supervisor_flash_obj_make_new, + .locals_dict = (mp_obj_t)&supervisor_flash_obj_locals_dict, +}; + +void supervisor_flash_init_vfs(fs_user_mount_t *vfs) { + vfs->base.type = &mp_fat_vfs_type; + vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; + vfs->fatfs.drv = vfs; + vfs->fatfs.part = 1; // flash filesystem lives on first partition + vfs->readblocks[0] = (mp_obj_t)&supervisor_flash_obj_readblocks_obj; + vfs->readblocks[1] = (mp_obj_t)&supervisor_flash_obj; + vfs->readblocks[2] = (mp_obj_t)supervisor_flash_read_blocks; // native version + vfs->writeblocks[0] = (mp_obj_t)&supervisor_flash_obj_writeblocks_obj; + vfs->writeblocks[1] = (mp_obj_t)&supervisor_flash_obj; + vfs->writeblocks[2] = (mp_obj_t)supervisor_flash_write_blocks; // native version + vfs->u.ioctl[0] = (mp_obj_t)&supervisor_flash_obj_ioctl_obj; + vfs->u.ioctl[1] = (mp_obj_t)&supervisor_flash_obj; +} diff --git a/supervisor/shared/micropython.c b/supervisor/shared/micropython.c new file mode 100644 index 000000000..245db11d4 --- /dev/null +++ b/supervisor/shared/micropython.c @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 + +#include "supervisor/serial.h" +#include "lib/oofatfs/ff.h" +#include "py/mpconfig.h" + +#include "supervisor/shared/status_leds.h" + +int mp_hal_stdin_rx_chr(void) { + for (;;) { + #ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP + #endif + if (serial_bytes_available()) { + toggle_rx_led(); + return serial_read(); + } + } +} + +void mp_hal_stdout_tx_strn(const char *str, size_t len) { + toggle_tx_led(); + + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + if (boot_output_file != NULL) { + UINT bytes_written = 0; + f_write(boot_output_file, str, len, &bytes_written); + } + #endif + + serial_write_substring(str, len); +} diff --git a/supervisor/shared/rgb_led_status.h b/supervisor/shared/rgb_led_status.h index 24e071aac..9542b3197 100644 --- a/supervisor/shared/rgb_led_status.h +++ b/supervisor/shared/rgb_led_status.h @@ -33,7 +33,7 @@ #include "lib/utils/pyexec.h" #include "supervisor/port.h" -#include "mpconfigport.h" +#include "py/mpconfig.h" #include "rgb_led_colors.h" // Overall, the time module must be implemented. diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c new file mode 100644 index 000000000..058c4a7a4 --- /dev/null +++ b/supervisor/shared/serial.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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 + +#include "supervisor/serial.h" +#include "supervisor/usb.h" + +#include "tusb.h" + +void serial_init(void) { + usb_init(); +} + +bool serial_connected(void) { + return tud_cdc_connected(); +} + +char serial_read(void) { + return (char) tud_cdc_read_char(); +} + +bool serial_bytes_available(void) { + return tud_cdc_available() > 0; +} + +void serial_write(const char* text) { + tud_cdc_write(text, strlen(text)); +} + +void serial_write_substring(const char* text, uint32_t length) { + tud_cdc_write(text, length); +} diff --git a/supervisor/shared/status_leds.c b/supervisor/shared/status_leds.c new file mode 100644 index 000000000..d99853ceb --- /dev/null +++ b/supervisor/shared/status_leds.c @@ -0,0 +1,62 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/shared/status_leds.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + +#ifdef MICROPY_HW_LED_RX +digitalio_digitalinout_obj_t rx_led; +#endif + +#ifdef MICROPY_HW_LED_TX +digitalio_digitalinout_obj_t tx_led; +#endif + +void init_status_leds(void) { + #ifdef MICROPY_HW_LED_RX + common_hal_digitalio_digitalinout_construct(&rx_led, MICROPY_HW_LED_RX); + common_hal_digitalio_digitalinout_switch_to_output(&rx_led, true, DRIVE_MODE_PUSH_PULL); + #endif + #ifdef MICROPY_HW_LED_TX + common_hal_digitalio_digitalinout_construct(&tx_led, MICROPY_HW_LED_TX); + common_hal_digitalio_digitalinout_switch_to_output(&tx_led, true, DRIVE_MODE_PUSH_PULL); + #endif +} + +void toggle_rx_led(void) { + #ifdef MICROPY_HW_LED_RX + common_hal_digitalio_digitalinout_set_value(&rx_led, !common_hal_digitalio_digitalinout_get_value(&rx_led)); + #endif +} + + +void toggle_tx_led(void) { + #ifdef MICROPY_HW_LED_TX + common_hal_digitalio_digitalinout_set_value(&tx_led, !common_hal_digitalio_digitalinout_get_value(&tx_led)); + #endif +} diff --git a/supervisor/shared/status_leds.h b/supervisor/shared/status_leds.h new file mode 100644 index 000000000..30132753f --- /dev/null +++ b/supervisor/shared/status_leds.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SUPERVISOR_STATUS_LEDS_H +#define MICROPY_INCLUDED_SUPERVISOR_STATUS_LEDS_H + +void init_status_leds(void); + +void toggle_rx_led(void); + +void toggle_tx_led(void); + +#endif // MICROPY_INCLUDED_SUPERVISOR_STATUS_LEDS_H diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h new file mode 100644 index 000000000..4693bfced --- /dev/null +++ b/supervisor/shared/usb/tusb_config.h @@ -0,0 +1,115 @@ +/**************************************************************************/ +/*! + @file tusb_config.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2013, hathach (tinyusb.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ +/**************************************************************************/ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#include "genhdr/autogen_usb_descriptor.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// COMMON CONFIGURATION +//--------------------------------------------------------------------+ +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE + +#define CFG_TUSB_DEBUG 0 + +/*------------- RTOS -------------*/ +#define CFG_TUSB_OS OPT_OS_NONE +//#define CFG_TUD_TASK_QUEUE_SZ 16 +//#define CFG_TUD_TASK_PRIO 0 +//#define CFG_TUD_TASK_STACK_SZ 150 + +//--------------------------------------------------------------------+ +// DEVICE CONFIGURATION +//--------------------------------------------------------------------+ + +#define CFG_TUD_ENDOINT0_SIZE 64 + +/*------------- Descriptors -------------*/ +/* Enable auto generated descriptor, tinyusb will try its best to create + * descriptor ( device, configuration, hid ) that matches enabled CFG_* in this file + * + * Note: All CFG_TUD_DESC_* are relevant only if CFG_TUD_DESC_AUTO is enabled + */ +#define CFG_TUD_DESC_AUTO 0 + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 1 +#define CFG_TUD_HID 1 + +/*------------------------------------------------------------------*/ +/* CLASS DRIVER + *------------------------------------------------------------------*/ + +/* TX is sent automatically on every Start of Frame event ~ 1ms. + * If not enabled, application must call tud_cdc_flush() periodically + * Note: Enabled this could overflow device task, if it does, define + * CFG_TUD_TASK_QUEUE_SZ with large value + */ +#define CFG_TUD_CDC_FLUSH_ON_SOF 0 + + +/*------------- MSC -------------*/ +// Number of supported Logical Unit Number (At least 1) +#define CFG_TUD_MSC_MAXLUN 1 + +// Number of Blocks +#define CFG_TUD_MSC_BLOCK_NUM (256*1024)/512 + + + +// Product revision string included in Inquiry response, max 4 bytes +#define CFG_TUD_MSC_PRODUCT_REV "1.0" + + +//--------------------------------------------------------------------+ +// USB RAM PLACEMENT +//--------------------------------------------------------------------+ +#define CFG_TUSB_ATTR_USBRAM +#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4) + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c new file mode 100644 index 000000000..1aa34e9e6 --- /dev/null +++ b/supervisor/shared/usb/usb.c @@ -0,0 +1,140 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "tick.h" +#include "shared-bindings/microcontroller/Processor.h" +#include "supervisor/port.h" +#include "supervisor/usb.h" +#include "lib/utils/interrupt_char.h" +#include "lib/mp-readline/readline.h" + +#include "tusb.h" + +// Serial number as hex characters. This writes directly to the USB +// descriptor. +extern uint16_t usb_serial_number[1 + COMMON_HAL_MCU_PROCESSOR_UID_LENGTH * 2]; + +void load_serial_number(void) { + // create serial number based on device unique id + uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; + common_hal_mcu_processor_get_uid(raw_id); + + const char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F'}; + for (int i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) { + for (int j = 0; j < 2; j++) { + uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf; + // Strings are UTF-16-LE encoded. + usb_serial_number[1 + i * 2 + j] = nibble_to_hex[nibble]; + } + } +} + +bool _usb_enabled = false; + +bool usb_enabled(void) { + return _usb_enabled; +} + +void usb_init(void) { + init_usb_hardware(); + load_serial_number(); + + tusb_init(); + _usb_enabled = true; + +#if MICROPY_KBD_EXCEPTION + // Set Ctrl+C as wanted char, tud_cdc_rx_wanted_cb() callback will be invoked when Ctrl+C is received + // This callback always got invoked regardless of mp_interrupt_char value since we only set it once here + tud_cdc_set_wanted_char(CHAR_CTRL_C); +#endif +} + +void usb_background(void) { + if (usb_enabled()) { + tusb_task(); + tud_cdc_write_flush(); + } +} + +//--------------------------------------------------------------------+ +// tinyusb callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) { +} + +// Invoked when device is unmounted +void tud_umount_cb(void) { +} + +uint32_t tusb_hal_millis(void) { + uint64_t ms; + uint32_t us; + current_tick(&ms, &us); + return (uint32_t) ms; +} + + +// Invoked when cdc when line state changed e.g connected/disconnected +// Use to reset to DFU when disconnect with 1200 bps +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { + (void) itf; // interface ID, not used + + // DTR = false is counted as disconnected + if ( !dtr ) + { + cdc_line_coding_t coding; + tud_cdc_get_line_coding(&coding); + + if ( coding.bit_rate == 1200 ) + { + reset_to_bootloader(); + } + } +} + +#if MICROPY_KBD_EXCEPTION + +/** + * Callback invoked when received an "wanted" char. + * @param itf Interface index (for multiple cdc interfaces) + * @param wanted_char The wanted char (set previously) + */ +void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) +{ + (void) itf; // not used + + // Workaround for using lib/utils/interrupt_char.c + // Compare mp_interrupt_char with wanted_char and ignore if not matched + if (mp_interrupt_char == wanted_char) { + tud_cdc_read_flush(); // flush read fifo + mp_keyboard_interrupt(); + } +} + +#endif diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c new file mode 100644 index 000000000..5a0de1ffc --- /dev/null +++ b/supervisor/shared/usb/usb_desc.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "supervisor/shared/usb/usb_desc.h" +#include "shared-module/usb_hid/Device.h" + +#include "genhdr/autogen_usb_descriptor.h" + +// tud_desc_set is required by tinyusb stack +tud_desc_set_t tud_desc_set = +{ + .device = &usb_desc_dev, + .config = &usb_desc_cfg, + .string_arr = (uint8_t const **) string_desc_arr, + .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]), + + .hid_report = + { + .generic = hid_report_descriptor, + .boot_keyboard = NULL, + .boot_mouse = NULL + } +}; diff --git a/supervisor/shared/usb/usb_desc.h b/supervisor/shared/usb/usb_desc.h new file mode 100644 index 000000000..250147aa6 --- /dev/null +++ b/supervisor/shared/usb/usb_desc.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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. + */ + +#ifndef USB_DESC_H_ +#define USB_DESC_H_ + +#include "lib/tinyusb/src/tusb.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// Descriptors set used by tinyusb stack +extern tud_desc_set_t tud_desc_set; + +#ifdef __cplusplus + } +#endif + +#endif /* USB_DESC_H_ */ diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c new file mode 100644 index 000000000..72dc40204 --- /dev/null +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -0,0 +1,205 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "tusb.h" +// // #include "supervisor/flash.h" + +// For updating fatfs's cache +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "lib/oofatfs/diskio.h" +#include "lib/oofatfs/ff.h" +#include "py/mpstate.h" + +#include "supervisor/shared/autoreload.h" + +#define MSC_FLASH_BLOCK_SIZE 512 + +// The root FS is always at the end of the list. +static fs_user_mount_t* get_vfs(int lun) { + // TODO(tannewt): Return the mount which matches the lun where 0 is the end + // and is counted in reverse. + if (lun > 0) { + return NULL; + } + mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table); + if (current_mount == NULL) { + return NULL; + } + while (current_mount->next != NULL) { + current_mount = current_mount->next; + } + return current_mount->obj; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 have their own callbacks +int32_t tud_msc_scsi_cb (uint8_t lun, const uint8_t scsi_cmd[16], void* buffer, uint16_t bufsize) { + const void* response = NULL; + uint16_t resplen = 0; + + switch ( scsi_cmd[0] ) { + case SCSI_CMD_TEST_UNIT_READY: + // Command that host uses to check our readiness before sending other commands + resplen = 0; + if (lun > 1) { + resplen = -1; + } else { + fs_user_mount_t* current_mount = get_vfs(lun); + if (current_mount == NULL) { + resplen = -1; + } + } + break; + + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Host is about to read/write etc ... better not to disconnect disk + resplen = 0; + break; + + case SCSI_CMD_START_STOP_UNIT: + { + // Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power + const scsi_start_stop_unit_t* start_stop = (const scsi_start_stop_unit_t*) scsi_cmd; + // Start bit = 0 : low power mode, if load_eject = 1 : unmount disk storage as well + // Start bit = 1 : Ready mode, if load_eject = 1 : mount disk storage + resplen = 0; + if (start_stop->load_eject == 1) { + if (lun > 1) { + resplen = -1; + } else { + fs_user_mount_t* current_mount = get_vfs(lun); + if (current_mount == NULL) { + resplen = -1; + } + } + } + } + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + + // return len must not larger than bufsize + if ( resplen > bufsize ) { + resplen = bufsize; + } + + // copy response to stack's buffer if any + if ( response && resplen ) { + memcpy(buffer, response, resplen); + } + + return resplen; +} + +bool tud_lun_capacity_cb(uint8_t lun, uint32_t* last_valid_sector, uint16_t* sector_size) { + fs_user_mount_t * vfs = get_vfs(lun); + if (vfs == NULL || + disk_ioctl(vfs, GET_SECTOR_COUNT, last_valid_sector) != RES_OK || + disk_ioctl(vfs, GET_SECTOR_SIZE, sector_size) != RES_OK) { + return false; + } + // Subtract one from the sector count to get the last valid sector. + (*last_valid_sector)--; + + return true; +} + +bool tud_msc_is_writable_cb(uint8_t lun) { + if (lun > 1) { + return false; + } + + fs_user_mount_t* vfs = get_vfs(lun); + if (vfs == NULL) { + return false; + } + if (vfs->writeblocks[0] == MP_OBJ_NULL || + (vfs->flags & FSUSER_USB_WRITABLE) == 0) { + return false; + } + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { + (void) lun; + (void) offset; + + const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE; + + fs_user_mount_t * vfs = get_vfs(lun); + disk_read(vfs, buffer, lba, block_count); + + return block_count * MSC_FLASH_BLOCK_SIZE; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { + (void) lun; + (void) offset; + + const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE; + + fs_user_mount_t * vfs = get_vfs(lun); + disk_write(vfs, buffer, lba, block_count); + // Since by getting here we assume the mount is read-only to + // MicroPython let's update the cached FatFs sector if it's the one + // we just wrote. + #if _MAX_SS != _MIN_SS + if (vfs->ssize == MSC_FLASH_BLOCK_SIZE) { + #else + // The compiler can optimize this away. + if (_MAX_SS == FILESYSTEM_BLOCK_SIZE) { + #endif + if (lba == vfs->fatfs.winsect && lba > 0) { + memcpy(vfs->fatfs.win, + buffer + MSC_FLASH_BLOCK_SIZE * (vfs->fatfs.winsect - lba), + MSC_FLASH_BLOCK_SIZE); + } + } + + return block_count * MSC_FLASH_BLOCK_SIZE; +} + +// Callback invoked when WRITE10 command is completed (status received and accepted by host). +// used to flush any pending cache. +void tud_msc_write10_complete_cb (uint8_t lun) { + (void) lun; + + // This write is complete, start the autoreload clock. + autoreload_start(); +} diff --git a/supervisor/spi_flash_api.h b/supervisor/spi_flash_api.h new file mode 100644 index 000000000..28cccb1b1 --- /dev/null +++ b/supervisor/spi_flash_api.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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. + */ +#ifndef MICROPY_INCLUDED_SUPERVISOR_SPI_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_SPI_FLASH_H + +#include +#include + +#include "supervisor/shared/external_flash/devices.h" + +// This API is implemented for both normal SPI peripherals and QSPI peripherals. + +bool spi_flash_command(uint8_t command); +bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length); +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length); +bool spi_flash_sector_command(uint8_t command, uint32_t address); +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length); +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length); +void spi_flash_init(void); +void spi_flash_init_device(const external_flash_device* device); + +#endif // MICROPY_INCLUDED_SUPERVISOR_SPI_FLASH_H diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index e516dcb2d..d870f799f 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -2,22 +2,78 @@ SRC_SUPERVISOR = \ main.c \ supervisor/port.c \ supervisor/shared/autoreload.c \ + supervisor/shared/filesystem.c \ + supervisor/shared/flash.c \ + supervisor/shared/micropython.c \ supervisor/shared/rgb_led_status.c \ supervisor/shared/stack.c \ + supervisor/shared/status_leds.c \ supervisor/shared/translate.c -ifeq ($(wildcard atmel-samd/supervisor/filesystem.c),) - SRC_SUPERVISOR += supervisor/filesystem.c +ifndef $(NO_USB) +NO_USB = $(wildcard supervisor/usb.c) +endif + + +# Choose which flash filesystem impl to use. +# (Right now INTERNAL_FLASH_FILESYSTEM and SPI_FLASH_FILESYSTEM are mutually exclusive. +# But that might not be true in the future.) +ifdef EXTERNAL_FLASH_DEVICES + CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \ + -DEXTERNAL_FLASH_DEVICE_COUNT=$(EXTERNAL_FLASH_DEVICE_COUNT) + + SRC_SUPERVISOR += supervisor/shared/external_flash/external_flash.c + ifeq ($(SPI_FLASH_FILESYSTEM),1) + CFLAGS += -DSPI_FLASH_FILESYSTEM + SRC_SUPERVISOR += supervisor/shared/external_flash/spi_flash.c + endif + ifeq ($(QSPI_FLASH_FILESYSTEM),1) + CFLAGS += -DQSPI_FLASH_FILESYSTEM + SRC_SUPERVISOR += supervisor/qspi_flash.c supervisor/shared/external_flash/qspi_flash.c + endif else - SRC_SUPERVISOR += supervisor/stub/filesystem.c + SRC_SUPERVISOR += supervisor/internal_flash.c endif -ifeq ($(wildcard atmel-samd/supervisor/serial.c),) - SRC_SUPERVISOR += supervisor/serial.c +ifeq ($(USB),FALSE) + ifeq ($(wildcard supervisor/serial.c),) + SRC_SUPERVISOR += supervisor/stub/serial.c + else + SRC_SUPERVISOR += supervisor/serial.c + endif else - SRC_SUPERVISOR += supervisor/stub/serial.c + SRC_SUPERVISOR += lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/control.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/class/msc/msc_device.c \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/class/hid/hid_device.c \ + lib/tinyusb/src/tusb.c \ + supervisor/shared/serial.c \ + supervisor/usb.c \ + supervisor/shared/usb/usb_desc.c \ + supervisor/shared/usb/usb.c \ + supervisor/shared/usb/usb_msc_flash.c \ + shared-bindings/usb_hid/__init__.c \ + shared-bindings/usb_hid/Device.c \ + shared-module/usb_hid/__init__.c \ + shared-module/usb_hid/Device.c \ + $(BUILD)/autogen_usb_descriptor.c + CFLAGS += -DUSB_AVAILABLE endif SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) $(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h + +$(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: ../../tools/gen_usb_descriptor.py Makefile | $(HEADER_BUILD) + $(STEPECHO) "GEN $@" + $(Q)install -d $(BUILD)/genhdr + $(PYTHON3) ../../tools/gen_usb_descriptor.py \ + --manufacturer $(USB_MANUFACTURER)\ + --product $(USB_PRODUCT)\ + --vid $(USB_VID)\ + --pid $(USB_PID)\ + --serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\ + --output_c_file $(BUILD)/autogen_usb_descriptor.c\ + --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h diff --git a/supervisor/usb.h b/supervisor/usb.h new file mode 100644 index 000000000..c87540d40 --- /dev/null +++ b/supervisor/usb.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SUPERVISOR_USB_H +#define MICROPY_INCLUDED_SUPERVISOR_USB_H + +#include + +// Ports must call this as frequently as they can in order to keep the USB connection +// alive and responsive. +void usb_background(void); + +// Only inits the USB peripheral clocks and pins. The peripheral will be initialized by +// TinyUSB. +void init_usb_hardware(void); + +// Shared implementation. +bool usb_enabled(void); +void usb_init(void); + +#endif // MICROPY_INCLUDED_SUPERVISOR_USB_H diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py new file mode 100644 index 000000000..b50c0798f --- /dev/null +++ b/tools/gen_usb_descriptor.py @@ -0,0 +1,474 @@ +import argparse + +import os +import sys + +sys.path.append("../../tools/usb_descriptor") + +from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standard, util +import hid_report_descriptors + +parser = argparse.ArgumentParser(description='Generate USB descriptors.') +parser.add_argument('--manufacturer', type=str, + help='manufacturer of the device') +parser.add_argument('--product', type=str, + help='product name of the device') +parser.add_argument('--vid', type=lambda x: int(x, 16), + help='vendor id') +parser.add_argument('--pid', type=lambda x: int(x, 16), + help='product id') +parser.add_argument('--serial_number_length', type=int, default=32, + help='length needed for the serial number in digits') +parser.add_argument('--output_c_file', type=argparse.FileType('w'), required=True) +parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True) + +args = parser.parse_args() + +class StringIndex: + """Assign a monotonically increasing index to each unique string. Start with 0.""" + string_to_index = {} + index_to_variable = {} + strings = [] + + @classmethod + def index(cls, string, *, variable_name = None): + if string in cls.string_to_index: + idx = cls.string_to_index[string] + if not cls.index_to_variable[idx]: + cls.index_to_variable[idx] = variable_name + return idx + else: + idx = len(cls.strings) + cls.string_to_index[string] = idx + cls.strings.append(string) + cls.index_to_variable[idx] = variable_name + return idx + + @classmethod + def strings_in_order(cls): + return cls.strings + + + +# langid must be the 0th string descriptor +LANGID_INDEX = StringIndex.index("\u0409", variable_name="language_id") +assert LANGID_INDEX == 0 +SERIAL_NUMBER_INDEX = StringIndex.index("S" * args.serial_number_length, variable_name="usb_serial_number") + +device = standard.DeviceDescriptor( + description="top", + idVendor=args.vid, + idProduct=args.pid, + iManufacturer=StringIndex.index(args.manufacturer), + iProduct=StringIndex.index(args.product), + iSerialNumber=SERIAL_NUMBER_INDEX) + +# Interface numbers are interface-set local and endpoints are interface local +# until util.join_interfaces renumbers them. + +cdc_union = cdc.Union( + description="CDC comm", + bMasterInterface=0x00, # Adjust this after interfaces are renumbered. + bSlaveInterface_list=[0x01]) # Adjust this after interfaces are renumbered. + +cdc_call_management = cdc.CallManagement( + description="CDC comm", + bmCapabilities=0x01, + bDataInterface=0x01) # Adjust this after interfaces are renumbered. + +cdc_comm_interface = standard.InterfaceDescriptor( + description="CDC comm", + bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class + bInterfaceSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model + bInterfaceProtocol=cdc.CDC_PROTOCOL_V25TER, + iInterface=StringIndex.index("CircuitPython CDC control"), + subdescriptors=[ + cdc.Header( + description="CDC comm", + bcdCDC=0x0110), + cdc_call_management, + cdc.AbstractControlManagement( + description="CDC comm", + bmCapabilities=0x02), + cdc_union, + standard.EndpointDescriptor( + description="CDC comm in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, + wMaxPacketSize=0x0040, + bInterval=0x10) + ]) + +cdc_data_interface = standard.InterfaceDescriptor( + description="CDC data", + bInterfaceClass=cdc.CDC_CLASS_DATA, + iInterface=StringIndex.index("CircuitPython CDC data"), + subdescriptors=[ + standard.EndpointDescriptor( + description="CDC data out", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK), + standard.EndpointDescriptor( + description="CDC data in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK), + ]) + +cdc_interfaces = [cdc_comm_interface, cdc_data_interface] + +msc_interfaces = [ + standard.InterfaceDescriptor( + description="MSC", + bInterfaceClass=msc.MSC_CLASS, + bInterfaceSubClass=msc.MSC_SUBCLASS_TRANSPARENT, + bInterfaceProtocol=msc.MSC_PROTOCOL_BULK, + iInterface=StringIndex.index("CircuitPython Mass Storage"), + subdescriptors=[ + standard.EndpointDescriptor( + description="MSC in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK, + bInterval=0), + standard.EndpointDescriptor( + description="MSC out", + bEndpointAddress=0x1 | standard.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK, + bInterval=0) + ] + ) +] + +# Include only these HID devices. +# DIGITIZER works on Linux but conflicts with MOUSE, so leave it out for now. +hid_devices = ("KEYBOARD", "MOUSE", "CONSUMER", "GAMEPAD") + +combined_hid_report_descriptor = hid.ReportDescriptor( + description="MULTIDEVICE", + report_descriptor=b''.join( + hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor for name in hid_devices )) + +hid_report_ids_dict = { name: hid_report_descriptors.REPORT_IDS[name] for name in hid_devices } +hid_report_lengths_dict = { name: hid_report_descriptors.REPORT_LENGTHS[name] for name in hid_devices } +hid_max_report_length = max(hid_report_lengths_dict.values()) + +# ASF4 expects keyboard and generic devices to have both in and out endpoints, +# and will fail (possibly silently) if both are not supplied. +hid_endpoint_in_descriptor = standard.EndpointDescriptor( + description="HID in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, + bInterval=10) + +hid_interfaces = [ + standard.InterfaceDescriptor( + description="HID Multiple Devices", + bInterfaceClass=hid.HID_CLASS, + bInterfaceSubClass=hid.HID_SUBCLASS_NOBOOT, + bInterfaceProtocol=hid.HID_PROTOCOL_NONE, + iInterface=StringIndex.index("CircuitPython HID"), + subdescriptors=[ + hid.HIDDescriptor( + description="HID", + wDescriptorLength=len(bytes(combined_hid_report_descriptor))), + hid_endpoint_in_descriptor, + ] + ), + ] + +# Audio! +midi_in_jack = midi.InJackDescriptor( + description="MIDI PC <- CircuitPython internals", + bJackType=midi.JACK_TYPE_EMBEDDED, + iJack=0) +midi_out_jack = midi.OutJackDescriptor( + description="MIDI PC -> CircuitPython internals", + bJackType=midi.JACK_TYPE_EMBEDDED, + iJack=0) +audio_midi_interface = standard.InterfaceDescriptor( + description="All the audio", + bInterfaceClass=audio.AUDIO_CLASS_DEVICE, + bInterfaceSubClass=audio.AUDIO_SUBCLASS_MIDI_STREAMING, + bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1, + iInterface=StringIndex.index("CircuitPython MIDI"), + subdescriptors=[ + midi.Header( + jacks_and_elements=[ + midi_in_jack, + midi.InJackDescriptor( + description="MIDI data in from user code.", + bJackType=midi.JACK_TYPE_EXTERNAL, iJack=0), + midi_out_jack, + midi.OutJackDescriptor( + description="MIDI data out to user code.", + bJackType=midi.JACK_TYPE_EXTERNAL, iJack=0), + ] + ), + standard.EndpointDescriptor( + description="MIDI data out", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK), + midi.DataEndpointDescriptor(baAssocJack=[midi_out_jack]), + standard.EndpointDescriptor( + description="MIDI data in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK), + midi.DataEndpointDescriptor(baAssocJack=[midi_in_jack]), + ]) + +cs_ac_interface = audio10.AudioControlInterface( + description="Empty audio control", + audio_streaming_interfaces = [], + midi_streaming_interfaces = [ + audio_midi_interface + ] + ) + +audio_control_interface = standard.InterfaceDescriptor( + description="All the audio", + bInterfaceClass=audio.AUDIO_CLASS_DEVICE, + bInterfaceSubClass=audio.AUDIO_SUBCLASS_CONTROL, + bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1, + iInterface=StringIndex.index("CircuitPython Audio"), + subdescriptors=[ + cs_ac_interface, + ]) + +# Audio streaming interfaces must occur before MIDI ones. +# audio_interfaces = [audio_control_interface] + cs_ac_interface.audio_streaming_interfaces + cs_ac_interface.midi_streaming_interfaces + +# This will renumber the endpoints to make them unique across descriptors, +# and renumber the interfaces in order. But we still need to fix up certain +# interface cross-references. +interfaces = util.join_interfaces(cdc_interfaces, msc_interfaces, hid_interfaces) + +# Now adjust the CDC interface cross-references. + +cdc_union.bMasterInterface = cdc_comm_interface.bInterfaceNumber +cdc_union.bSlaveInterface_list = [cdc_data_interface.bInterfaceNumber] + +cdc_call_management.bDataInterface = cdc_data_interface.bInterfaceNumber + +cdc_iad = standard.InterfaceAssociationDescriptor( + description="CDC IAD", + bFirstInterface=cdc_comm_interface.bInterfaceNumber, + bInterfaceCount=len(cdc_interfaces), + bFunctionClass=cdc.CDC_CLASS_COMM, # Communications Device Class + bFunctionSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model + bFunctionProtocol=cdc.CDC_PROTOCOL_V25TER) # TODO(tannewt): can this be NONE (aka 0)? + +# audio_iad = standard.InterfaceAssociationDescriptor( +# description="Audio IAD", +# bFirstInterface=audio_control_interface.bInterfaceNumber, +# bInterfaceCount=len(audio_interfaces), +# bFunctionClass=audio.AUDIO_CLASS_DEVICE, +# bFunctionSubClass=audio.AUDIO_SUBCLASS_UNKNOWN, +# bFunctionProtocol=audio.AUDIO_PROTOCOL_V1) + + +descriptor_list = [] +descriptor_list.append(cdc_iad) +# descriptor_list.append(audio_iad) +descriptor_list.extend(cdc_interfaces) +descriptor_list.extend(msc_interfaces) +# descriptor_list.append(audio_control_interface) +# Put the CDC IAD just before the CDC interfaces. +# There appears to be a bug in the Windows composite USB driver that requests the +# HID report descriptor with the wrong interface number if the HID interface is not given +# first. However, it still fetches the descriptor anyway. We could reorder the interfaces but +# the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it +# there for backwards compatibility. +descriptor_list.extend(hid_interfaces) + +configuration = standard.ConfigurationDescriptor( + description="Composite configuration", + wTotalLength=(standard.ConfigurationDescriptor.bLength + + sum([len(bytes(x)) for x in descriptor_list])), + bNumInterfaces=len(interfaces)) +descriptor_list.insert(0, configuration) + +string_descriptors = [standard.StringDescriptor(string) for string in StringIndex.strings_in_order()] +serial_number_descriptor = string_descriptors[SERIAL_NUMBER_INDEX] + +c_file = args.output_c_file +h_file = args.output_h_file + + +c_file.write("""\ +#include + +#include "{H_FILE_NAME}" + +""".format(H_FILE_NAME=h_file.name)) + +c_file.write("""\ +// {DESCRIPTION} : {CLASS} +""".format(DESCRIPTION=device.description, + CLASS=device.__class__)) + +c_file.write("""\ +const uint8_t usb_desc_dev[] = { +""") +for b in bytes(device): + c_file.write("0x{:02x}, ".format(b)) + +c_file.write("""\ +}; +""") + +c_file.write("""\ +const uint8_t usb_desc_cfg[] = { +""") + +# Write out all the regular descriptors as one long array (that's how ASF4 does it). +descriptor_length = 0 +for descriptor in descriptor_list: + c_file.write("""\ +// {DESCRIPTION} : {CLASS} +""".format(DESCRIPTION=descriptor.description, + CLASS=descriptor.__class__)) + + b = bytes(descriptor) + notes = descriptor.notes() + i = 0 + + # This prints each subdescriptor on a separate line. + n = 0 + while i < len(b): + length = b[i] + for j in range(length): + c_file.write("0x{:02x}, ".format(b[i + j])) + c_file.write("// " + notes[n]) + n += 1 + c_file.write("\n") + i += length + descriptor_length += len(b) + +c_file.write("""\ +}; +""") + +pointers_to_strings = [] + +for idx, descriptor in enumerate(string_descriptors): + c_file.write("""\ +// {DESCRIPTION} : {CLASS} +""".format(DESCRIPTION=descriptor.description, + CLASS=descriptor.__class__)) + + b = bytes(descriptor) + notes = descriptor.notes() + i = 0 + + # This prints each subdescriptor on a separate line. + variable_name = StringIndex.index_to_variable[idx] + if not variable_name: + variable_name = "string_descriptor{}".format(idx) + + const = "const " + if variable_name == "usb_serial_number": + const = "" + c_file.write("""\ +{const}uint16_t {NAME}[] = {{ +""".format(const=const, NAME=variable_name)) + pointers_to_strings.append("{name}".format(name=variable_name)) + n = 0 + while i < len(b): + length = b[i] + for j in range(length // 2): + c_file.write("0x{:04x}, ".format(b[i + 2*j + 1] << 8 | b[i + 2*j])) + n += 1 + c_file.write("\n") + i += length + c_file.write("""\ +}; +""") + +c_file.write("""\ +// array of pointer to string descriptors +uint16_t const * const string_desc_arr [] = +{ +""") +c_file.write(""",\ + +""".join(pointers_to_strings)) + +c_file.write(""" +}; +""") + +c_file.write("\n"); + +hid_descriptor_length = len(bytes(combined_hid_report_descriptor)) + +# Now we values we need for the .h file. +h_file.write("""\ +#ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H +#define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H + +#include + +const uint8_t usb_desc_dev[{device_length}]; +// Make sure the control buffer is big enough to fit the descriptor. +#define CFG_TUD_ENUM_BUFFER_SIZE {max_configuration_length} +const uint8_t usb_desc_cfg[{configuration_length}]; +uint16_t usb_serial_number[{serial_number_length}]; +uint16_t const * const string_desc_arr [{string_descriptor_length}]; + +const uint8_t hid_report_descriptor[{HID_REPORT_DESCRIPTOR_LENGTH}]; + +// Vendor name included in Inquiry response, max 8 bytes +#define CFG_TUD_MSC_VENDOR "{msc_vendor}" + +// Product name included in Inquiry response, max 16 bytes +#define CFG_TUD_MSC_PRODUCT "{msc_product}" + +""" +.format(serial_number_length=len(bytes(serial_number_descriptor)) // 2, + device_length=len(bytes(device)), + configuration_length=descriptor_length, + max_configuration_length=max(hid_descriptor_length, descriptor_length), + string_descriptor_length=len(pointers_to_strings), + HID_REPORT_DESCRIPTOR_LENGTH=len(bytes(combined_hid_report_descriptor)), + msc_vendor=args.manufacturer[:8], + msc_product=args.product[:16])) + +# #define the report ID's used in the combined HID descriptor +for name, id in hid_report_ids_dict.items(): + h_file.write("""\ +#define USB_HID_REPORT_ID_{name} {id} +""".format(name=name, + id=id)) + +h_file.write("\n") + +# #define the report sizes used in the combined HID descriptor +for name, length in hid_report_lengths_dict.items(): + h_file.write("""\ +#define USB_HID_REPORT_LENGTH_{name} {length} +""".format(name=name, + length=length)) + +h_file.write("\n") + +h_file.write("""\ +#define USB_HID_NUM_DEVICES {num_devices} +#define USB_HID_MAX_REPORT_LENGTH {max_length} +""".format(num_devices=len(hid_report_lengths_dict), + max_length=hid_max_report_length)) + + + +# Write out the report descriptor and info +c_file.write("""\ +const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ +""".format(HID_DESCRIPTOR_LENGTH=hid_descriptor_length)) + +for b in bytes(combined_hid_report_descriptor): + c_file.write("0x{:02x}, ".format(b)) +c_file.write(""" +}; +""") + +h_file.write("""\ +#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H +""") diff --git a/tools/hid_report_descriptors.py b/tools/hid_report_descriptors.py new file mode 100644 index 000000000..f3b28ebcf --- /dev/null +++ b/tools/hid_report_descriptors.py @@ -0,0 +1,239 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Dan Halbert for Adafruit Industries +# +# 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. + +import struct + +""" +HID specific descriptors +======================== + +* Author(s): Dan Halbert +""" + +from adafruit_usb_descriptor import hid + +REPORT_IDS = { + "KEYBOARD" : 1, + "MOUSE" : 2, + "CONSUMER" : 3, + "SYS_CONTROL" : 4, + "GAMEPAD" : 5, + "DIGITIZER" : 6, + } + +# Byte count for each kind of report. Length does not include report ID in first byte. +REPORT_LENGTHS = { + "KEYBOARD" : 8, + "MOUSE" : 4, + "CONSUMER" : 2, + "SYS_CONTROL" : 1, + "GAMEPAD" : 6, + "DIGITIZER" : 5, + } + +KEYBOARD_WITH_ID = hid.ReportDescriptor( + description="KEYBOARD", + report_descriptor=bytes([ + # Regular keyboard + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x06, # Usage (Keyboard) + 0xA1, 0x01, # Collection (Application) + 0x85, REPORT_IDS["KEYBOARD"], # Report ID (1) + 0x05, 0x07, # Usage Page (Keyboard) + 0x19, 224, # Usage Minimum (224) + 0x29, 231, # Usage Maximum (231) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x01, # Logical Maximum (1) + 0x75, 0x01, # Report Size (1) + 0x95, 0x08, # Report Count (8) + 0x81, 0x02, # Input (Data, Variable, Absolute) + 0x81, 0x01, # Input (Constant) + 0x19, 0x00, # Usage Minimum (0) + 0x29, 101, # Usage Maximum (101) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 101, # Logical Maximum (101) + 0x75, 0x08, # Report Size (8) + 0x95, 0x06, # Report Count (6) + 0x81, 0x00, # Input (Data, Array) + 0x05, 0x08, # Usage Page (LED) + 0x19, 0x01, # Usage Minimum (1) + 0x29, 0x05, # Usage Maximum (5) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x01, # Logical Maximum (1) + 0x75, 0x01, # Report Size (1) + 0x95, 0x05, # Report Count (5) + 0x91, 0x02, # Output (Data, Variable, Absolute) + 0x95, 0x03, # Report Count (3) + 0x91, 0x01, # Output (Constant) + 0xC0, # End Collection + ])) + +MOUSE_WITH_ID = hid.ReportDescriptor( + description="MOUSE", + report_descriptor=bytes([ + # Regular mouse + 0x05, 0x01, # Usage Page (Generic Desktop) + 0x09, 0x02, # Usage (Mouse) + 0xA1, 0x01, # Collection (Application) + 0x09, 0x01, # Usage (Pointer) + 0xA1, 0x00, # Collection (Physical) + 0x85, REPORT_IDS["MOUSE"], # Report ID (n) + 0x05, 0x09, # Usage Page (Button) + 0x19, 0x01, # Usage Minimum (0x01) + 0x29, 0x05, # Usage Maximum (0x05) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x01, # Logical Maximum (1) + 0x95, 0x05, # Report Count (5) + 0x75, 0x01, # Report Size (1) + 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, # Report Count (1) + 0x75, 0x03, # Report Size (3) + 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x30, # Usage (X) + 0x09, 0x31, # Usage (Y) + 0x15, 0x81, # Logical Minimum (-127) + 0x25, 0x7F, # Logical Maximum (127) + 0x75, 0x08, # Report Size (8) + 0x95, 0x02, # Report Count (2) + 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0x09, 0x38, # Usage (Wheel) + 0x15, 0x81, # Logical Minimum (-127) + 0x25, 0x7F, # Logical Maximum (127) + 0x75, 0x08, # Report Size (8) + 0x95, 0x01, # Report Count (1) + 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, # End Collection + 0xC0, # End Collection + ])) + +CONSUMER_WITH_ID = hid.ReportDescriptor( + description="CONSUMER", + report_descriptor=bytes([ + # Consumer ("multimedia") keys + 0x05, 0x0C, # Usage Page (Consumer) + 0x09, 0x01, # Usage (Consumer Control) + 0xA1, 0x01, # Collection (Application) + 0x85, REPORT_IDS["CONSUMER"], # Report ID (n) + 0x75, 0x10, # Report Size (16) + 0x95, 0x01, # Report Count (1) + 0x15, 0x01, # Logical Minimum (1) + 0x26, 0x8C, 0x02, # Logical Maximum (652) + 0x19, 0x01, # Usage Minimum (Consumer Control) + 0x2A, 0x8C, 0x02, # Usage Maximum (AC Send) + 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, # End Collection + ])) + +SYS_CONTROL_WITH_ID = hid.ReportDescriptor( + description="SYS_CONTROL", + report_descriptor=bytes([ + # Power controls + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x80, # Usage (Sys Control) + 0xA1, 0x01, # Collection (Application) + 0x85, REPORT_IDS["SYS_CONTROL"], # Report ID (n) + 0x75, 0x02, # Report Size (2) + 0x95, 0x01, # Report Count (1) + 0x15, 0x01, # Logical Minimum (1) + 0x25, 0x03, # Logical Maximum (3) + 0x09, 0x82, # Usage (Sys Sleep) + 0x09, 0x81, # Usage (Sys Power Down) + 0x09, 0x83, # Usage (Sys Wake Up) + 0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State) + 0x75, 0x06, # Report Size (6) + 0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, # End Collection + ])) + +GAMEPAD_WITH_ID = hid.ReportDescriptor( + description="GAMEPAD", + report_descriptor=bytes([ + # Gamepad with 16 buttons and two joysticks + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x05, # Usage (Game Pad) + 0xA1, 0x01, # Collection (Application) + 0x85, REPORT_IDS["GAMEPAD"], # Report ID (n) + 0x05, 0x09, # Usage Page (Button) + 0x19, 0x01, # Usage Minimum (Button 1) + 0x29, 0x10, # Usage Maximum (Button 16) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x01, # Logical Maximum (1) + 0x75, 0x01, # Report Size (1) + 0x95, 0x10, # Report Count (16) + 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x15, 0x81, # Logical Minimum (-127) + 0x25, 0x7F, # Logical Maximum (127) + 0x09, 0x30, # Usage (X) + 0x09, 0x31, # Usage (Y) + 0x09, 0x32, # Usage (Z) + 0x09, 0x35, # Usage (Rz) + 0x75, 0x08, # Report Size (8) + 0x95, 0x04, # Report Count (4) + 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, # End Collection + ])) + +DIGITIZER_WITH_ID = hid.ReportDescriptor( + description="DIGITIZER", + report_descriptor=bytes([ + # Digitizer (used as an absolute pointer) + 0x05, 0x0D, # Usage Page (Digitizers) + 0x09, 0x02, # Usage (Pen) + 0xA1, 0x01, # Collection (Application) + 0x85, REPORT_IDS["DIGITIZER"], # Report ID (n) + 0x09, 0x01, # Usage (Stylus) + 0xA1, 0x00, # Collection (Physical) + 0x09, 0x32, # Usage (In-Range) + 0x09, 0x42, # Usage (Tip Switch) + 0x09, 0x44, # Usage (Barrel Switch) + 0x09, 0x45, # Usage (Eraser Switch) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x01, # Logical Maximum (1) + 0x75, 0x01, # Report Size (1) + 0x95, 0x04, # Report Count (4) + 0x81, 0x02, # Input (Data,Var,Abs) + 0x75, 0x04, # Report Size (4) -- Filler + 0x95, 0x01, # Report Count (1) -- Filler + 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x15, 0x00, # Logical Minimum (0) + 0x26, 0xff, 0x7f, # Logical Maximum (32767) + 0x09, 0x30, # Usage (X) + 0x09, 0x31, # Usage (Y) + 0x75, 0x10, # Report Size (16) + 0x95, 0x02, # Report Count (2) + 0x81, 0x02, # Input (Data,Var,Abs) + 0xC0, # End Collection + 0xC0, # End Collection + ])) + +# Byte count for each kind of report. Length does not include report ID in first byte. +REPORT_DESCRIPTORS = { + "KEYBOARD" : KEYBOARD_WITH_ID, + "MOUSE" : MOUSE_WITH_ID, + "CONSUMER" : CONSUMER_WITH_ID, + "SYS_CONTROL" : SYS_CONTROL_WITH_ID, + "GAMEPAD" : GAMEPAD_WITH_ID, + "DIGITIZER" : DIGITIZER_WITH_ID, + } diff --git a/tools/usb_descriptor b/tools/usb_descriptor index 250784703..57bf602da 160000 --- a/tools/usb_descriptor +++ b/tools/usb_descriptor @@ -1 +1 @@ -Subproject commit 2507847031a0395465956539253cbfa27f87511e +Subproject commit 57bf602dac9cba8c4226f764c286cbc60103d67d -- cgit v1.2.3 From 168e23e466737fd25d6c7a524231781e35d1d49a Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 9 Nov 2018 00:11:43 -0800 Subject: Build refinement to handle warnings and quiet output --- lib/tinyusb | 2 +- py/py.mk | 4 ++-- shared-module/usb_hid/__init__.c | 12 ++++++------ supervisor/shared/usb/tusb_config.h | 1 + supervisor/supervisor.mk | 8 ++++++-- 5 files changed, 16 insertions(+), 11 deletions(-) (limited to 'shared-module') diff --git a/lib/tinyusb b/lib/tinyusb index 30e3c6413..537a29273 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 30e3c64134789416e10ed867fa12c210b808e98f +Subproject commit 537a29273c08b1e047004e1bd71c37af82937dd4 diff --git a/py/py.mk b/py/py.mk index 9874dde4d..c640e6cff 100644 --- a/py/py.mk +++ b/py/py.mk @@ -307,7 +307,7 @@ $(HEADER_BUILD)/qstrdefs.preprocessed.h: $(PY_QSTR_DEFS) $(QSTR_DEFS) $(QSTR_DEF # qstr data $(HEADER_BUILD)/qstrdefs.enum.h: $(PY_SRC)/makeqstrdata.py $(HEADER_BUILD)/qstrdefs.preprocessed.h $(STEPECHO) "GEN $@" - $(PYTHON3) $(PY_SRC)/makeqstrdata.py $(HEADER_BUILD)/qstrdefs.preprocessed.h > $@ + $(Q)$(PYTHON3) $(PY_SRC)/makeqstrdata.py $(HEADER_BUILD)/qstrdefs.preprocessed.h > $@ # Adding an order only dependency on $(HEADER_BUILD) causes $(HEADER_BUILD) to get # created before we run the script to generate the .h @@ -315,7 +315,7 @@ $(HEADER_BUILD)/qstrdefs.enum.h: $(PY_SRC)/makeqstrdata.py $(HEADER_BUILD)/qstrd # the lines in "" and then unwrap after the preprocessor is finished. $(HEADER_BUILD)/qstrdefs.generated.h: $(PY_SRC)/makeqstrdata.py $(HEADER_BUILD)/$(TRANSLATION).mo $(HEADER_BUILD)/qstrdefs.preprocessed.h $(STEPECHO) "GEN $@" - $(PYTHON3) $(PY_SRC)/makeqstrdata.py --compression_filename $(HEADER_BUILD)/compression.generated.h --translation $(HEADER_BUILD)/$(TRANSLATION).mo $(HEADER_BUILD)/qstrdefs.preprocessed.h > $@ + $(Q)$(PYTHON3) $(PY_SRC)/makeqstrdata.py --compression_filename $(HEADER_BUILD)/compression.generated.h --translation $(HEADER_BUILD)/$(TRANSLATION).mo $(HEADER_BUILD)/qstrdefs.preprocessed.h > $@ $(PY_BUILD)/qstr.o: $(HEADER_BUILD)/qstrdefs.generated.h diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c index c0f9c897c..f14fdd41e 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -58,7 +58,7 @@ static uint8_t digitizer_report_buffer[USB_HID_REPORT_LENGTH_DIGITIZER]; #endif usb_hid_device_obj_t usb_hid_devices[] = { -#if USB_HID_REPORT_ID_KEYBOARD +#ifdef USB_HID_REPORT_ID_KEYBOARD { .base = { .type = &usb_hid_device_type } , .report_buffer = keyboard_report_buffer , @@ -69,7 +69,7 @@ usb_hid_device_obj_t usb_hid_devices[] = { }, #endif -#if USB_HID_REPORT_ID_MOUSE +#ifdef USB_HID_REPORT_ID_MOUSE { .base = { .type = &usb_hid_device_type } , .report_buffer = mouse_report_buffer , @@ -80,7 +80,7 @@ usb_hid_device_obj_t usb_hid_devices[] = { }, #endif -#if USB_HID_REPORT_ID_CONSUMER +#ifdef USB_HID_REPORT_ID_CONSUMER { .base = { .type = &usb_hid_device_type } , .report_buffer = consumer_report_buffer , @@ -91,7 +91,7 @@ usb_hid_device_obj_t usb_hid_devices[] = { }, #endif -#if USB_HID_REPORT_ID_SYS_CONTROL +#ifdef USB_HID_REPORT_ID_SYS_CONTROL { .base = { .type = &usb_hid_device_type } , .report_buffer = sys_control_report_buffer , @@ -102,7 +102,7 @@ usb_hid_device_obj_t usb_hid_devices[] = { }, #endif -#if USB_HID_REPORT_ID_GAMEPAD +#ifdef USB_HID_REPORT_ID_GAMEPAD { .base = { .type = &usb_hid_device_type } , .report_buffer = gamepad_report_buffer , @@ -113,7 +113,7 @@ usb_hid_device_obj_t usb_hid_devices[] = { }, #endif -#if USB_HID_REPORT_ID_DIGITIZER +#ifdef USB_HID_REPORT_ID_DIGITIZER { .base = { .type = &usb_hid_device_type } , .report_buffer = digitizer_report_buffer , diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index 4693bfced..649c390ac 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -75,6 +75,7 @@ #define CFG_TUD_CDC 1 #define CFG_TUD_MSC 1 #define CFG_TUD_HID 1 +#define CFG_TUD_CUSTOM_CLASS 0 /*------------------------------------------------------------------*/ /* CLASS DRIVER diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index d870f799f..ac982d277 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -66,10 +66,14 @@ SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) $(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h -$(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: ../../tools/gen_usb_descriptor.py Makefile | $(HEADER_BUILD) +$(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: autogen_usb_descriptor.intermediate + +.INTERMEDIATE: autogen_usb_descriptor.intermediate + +autogen_usb_descriptor.intermediate: ../../tools/gen_usb_descriptor.py Makefile | $(HEADER_BUILD) $(STEPECHO) "GEN $@" $(Q)install -d $(BUILD)/genhdr - $(PYTHON3) ../../tools/gen_usb_descriptor.py \ + $(Q)$(PYTHON3) ../../tools/gen_usb_descriptor.py \ --manufacturer $(USB_MANUFACTURER)\ --product $(USB_PRODUCT)\ --vid $(USB_VID)\ -- cgit v1.2.3 From 28383afa11863f1fe289cc811c8eab403ea79f6f Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Fri, 9 Nov 2018 18:25:55 +0100 Subject: shared-module/os: Fix os.mkdir('a/b') This fixes commit a99f9427420d("'/' and '\' are also acceptable ends of the path now") which broke mkdir. The problem is where the directory name is a single letter like this: >>> os.mkdir('a') >>> os.mkdir('a/b') Traceback (most recent call last): File "", line 1, in OSError: [Errno 17] File exists >>> os.mkdir('a/bb') >>> I wasn't smart enough to fix this in the oofatfs library, so I did it in the os shared module by creating a path lookup function for the os methods that only deals with directories. I reverted the library change introduced by the aforementioned commit. This means that os.stat and os.rename can't handle trailing slashes. This is to avoid allowing filenames with trailing slashes to pass through. In order to handle trailing slashes for these it would be necessary to check if it really is a directory before stripping. I didn't do this since the original issue was to make os.chdir tolerate trailing slashes. There's an open MicroPython issue #2929 wrt. trailing slashes and mkdir. --- lib/oofatfs/ff.c | 2 +- shared-module/os/__init__.c | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) (limited to 'shared-module') diff --git a/lib/oofatfs/ff.c b/lib/oofatfs/ff.c index 4e8680545..b0984756b 100644 --- a/lib/oofatfs/ff.c +++ b/lib/oofatfs/ff.c @@ -2579,8 +2579,8 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ lfn[di++] = w; /* Store the Unicode character */ } - cf = ((w < ' ') || ((w == '/' || w == '\\') && p[si+1] < ' ')) ? NS_LAST : 0; /* Set last segment flag if end of the path */ *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ #if _FS_RPATH != 0 if ((di == 1 && lfn[di - 1] == '.') || (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ diff --git a/shared-module/os/__init__.c b/shared-module/os/__init__.c index a38f3781f..313893d97 100644 --- a/shared-module/os/__init__.c +++ b/shared-module/os/__init__.c @@ -50,6 +50,20 @@ STATIC mp_vfs_mount_t *lookup_path(const char* path, mp_obj_t *path_out) { return vfs; } +// Strip off trailing slashes to please underlying libraries +STATIC mp_vfs_mount_t *lookup_dir_path(const char* path, mp_obj_t *path_out) { + const char *p_out; + mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out); + if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) { + size_t len = strlen(p_out); + while (len > 1 && p_out[len - 1] == '/') { + len--; + } + *path_out = mp_obj_new_str_of_type(&mp_type_str, (const byte*)p_out, len); + } + return vfs; +} + STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) { if (vfs == MP_VFS_NONE) { // mount point not found @@ -69,7 +83,7 @@ STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_ void common_hal_os_chdir(const char* path) { mp_obj_t path_out; - mp_vfs_mount_t *vfs = lookup_path(path, &path_out); + mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out); MP_STATE_VM(vfs_cur) = vfs; if (vfs == MP_VFS_ROOT) { // If we change to the root dir and a VFS is mounted at the root then @@ -93,7 +107,7 @@ mp_obj_t common_hal_os_getcwd(void) { mp_obj_t common_hal_os_listdir(const char* path) { mp_obj_t path_out; - mp_vfs_mount_t *vfs = lookup_path(path, &path_out); + mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out); mp_vfs_ilistdir_it_t iter; mp_obj_t iter_obj = MP_OBJ_FROM_PTR(&iter); @@ -120,7 +134,7 @@ mp_obj_t common_hal_os_listdir(const char* path) { void common_hal_os_mkdir(const char* path) { mp_obj_t path_out; - mp_vfs_mount_t *vfs = lookup_path(path, &path_out); + mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out); if (vfs == MP_VFS_ROOT || (vfs != MP_VFS_NONE && !strcmp(mp_obj_str_get_str(path_out), "/"))) { mp_raise_OSError(MP_EEXIST); } @@ -146,7 +160,7 @@ void common_hal_os_rename(const char* old_path, const char* new_path) { void common_hal_os_rmdir(const char* path) { mp_obj_t path_out; - mp_vfs_mount_t *vfs = lookup_path(path, &path_out); + mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out); mp_vfs_proxy_call(vfs, MP_QSTR_rmdir, 1, &path_out); } -- cgit v1.2.3