Porównaj commity

...

18 Commity

Autor SHA1 Wiadomość Data
felixdoerre 676fc6ec21
Merge 64a24f426e into e60e8079a7 2024-04-26 18:30:08 +08:00
Damien George e60e8079a7 nrf/mpconfigport: Enable MICROPY_NLR_THUMB_USE_LONG_JUMP on nRF51xx.
Signed-off-by: Damien George <damien@micropython.org>
2024-04-26 11:15:59 +10:00
J. Neuschäfer 7b050b366b py/nlrthumb: Make non-Thumb2 long-jump workaround opt-in.
Although the original motivation given for the workaround[1] is correct,
nlr.o and nlrthumb.o are linked with a small enough distance that the
problem does not occur, and the workaround isn't necessary. The distance
between the b instruction and its target (nlr_push_tail) is just 64
bytes[2], well within the ±2046 byte range addressable by an
unconditional branch instruction in Thumb mode.

The workaround induces a relocation in the text section (textrel), which
isn't supported everywhere, notably not on musl-libc[3], where it causes
a crash on start-up. With the workaround removed, micropython works on an
ARMv5T Linux system built with musl-libc.

This commit changes nlrthumb.c to use a direct jump by default, but
leaves the long jump workaround as an option for those cases where it's
actually needed.

[1]: commit dd376a239d

Author: Damien George <damien.p.george@gmail.com>
Date:   Fri Sep 1 15:25:29 2017 +1000

    py/nlrthumb: Get working again on standard Thumb arch (ie not Thumb2).

    "b" on Thumb might not be long enough for the jump to nlr_push_tail so
    it must be done indirectly.

[2]: Excerpt from objdump -d micropython:

000095c4 <nlr_push_tail>:
    95c4:       b510            push    {r4, lr}
    95c6:       0004            movs    r4, r0
    95c8:       f02d fd42       bl      37050 <mp_thread_get_state>
    95cc:       6943            ldr     r3, [r0, #20]
    95ce:       6023            str     r3, [r4, #0]
    95d0:       6144            str     r4, [r0, #20]
    95d2:       2000            movs    r0, #0
    95d4:       bd10            pop     {r4, pc}

000095d6 <nlr_pop>:
    95d6:       b510            push    {r4, lr}
    95d8:       f02d fd3a       bl      37050 <mp_thread_get_state>
    95dc:       6943            ldr     r3, [r0, #20]
    95de:       681b            ldr     r3, [r3, #0]
    95e0:       6143            str     r3, [r0, #20]
    95e2:       bd10            pop     {r4, pc}

000095e4 <nlr_push>:
    95e4:       60c4            str     r4, [r0, #12]
    95e6:       6105            str     r5, [r0, #16]
    95e8:       6146            str     r6, [r0, #20]
    95ea:       6187            str     r7, [r0, #24]
    95ec:       4641            mov     r1, r8
    95ee:       61c1            str     r1, [r0, #28]
    95f0:       4649            mov     r1, r9
    95f2:       6201            str     r1, [r0, #32]
    95f4:       4651            mov     r1, sl
    95f6:       6241            str     r1, [r0, #36]   @ 0x24
    95f8:       4659            mov     r1, fp
    95fa:       6281            str     r1, [r0, #40]   @ 0x28
    95fc:       4669            mov     r1, sp
    95fe:       62c1            str     r1, [r0, #44]   @ 0x2c
    9600:       4671            mov     r1, lr
    9602:       6081            str     r1, [r0, #8]
    9604:       e7de            b.n     95c4 <nlr_push_tail>

[3]: https://www.openwall.com/lists/musl/2020/09/25/4

Signed-off-by: J. Neuschäfer <j.ne@posteo.net>
2024-04-25 16:06:28 +10:00
Damien George 49af8cad49 webassembly/api: Inject asyncio.run if needed by the script.
This allows a simple way to run the existing asyncio tests under the
webassembly port, which doesn't support `asyncio.run()`.

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:24:00 +10:00
Damien George 8a3546b3bd webassembly: Add JavaScript-based asyncio support.
This commit adds a significant portion of the existing MicroPython asyncio
module to the webassembly port, using parts of the existing asyncio code
and some custom JavaScript parts.

The key difference to the standard asyncio is that this version uses the
JavaScript runtime to do the actual scheduling and waiting on events, eg
Promise fulfillment, timeouts, fetching URLs.

This implementation does not include asyncio.run(). Instead one just uses
asyncio.create_task(..) to start tasks and then returns to the JavaScript.
Then JavaScript will run the tasks.

The implementation here tries to reuse as much existing asyncio code as
possible, and gets all the semantics correct for things like cancellation
and asyncio.wait_for.  An alternative approach would reimplement Task,
Event, etc using JavaScript Promise's.  That approach is very difficult to
get right when trying to implement cancellation (because it's not possible
to cancel a JavaScript Promise).

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:24:00 +10:00
Damien George 84d6f8e8cb webassembly/modjsffi: Add jsffi.async_timeout_ms.
This function exposes `setTimeout()` as an async function.

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:24:00 +10:00
Damien George 967ad38ac7 extmod/modasyncio: Make mp_asyncio_context variable public.
So it can be accessed by a port if needed, for example to see if asyncio
has been imported.

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:23:59 +10:00
Damien George d998ca78c8 webassembly/proxy_c: Fix then-continue to convert reason to throw value.
When a Promise is rejected on the JavaScript side, the reject reason should
be thrown into the encapsulating generator on the Python side.

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:23:42 +10:00
Damien George 92b3b69648 webassembly/proxy_c: Fix proxy then reject handling.
An exception on the Python side should be passed to the Promise reject
callback on the JavaScript side.

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:14:17 +10:00
Damien George 4c3f5f552b webassembly/objjsproxy: Fix handling of thrown value into JS generator.
Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 16:07:00 +10:00
Damien George 9c7f0659e2 webassembly/api: Allocate code data on C heap when running Python code.
Otherwise Emscripten allocates it on the Emscripten C stack, which will
overflow for large amounts of code.

Fixes issue #14307.

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 13:15:54 +10:00
Damien George 45848f77ca webassembly/api: Fix waiting for Emscripten module to be loaded.
In modularize mode, the `_createMicroPythonModule()` constructor must be
await'ed on, before `Module` is ready to use.

Signed-off-by: Damien George <damien@micropython.org>
2024-04-24 13:15:54 +10:00
robert-hh 64a24f426e cc3200/modflash.c: Add the ipconfig() methods.
Signed-off-by: robert-hh <robert@hammelrath.com>
2024-03-30 22:41:01 +00:00
Felix Dörre 92b9b97997 ports/esp8266/network_wlan: Get/set config of esp8266 via ipconfig.
Co-authored-by: robert-hh <robert@hammelrath.com>

Signed-off-by: Felix Dörre <felix@dogcraft.de>
2024-03-30 22:41:01 +00:00
Felix Dörre 6a9af21bde ports/esp32/network_ppp: Get config of esp ppp interfaces via ipconfig.
Signed-off-by: Felix Dörre <felix@dogcraft.de>
2024-03-30 22:41:01 +00:00
robert-hh 329ecf9130 extmod/network_lwip.c: Allow using the CIDR notation for addr4.
There was a little omisssion in the code.

Signed-off-by: robert-hh <robert@hammelrath.com>
2024-03-30 22:41:01 +00:00
robert-hh 29bdcc3ff5 extmod/network_ninaw10: Implement the ipconfig() methods for NINAW10.
Due to a omission in the NINAW10 driver stack, setting the DNS address
has no effect. But the interface is kept here just in case it's fixed
eventually.

dhcp4 and has_dhcp4 are dummy arguments. NINAW10 seems to use always
DHCP.

Signed-off-by: robert-hh <robert@hammelrath.com>
2024-03-30 22:41:01 +00:00
Felix Dörre 7e1fc9e3a0 ports/{esp32,...}: Implement nic.ipconfig for more network interfaces.
Signed-off-by: Felix Dörre <felix@dogcraft.de>
2024-03-30 22:41:01 +00:00
37 zmienionych plików z 1261 dodań i 43 usunięć

Wyświetl plik

@ -156,7 +156,7 @@ static MP_DEFINE_CONST_OBJ_TYPE(
// Task class
// This is the core asyncio context with cur_task, _task_queue and CancelledError.
static mp_obj_t asyncio_context = MP_OBJ_NULL;
mp_obj_t mp_asyncio_context = MP_OBJ_NULL;
static mp_obj_t task_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, false);
@ -168,7 +168,7 @@ static mp_obj_t task_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
self->state = TASK_STATE_RUNNING_NOT_WAITED_ON;
self->ph_key = MP_OBJ_NEW_SMALL_INT(0);
if (n_args == 2) {
asyncio_context = args[1];
mp_asyncio_context = args[1];
}
return MP_OBJ_FROM_PTR(self);
}
@ -186,7 +186,7 @@ static mp_obj_t task_cancel(mp_obj_t self_in) {
return mp_const_false;
}
// Can't cancel self (not supported yet).
mp_obj_t cur_task = mp_obj_dict_get(asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
mp_obj_t cur_task = mp_obj_dict_get(mp_asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
if (self_in == cur_task) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("can't cancel self"));
}
@ -195,7 +195,7 @@ static mp_obj_t task_cancel(mp_obj_t self_in) {
self = MP_OBJ_TO_PTR(self->data);
}
mp_obj_t _task_queue = mp_obj_dict_get(asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR__task_queue));
mp_obj_t _task_queue = mp_obj_dict_get(mp_asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR__task_queue));
// Reschedule Task as a cancelled task.
mp_obj_t dest[3];
@ -218,7 +218,7 @@ static mp_obj_t task_cancel(mp_obj_t self_in) {
task_queue_push(2, dest);
}
self->data = mp_obj_dict_get(asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_CancelledError));
self->data = mp_obj_dict_get(mp_asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_CancelledError));
return mp_const_true;
}
@ -278,7 +278,7 @@ static mp_obj_t task_iternext(mp_obj_t self_in) {
nlr_raise(self->data);
} else {
// Put calling task on waiting queue.
mp_obj_t cur_task = mp_obj_dict_get(asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
mp_obj_t cur_task = mp_obj_dict_get(mp_asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
mp_obj_t args[2] = { self->state, cur_task };
task_queue_push(2, args);
// Set calling task's data to this task that it waits on, to double-link it.

Wyświetl plik

@ -144,12 +144,15 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_network_hostname_obj, 0, 1, mod_n
#if LWIP_VERSION_MAJOR >= 2
MP_DEFINE_CONST_FUN_OBJ_KW(mod_network_ipconfig_obj, 0, mod_network_ipconfig);
#endif
#if MICROPY_PY_NETWORK_NINAW10
MP_DEFINE_CONST_FUN_OBJ_KW(mod_network_ipconfig_obj, 0, network_ninaw10_ipconfig);
#endif
static const mp_rom_map_elem_t mp_module_network_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) },
{ MP_ROM_QSTR(MP_QSTR_country), MP_ROM_PTR(&mod_network_country_obj) },
{ MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&mod_network_hostname_obj) },
#if LWIP_VERSION_MAJOR >= 2
#if LWIP_VERSION_MAJOR >= 2 || MICROPY_PY_NETWORK_NINAW10
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&mod_network_ipconfig_obj) },
#endif

Wyświetl plik

@ -83,6 +83,10 @@ extern int mp_mod_network_prefer_dns_use_ip_version;
#endif
#elif defined(MICROPY_PORT_NETWORK_INTERFACES)
#if MICROPY_PY_NETWORK_NINAW10
mp_obj_t network_ninaw10_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
#endif
struct _mod_network_socket_obj_t;
typedef struct _mod_network_nic_protocol_t {

Wyświetl plik

@ -297,20 +297,23 @@ mp_obj_t mod_network_nic_ipconfig(struct netif *netif, size_t n_args, const mp_o
char plain_ip[IPADDR_STRLEN_MAX];
char *split = strchr(input_str, '/');
const char *addr_str = input_str;
int to_copy = MIN(sizeof(plain_ip) - 1, addr_len);
memcpy(plain_ip, addr_str, to_copy);
if (split) {
int to_copy = sizeof(plain_ip) - 1;
if (split - addr_str < to_copy) {
to_copy = split - addr_str;
}
memcpy(plain_ip, addr_str, to_copy);
mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL);
prefix_bits = mp_obj_get_int(prefix_obj);
if (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr4) {
uint32_t mask = -(1u << (32 - prefix_bits));
ip_addr_set_ip4_u32_val(netmask, ((mask & 0xFF) << 24) | ((mask & 0xFF00) << 8) | ((mask >> 8) & 0xFF00) | ((mask >> 24) & 0xFF));
}
} else {
netmask = netif->netmask;
}
if (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr4) {
uint32_t mask = -(1u << (32 - prefix_bits));
ip_addr_set_ip4_u32_val(netmask, ((mask & 0xFF) << 24) | ((mask & 0xFF00) << 8) | ((mask >> 8) & 0xFF00) | ((mask >> 24) & 0xFF));
}
if (!ipaddr_aton(addr_str, &ip_addr)) {
plain_ip[to_copy] = '\0';
if (!ipaddr_aton(plain_ip, &ip_addr)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments"));
}
if ((mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr6) != IP_IS_V6(&ip_addr)

Wyświetl plik

@ -41,6 +41,7 @@
#include "py/runtime.h"
#include "py/misc.h"
#include "py/mperrno.h"
#include "py/parsenum.h"
#include "shared/netutils/netutils.h"
#include "shared/runtime/softtimer.h"
#include "extmod/modnetwork.h"
@ -75,6 +76,8 @@ typedef struct _nina_obj_t {
#define SO_NO_CHECK (0x100a)
#define NINAW10_POLL_INTERVAL (100)
#define IPADDR_STRLEN_MAX 46
#define is_nonblocking_error(errno) ((errno) == MP_EAGAIN || (errno) == MP_EWOULDBLOCK || (errno) == MP_EINPROGRESS)
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
@ -87,6 +90,8 @@ static mp_sched_node_t mp_wifi_poll_node;
static soft_timer_entry_t mp_wifi_poll_timer;
static void network_ninaw10_deinit(void);
static bool network_ninaw10_dhcp_active = false;
static bool network_ninaw10_poll_list_is_empty(void) {
return MP_STATE_PORT(mp_wifi_poll_list) == NULL ||
MP_STATE_PORT(mp_wifi_poll_list)->len == 0;
@ -199,6 +204,7 @@ static mp_obj_t network_ninaw10_active(size_t n_args, const mp_obj_t *args) {
mp_raise_msg_varg(&mp_type_OSError,
MP_ERROR_TEXT("failed to initialize Nina-W10 module, error: %d"), error);
}
network_ninaw10_dhcp_active = true;
// check firmware version
uint8_t semver[NINA_FW_VER_LEN];
if (nina_fw_version(semver) != 0) {
@ -367,11 +373,155 @@ static mp_obj_t network_ninaw10_ifconfig(size_t n_args, const mp_obj_t *args) {
netutils_parse_ipv4_addr(items[2], ifconfig.gateway_addr, NETUTILS_BIG);
netutils_parse_ipv4_addr(items[3], ifconfig.dns_addr, NETUTILS_BIG);
nina_ifconfig(&ifconfig, true);
network_ninaw10_dhcp_active = false;
return mp_const_none;
}
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ninaw10_ifconfig_obj, 1, 2, network_ninaw10_ifconfig);
mp_obj_t network_ninaw10_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
nina_ifconfig_t ifconfig;
// get ifconfig info
nina_ifconfig(&ifconfig, false);
if (kwargs->used == 0) {
// Get config value
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[0])) {
case MP_QSTR_dns: {
return netutils_format_ipv4_addr(ifconfig.dns_addr, NETUTILS_BIG);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
} else {
// Set config value(s)
if (n_args != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dns: {
netutils_parse_ipv4_addr(e->value, ifconfig.dns_addr, NETUTILS_BIG);
nina_ifconfig(&ifconfig, true);
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
static mp_obj_t network_ninaw10_nic_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
nina_ifconfig_t ifconfig;
// get ifconfig info
nina_ifconfig(&ifconfig, false);
if (kwargs->used == 0) {
// Get config value
if (n_args != 2) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[1])) {
case MP_QSTR_dhcp4: {
return mp_obj_new_bool(network_ninaw10_dhcp_active);
}
case MP_QSTR_has_dhcp4: {
uint16_t ip_sum =
ifconfig.ip_addr[0] + ifconfig.ip_addr[1] + ifconfig.ip_addr[2] + ifconfig.ip_addr[3];
if (network_ninaw10_dhcp_active) {
return mp_obj_new_bool(ip_sum != 0);
} else {
return mp_const_false;
}
}
case MP_QSTR_addr4: {
mp_obj_t tuple[2] = {
netutils_format_ipv4_addr(ifconfig.ip_addr, NETUTILS_BIG),
netutils_format_ipv4_addr(ifconfig.subnet_addr, NETUTILS_BIG),
};
return mp_obj_new_tuple(2, tuple);
}
case MP_QSTR_gw4: {
return netutils_format_ipv4_addr(ifconfig.gateway_addr, NETUTILS_BIG);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
return mp_const_none;
} else {
// Set config value(s)
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dhcp4: {
mp_raise_ValueError(MP_ERROR_TEXT("DHCP control unsupported"));
break;
}
case MP_QSTR_addr4: {
int prefix_bits = 32;
if (e->value != mp_const_none && mp_obj_is_str(e->value)) {
size_t addr_len;
const char *input_str = mp_obj_str_get_data(e->value, &addr_len);
char *split = strchr(input_str, '/');
if (split) {
mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL);
prefix_bits = mp_obj_get_int(prefix_obj);
uint32_t mask = -(1u << (32 - prefix_bits));
ifconfig.subnet_addr[0] = (mask >> 24) & 0xFF;
ifconfig.subnet_addr[1] = (mask >> 16) & 0xFF;
ifconfig.subnet_addr[2] = (mask >> 8) & 0xFF;
ifconfig.subnet_addr[3] = mask & 0xFF;
}
netutils_parse_ipv4_addr(e->value, ifconfig.ip_addr, NETUTILS_BIG);
} else if (e->value != mp_const_none) {
mp_obj_t *items;
mp_obj_get_array_fixed_n(e->value, 2, &items);
netutils_parse_ipv4_addr(items[0], ifconfig.ip_addr, NETUTILS_BIG);
netutils_parse_ipv4_addr(items[1], ifconfig.subnet_addr, NETUTILS_BIG);
}
nina_ifconfig(&ifconfig, true);
network_ninaw10_dhcp_active = false;
break;
}
case MP_QSTR_gw4: {
netutils_parse_ipv4_addr(e->value, ifconfig.gateway_addr, NETUTILS_BIG);
nina_ifconfig(&ifconfig, true);
network_ninaw10_dhcp_active = false;
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(network_ninaw10_nic_ipconfig_obj, 1, network_ninaw10_nic_ipconfig);
static mp_obj_t network_ninaw10_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
nina_obj_t *self = MP_OBJ_TO_PTR(args[0]);
(void)self;
@ -856,6 +1006,7 @@ static const mp_rom_map_elem_t nina_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ninaw10_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ninaw10_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ninaw10_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ninaw10_nic_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ninaw10_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ninaw10_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&network_ninaw10_ioctl_obj) },

Wyświetl plik

@ -143,6 +143,9 @@ static mp_obj_t network_server_deinit(mp_obj_t self_in) {
static MP_DEFINE_CONST_FUN_OBJ_1(network_server_deinit_obj, network_server_deinit);
#endif
mp_obj_t mod_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
MP_DEFINE_CONST_FUN_OBJ_KW(mod_network_ipconfig_obj, 0, mod_network_ipconfig);
static const mp_rom_map_elem_t mp_module_network_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) },
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_wlan) },
@ -150,6 +153,7 @@ static const mp_rom_map_elem_t mp_module_network_globals_table[] = {
#if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP)
{ MP_ROM_QSTR(MP_QSTR_Server), MP_ROM_PTR(&network_server_type) },
#endif
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&mod_network_ipconfig_obj) },
};
static MP_DEFINE_CONST_DICT(mp_module_network_globals, mp_module_network_globals_table);

Wyświetl plik

@ -32,6 +32,7 @@
#include "py/mpconfig.h"
#include "py/obj.h"
#include "py/objstr.h"
#include "py/parsenum.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "py/mphal.h"
@ -1057,6 +1058,164 @@ static mp_obj_t wlan_ifconfig(size_t n_args, const mp_obj_t *pos_args, mp_map_t
}
static MP_DEFINE_CONST_FUN_OBJ_KW(wlan_ifconfig_obj, 1, wlan_ifconfig);
mp_obj_t mod_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
unsigned char len = sizeof(SlNetCfgIpV4Args_t);
unsigned char dhcpIsOn;
SlNetCfgIpV4Args_t ipV4;
sl_NetCfgGet(SL_IPV4_STA_P2P_CL_GET_INFO, &dhcpIsOn, &len, (uint8_t *)&ipV4);
if (kwargs->used == 0) {
// Get config value
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[0])) {
case MP_QSTR_dns: {
return netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4DnsServer, NETUTILS_LITTLE);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
} else {
// Set config value(s)
if (n_args != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dns: {
netutils_parse_ipv4_addr(e->value, (uint8_t *)&ipV4.ipV4DnsServer, NETUTILS_LITTLE);
sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4);
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
static mp_obj_t wlan_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
unsigned char len = sizeof(SlNetCfgIpV4Args_t);
unsigned char dhcpIsOn;
SlNetCfgIpV4Args_t ipV4;
sl_NetCfgGet(SL_IPV4_STA_P2P_CL_GET_INFO, &dhcpIsOn, &len, (uint8_t *)&ipV4);
if (kwargs->used == 0) {
// Get config value
if (n_args != 2) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[1])) {
case MP_QSTR_dhcp4: {
return mp_obj_new_bool(dhcpIsOn);
}
case MP_QSTR_has_dhcp4: {
return mp_obj_new_bool(dhcpIsOn && ipV4.ipV4 != 0);
}
case MP_QSTR_addr4: {
mp_obj_t tuple[2] = {
netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4, NETUTILS_LITTLE),
netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4Mask, NETUTILS_LITTLE),
};
return mp_obj_new_tuple(2, tuple);
}
case MP_QSTR_gw4: {
return netutils_format_ipv4_addr((uint8_t *)&ipV4.ipV4Gateway, NETUTILS_LITTLE);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
return mp_const_none;
} else {
// Set config value(s)
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dhcp4: {
if (wlan_obj.mode == ROLE_AP) {
if (mp_obj_is_true(e->value)) {
sl_NetCfgSet(SL_IPV4_AP_P2P_GO_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4,
sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4);
SlNetAppDhcpServerBasicOpt_t dhcpParams;
dhcpParams.lease_time = 4096; // lease time (in seconds) of the IP Address
dhcpParams.ipv4_addr_start = ipV4.ipV4 + 1; // first IP Address for allocation.
dhcpParams.ipv4_addr_last = (ipV4.ipV4 & 0xFFFFFF00) + 254; // last IP Address for allocation.
sl_NetAppStop(SL_NET_APP_DHCP_SERVER_ID); // stop DHCP server before settings
sl_NetAppSet(SL_NET_APP_DHCP_SERVER_ID, NETAPP_SET_DHCP_SRV_BASIC_OPT,
sizeof(SlNetAppDhcpServerBasicOpt_t), (_u8* )&dhcpParams); // set parameters
sl_NetAppStart(SL_NET_APP_DHCP_SERVER_ID); // start DHCP server with new settings
} else {
sl_NetAppStop(SL_NET_APP_DHCP_SERVER_ID); // stop DHCP server before settings
}
} else {
_u8 val = 1;
if (mp_obj_is_true(e->value)) {
sl_NetCfgSet(SL_IPV4_STA_P2P_CL_DHCP_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, 1, &val);
} else {
sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4,
sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4);
}
}
break;
}
case MP_QSTR_addr4: {
int prefix_bits = 32;
if (e->value != mp_const_none && mp_obj_is_str(e->value)) {
size_t addr_len;
const char *input_str = mp_obj_str_get_data(e->value, &addr_len);
char *split = strchr(input_str, '/');
if (split) {
mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL);
prefix_bits = mp_obj_get_int(prefix_obj);
ipV4.ipV4Mask = -(1u << (32 - prefix_bits));
}
netutils_parse_ipv4_addr(e->value, (uint8_t *)&ipV4.ipV4, NETUTILS_LITTLE);
} else if (e->value != mp_const_none) {
mp_obj_t *items;
mp_obj_get_array_fixed_n(e->value, 2, &items);
netutils_parse_ipv4_addr(items[0], (uint8_t *)&ipV4.ipV4, NETUTILS_LITTLE);
netutils_parse_ipv4_addr(items[1], (uint8_t *)&ipV4.ipV4Mask, NETUTILS_LITTLE);
}
ASSERT_ON_ERROR(sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4));
break;
}
case MP_QSTR_gw4: {
netutils_parse_ipv4_addr(e->value, (uint8_t *)&ipV4.ipV4Gateway, NETUTILS_LITTLE);
sl_NetCfgSet(SL_IPV4_STA_P2P_CL_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4);
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(wlan_ipconfig_obj, 1, wlan_ipconfig);
static mp_obj_t wlan_mode(size_t n_args, const mp_obj_t *args) {
wlan_obj_t *self = args[0];
if (n_args == 1) {
@ -1260,6 +1419,7 @@ static const mp_rom_map_elem_t wlan_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&wlan_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&wlan_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wlan_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&wlan_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&wlan_mode_obj) },
{ MP_ROM_QSTR(MP_QSTR_ssid), MP_ROM_PTR(&wlan_ssid_obj) },
{ MP_ROM_QSTR(MP_QSTR_auth), MP_ROM_PTR(&wlan_auth_obj) },

Wyświetl plik

@ -54,6 +54,8 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj);
MP_DECLARE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_config_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj);

Wyświetl plik

@ -11,6 +11,7 @@
{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_make_new_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) },
#if MICROPY_PY_NETWORK_WLAN
{ MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(WIFI_IF_STA)},

Wyświetl plik

@ -34,6 +34,7 @@
#include <string.h>
#include "py/runtime.h"
#include "py/parsenum.h"
#include "py/mperrno.h"
#include "shared/netutils/netutils.h"
#include "modnetwork.h"
@ -42,7 +43,7 @@
#include "esp_netif.h"
#include "esp_wifi.h"
#include "lwip/sockets.h"
// #include "lwip/dns.h"
#include "lwip/dns.h"
NORETURN void esp_exceptions_helper(esp_err_t e) {
switch (e) {
@ -154,6 +155,176 @@ static mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) {
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj, 1, 2, esp_ifconfig);
static mp_obj_t esp_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (kwargs->used == 0) {
// Get config value
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[0])) {
case MP_QSTR_dns: {
char addr_str[IPADDR_STRLEN_MAX];
ipaddr_ntoa_r(dns_getserver(0), addr_str, sizeof(addr_str));
return mp_obj_new_str(addr_str, strlen(addr_str));
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
} else {
// Set config value(s)
if (n_args != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dns: {
ip_addr_t dns;
size_t addr_len;
const char *addr_str = mp_obj_str_get_data(e->value, &addr_len);
if (!ipaddr_aton(addr_str, &dns)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments as dns server"));
}
dns_setserver(0, &dns);
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj, 0, esp_network_ipconfig);
static mp_obj_t esp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
base_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
esp_netif_ip_info_t info;
esp_netif_get_ip_info(self->netif, &info);
if (kwargs->used == 0) {
// Get config value
if (n_args != 2) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[1])) {
case MP_QSTR_dhcp4: {
if (self->if_id == ESP_IF_WIFI_STA || self->if_id == ESP_IF_ETH) {
esp_netif_dhcp_status_t status;
esp_exceptions(esp_netif_dhcpc_get_status(self->netif, &status));
return mp_obj_new_bool(status == ESP_NETIF_DHCP_STARTED);
} else {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
case MP_QSTR_addr4: {
mp_obj_t tuple[2] = {
netutils_format_ipv4_addr((uint8_t *)&info.ip, NETUTILS_BIG),
netutils_format_ipv4_addr((uint8_t *)&info.netmask, NETUTILS_BIG),
};
return mp_obj_new_tuple(2, tuple);
}
case MP_QSTR_gw4: {
return netutils_format_ipv4_addr((uint8_t *)&info.gw, NETUTILS_BIG);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
return mp_const_none;
} else {
// Set config value(s)
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
int touched_ip_info = 0;
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dhcp4: {
esp_netif_dhcp_status_t status;
if (self->if_id == ESP_IF_WIFI_STA || self->if_id == ESP_IF_ETH) {
esp_exceptions(esp_netif_dhcpc_get_status(self->netif, &status));
if (mp_obj_is_true(e->value) && status != ESP_NETIF_DHCP_STARTED) {
esp_exceptions(esp_netif_dhcpc_start(self->netif));
} else if (!mp_obj_is_true(e->value) && status == ESP_NETIF_DHCP_STARTED) {
esp_exceptions(esp_netif_dhcpc_stop(self->netif));
}
} else {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
break;
}
case MP_QSTR_addr4: {
if (e->value != mp_const_none && mp_obj_is_str(e->value)) {
size_t addr_len;
const char *input_str = mp_obj_str_get_data(e->value, &addr_len);
char *split = strchr(input_str, '/');
if (split) {
mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL);
int prefix_bits = mp_obj_get_int(prefix_obj);
uint32_t mask = -(1u << (32 - prefix_bits));
uint32_t *m = (uint32_t *)&info.netmask;
*m = esp_netif_htonl(mask);
}
netutils_parse_ipv4_addr(e->value, (void *)&info.ip, NETUTILS_BIG);
} else if (e->value != mp_const_none) {
mp_obj_t *items;
mp_obj_get_array_fixed_n(e->value, 2, &items);
netutils_parse_ipv4_addr(items[0], (void *)&info.ip, NETUTILS_BIG);
netutils_parse_ipv4_addr(items[1], (void *)&info.netmask, NETUTILS_BIG);
}
touched_ip_info = 1;
break;
}
case MP_QSTR_gw4: {
netutils_parse_ipv4_addr(e->value, (void *)&info.gw, NETUTILS_BIG);
touched_ip_info = 1;
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
if (self->if_id == ESP_IF_WIFI_STA || self->if_id == ESP_IF_ETH) {
if (touched_ip_info) {
esp_err_t e = esp_netif_dhcpc_stop(self->netif);
if (e != ESP_OK && e != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
esp_exceptions_helper(e);
}
esp_exceptions(esp_netif_set_ip_info(self->netif, &info));
}
} else if (self->if_id == ESP_IF_WIFI_AP) {
esp_err_t e = esp_netif_dhcps_stop(self->netif);
if (e != ESP_OK && e != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
esp_exceptions_helper(e);
}
esp_exceptions(esp_netif_set_ip_info(self->netif, &info));
esp_exceptions(esp_netif_dhcps_start(self->netif));
}
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj, 1, esp_ipconfig);
mp_obj_t esp_ifname(esp_netif_t *netif) {
char ifname[NETIF_NAMESIZE + 1] = {0};
mp_obj_t ret = mp_const_none;

Wyświetl plik

@ -404,6 +404,7 @@ static const mp_rom_map_elem_t lan_if_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&lan_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&lan_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_network_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_nic_ipconfig_obj) },
};
static MP_DEFINE_CONST_DICT(lan_if_locals_dict, lan_if_locals_dict_table);

Wyświetl plik

@ -266,6 +266,43 @@ static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig);
static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (kwargs->used == 0) {
ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (self->pcb == NULL) {
mp_raise_ValueError(MP_ERROR_TEXT("PPP not active"));
}
struct netif *netif = ppp_netif(self->pcb);
// Get config value
if (n_args != 2) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[1])) {
case MP_QSTR_addr4: {
mp_obj_t tuple[2] = {
netutils_format_ipv4_addr((uint8_t *)&netif->ip_addr, NETUTILS_BIG),
netutils_format_ipv4_addr((uint8_t *)&netif->netmask, NETUTILS_BIG),
};
return mp_obj_new_tuple(2, tuple);
}
case MP_QSTR_gw4: {
return netutils_format_ipv4_addr((uint8_t *)&netif->gw, NETUTILS_BIG);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
return mp_const_none;
} else {
mp_raise_TypeError(MP_ERROR_TEXT("setting properties not supported"));
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(ppp_ipconfig_obj, 1, ppp_ipconfig);
static mp_obj_t ppp_status(mp_obj_t self_in) {
return mp_const_none;
}
@ -328,6 +365,7 @@ static const mp_rom_map_elem_t ppp_if_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&ppp_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&ppp_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) },

Wyświetl plik

@ -728,6 +728,7 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_wlan_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_wlan_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_network_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_nic_ipconfig_obj) },
// Constants
{ MP_ROM_QSTR(MP_QSTR_IF_STA), MP_ROM_INT(WIFI_IF_STA)},

Wyświetl plik

@ -1,3 +1,4 @@
extern const mp_obj_type_t esp_network_wlan_type;
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj);

Wyświetl plik

@ -1,5 +1,6 @@
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&esp_network_wlan_type) },
{ MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(STATION_IF)},
{ MP_ROM_QSTR(MP_QSTR_AP_IF), MP_ROM_INT(SOFTAP_IF)},

Wyświetl plik

@ -31,6 +31,7 @@
#include "py/objlist.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "py/parsenum.h"
#include "extmod/modnetwork.h"
#include "shared/netutils/netutils.h"
#include "queue.h"
@ -41,6 +42,8 @@
#include "lwip/dns.h"
#include "modnetwork.h"
#define IPADDR_STRLEN_MAX (20)
typedef struct _wlan_if_obj_t {
mp_obj_base_t base;
int if_id;
@ -330,6 +333,174 @@ static mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) {
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj, 1, 2, esp_ifconfig);
static mp_obj_t esp_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (kwargs->used == 0) {
// Get config value
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[0])) {
case MP_QSTR_dns: {
char addr_str[IPADDR_STRLEN_MAX];
ip_addr_t dns_addr = dns_getserver(0);
ipaddr_ntoa_r(&dns_addr, addr_str, sizeof(addr_str));
return mp_obj_new_str(addr_str, strlen(addr_str));
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
} else {
// Set config value(s)
if (n_args != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dns: {
ip_addr_t dns;
size_t addr_len;
const char *addr_str = mp_obj_str_get_data(e->value, &addr_len);
if (!ipaddr_aton(addr_str, &dns)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments as dns server"));
}
dns_setserver(0, &dns);
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj, 0, esp_network_ipconfig);
static mp_obj_t esp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
struct ip_info info;
wifi_get_ip_info(self->if_id, &info);
if (kwargs->used == 0) {
// Get config value
if (n_args != 2) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[1])) {
case MP_QSTR_dhcp4: {
if (self->if_id == STATION_IF) {
return mp_obj_new_bool(wifi_station_dhcpc_status() == DHCP_STARTED);
} else if (self->if_id == SOFTAP_IF) {
return mp_obj_new_bool(wifi_softap_dhcps_status() == DHCP_STARTED);
} else {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
case MP_QSTR_addr4: {
mp_obj_t tuple[2] = {
netutils_format_ipv4_addr((uint8_t *)&info.ip, NETUTILS_BIG),
netutils_format_ipv4_addr((uint8_t *)&info.netmask, NETUTILS_BIG),
};
return mp_obj_new_tuple(2, tuple);
}
case MP_QSTR_gw4: {
return netutils_format_ipv4_addr((uint8_t *)&info.gw, NETUTILS_BIG);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
return mp_const_none;
} else {
// Set config value(s)
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
int touched_ip_info = 0;
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dhcp4: {
if (self->if_id == STATION_IF) {
enum dhcp_status status = wifi_station_dhcpc_status();
if (mp_obj_is_true(e->value) && status != DHCP_STARTED) {
wifi_station_dhcpc_start();
} else if (!mp_obj_is_true(e->value) && status == DHCP_STARTED) {
wifi_station_dhcpc_stop();
}
} else {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
break;
}
case MP_QSTR_addr4: {
if (e->value != mp_const_none && mp_obj_is_str(e->value)) {
size_t addr_len;
const char *input_str = mp_obj_str_get_data(e->value, &addr_len);
char *split = strchr(input_str, '/');
if (split) {
mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL);
int prefix_bits = mp_obj_get_int(prefix_obj);
uint32_t mask = -(1u << (32 - prefix_bits));
uint32_t *m = (uint32_t *)&info.netmask;
*m = htonl(mask);
}
netutils_parse_ipv4_addr(e->value, (void *)&info.ip, NETUTILS_BIG);
} else if (e->value != mp_const_none) {
mp_obj_t *items;
mp_obj_get_array_fixed_n(e->value, 2, &items);
netutils_parse_ipv4_addr(items[0], (void *)&info.ip, NETUTILS_BIG);
netutils_parse_ipv4_addr(items[1], (void *)&info.netmask, NETUTILS_BIG);
}
touched_ip_info = 1;
break;
}
case MP_QSTR_gw4: {
netutils_parse_ipv4_addr(e->value, (void *)&info.gw, NETUTILS_BIG);
touched_ip_info = 1;
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
bool restart_dhcp_server = false;
if (self->if_id == STATION_IF) {
if (touched_ip_info) {
wifi_station_dhcpc_stop();
wifi_set_ip_info(self->if_id, &info);
}
} else {
restart_dhcp_server = wifi_softap_dhcps_status();
wifi_softap_dhcps_stop();
wifi_set_ip_info(self->if_id, &info);
}
if (restart_dhcp_server) {
wifi_softap_dhcps_start();
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj, 1, esp_ipconfig);
static mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (n_args != 1 && kwargs->used != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
@ -514,6 +685,7 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&esp_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&esp_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_nic_ipconfig_obj) },
// Constants
{ MP_ROM_QSTR(MP_QSTR_IF_STA), MP_ROM_INT(STATION_IF)},

Wyświetl plik

@ -179,6 +179,12 @@ static mp_obj_t network_lan_ifconfig(size_t n_args, const mp_obj_t *args) {
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_ifconfig_obj, 1, 2, network_lan_ifconfig);
static mp_obj_t network_lan_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]);
return mod_network_nic_ipconfig(eth_netif(self->eth), n_args - 1, args + 1, kwargs);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(network_lan_ipconfig_obj, 1, network_lan_ipconfig);
static mp_obj_t network_lan_status(size_t n_args, const mp_obj_t *args) {
network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]);
(void)self;
@ -241,6 +247,7 @@ static const mp_rom_map_elem_t network_lan_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_lan_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_lan_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_lan_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_lan_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_lan_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_lan_config_obj) },

Wyświetl plik

@ -59,6 +59,12 @@
// options to control how MicroPython is built
// Due to the use of LTO and the unknown distance between nlr.o and nlrthumb.o code,
// MCUs using the Thumb 1 instruction set must enable this NLR long jump feature.
#if defined(NRF51822)
#define MICROPY_NLR_THUMB_USE_LONG_JUMP (1)
#endif
#ifndef MICROPY_VFS
#define MICROPY_VFS (CORE_FEAT)
#endif

Wyświetl plik

@ -101,6 +101,12 @@ static mp_obj_t network_lan_ifconfig(size_t n_args, const mp_obj_t *args) {
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_ifconfig_obj, 1, 2, network_lan_ifconfig);
static mp_obj_t network_lan_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]);
return mod_network_nic_ipconfig(eth_netif(self->eth), n_args - 1, args + 1, kwargs);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(network_lan_ipconfig_obj, 1, network_lan_ipconfig);
static mp_obj_t network_lan_status(size_t n_args, const mp_obj_t *args) {
network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]);
(void)self;
@ -163,6 +169,7 @@ static const mp_rom_map_elem_t network_lan_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_lan_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_lan_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_lan_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_lan_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_lan_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_lan_config_obj) },

Wyświetl plik

@ -22,6 +22,9 @@ BUILD ?= build-$(VARIANT)
include ../../py/mkenv.mk
include $(VARIANT_DIR)/mpconfigvariant.mk
# Use the default frozen manifest, variants may override this.
FROZEN_MANIFEST ?= variants/manifest.py
# Qstr definitions (must come before including py.mk).
QSTR_DEFS = qstrdefsport.h

Wyświetl plik

@ -38,7 +38,7 @@ export async function loadMicroPython(options) {
{ heapsize: 1024 * 1024, linebuffer: true },
options,
);
const Module = {};
let Module = {};
Module.locateFile = (path, scriptDirectory) =>
url || scriptDirectory + path;
Module._textDecoder = new TextDecoder();
@ -83,11 +83,7 @@ export async function loadMicroPython(options) {
Module.stderr = (c) => stderr(new Uint8Array([c]));
}
}
const moduleLoaded = new Promise((r) => {
Module.postRun = r;
});
_createMicroPythonModule(Module);
await moduleLoaded;
Module = await _createMicroPythonModule(Module);
globalThis.Module = Module;
proxy_js_init();
const pyimport = (name) => {
@ -131,23 +127,31 @@ export async function loadMicroPython(options) {
},
pyimport: pyimport,
runPython(code) {
const len = Module.lengthBytesUTF8(code);
const buf = Module._malloc(len + 1);
Module.stringToUTF8(code, buf, len + 1);
const value = Module._malloc(3 * 4);
Module.ccall(
"mp_js_do_exec",
"number",
["string", "pointer"],
[code, value],
["pointer", "number", "pointer"],
[buf, len, value],
);
Module._free(buf);
return proxy_convert_mp_to_js_obj_jsside_with_free(value);
},
runPythonAsync(code) {
const len = Module.lengthBytesUTF8(code);
const buf = Module._malloc(len + 1);
Module.stringToUTF8(code, buf, len + 1);
const value = Module._malloc(3 * 4);
Module.ccall(
"mp_js_do_exec_async",
"number",
["string", "pointer"],
[code, value],
["pointer", "number", "pointer"],
[buf, len, value],
);
Module._free(buf);
return proxy_convert_mp_to_js_obj_jsside_with_free(value);
},
replInit() {
@ -224,6 +228,16 @@ async function runCLI() {
}
});
} else {
// If the script to run ends with a running of the asyncio main loop, then inject
// a simple `asyncio.run` hook that starts the main task. This is primarily to
// support running the standard asyncio tests.
if (contents.endsWith("asyncio.run(main())\n")) {
const asyncio = mp.pyimport("asyncio");
asyncio.run = async (task) => {
await asyncio.create_task(task);
};
}
try {
mp.runPython(contents);
} catch (error) {

Wyświetl plik

@ -0,0 +1,9 @@
# MicroPython asyncio module, for use with webassembly port
# MIT license; Copyright (c) 2024 Damien P. George
from .core import *
from .funcs import wait_for, wait_for_ms, gather
from .event import Event
from .lock import Lock
__version__ = (3, 0, 0)

Wyświetl plik

@ -0,0 +1,249 @@
# MicroPython asyncio module, for use with webassembly port
# MIT license; Copyright (c) 2019-2024 Damien P. George
from time import ticks_ms as ticks, ticks_diff, ticks_add
import sys, js, jsffi
# Import TaskQueue and Task from built-in C code.
from _asyncio import TaskQueue, Task
################################################################################
# Exceptions
class CancelledError(BaseException):
pass
class TimeoutError(Exception):
pass
# Used when calling Loop.call_exception_handler.
_exc_context = {"message": "Task exception wasn't retrieved", "exception": None, "future": None}
################################################################################
# Sleep functions
# "Yield" once, then raise StopIteration
class SingletonGenerator:
def __init__(self):
self.state = None
self.exc = StopIteration()
def __iter__(self):
return self
def __next__(self):
if self.state is not None:
_task_queue.push(cur_task, self.state)
self.state = None
return None
else:
self.exc.__traceback__ = None
raise self.exc
# Pause task execution for the given time (integer in milliseconds, uPy extension)
# Use a SingletonGenerator to do it without allocating on the heap
def sleep_ms(t, sgen=SingletonGenerator()):
if cur_task is None:
# Support top-level asyncio.sleep, via a JavaScript Promise.
return jsffi.async_timeout_ms(t)
assert sgen.state is None
sgen.state = ticks_add(ticks(), max(0, t))
return sgen
# Pause task execution for the given time (in seconds)
def sleep(t):
return sleep_ms(int(t * 1000))
################################################################################
# Main run loop
asyncio_timer = None
class ThenableEvent:
def __init__(self, thenable):
self.result = None # Result of the thenable
self.waiting = None # Task waiting on completion of this thenable
thenable.then(self.set)
def set(self, value):
# Thenable/Promise is fulfilled, set result and schedule any waiting task.
self.result = value
if self.waiting:
_task_queue.push(self.waiting)
self.waiting = None
_schedule_run_iter(0)
def remove(self, task):
self.waiting = None
# async
def wait(self):
# Set the calling task as the task waiting on this thenable.
self.waiting = cur_task
# Set calling task's data to this object so it can be removed if needed.
cur_task.data = self
# Wait for the thenable to fulfill.
yield
# Return the result of the thenable.
return self.result
# Ensure the awaitable is a task
def _promote_to_task(aw):
return aw if isinstance(aw, Task) else create_task(aw)
def _schedule_run_iter(dt):
global asyncio_timer
if asyncio_timer is not None:
js.clearTimeout(asyncio_timer)
asyncio_timer = js.setTimeout(_run_iter, dt)
def _run_iter():
global cur_task
excs_all = (CancelledError, Exception) # To prevent heap allocation in loop
excs_stop = (CancelledError, StopIteration) # To prevent heap allocation in loop
while True:
# Wait until the head of _task_queue is ready to run
t = _task_queue.peek()
if t:
# A task waiting on _task_queue; "ph_key" is time to schedule task at
dt = max(0, ticks_diff(t.ph_key, ticks()))
else:
# No tasks can be woken so finished running
cur_task = None
return
if dt > 0:
# schedule to call again later
cur_task = None
_schedule_run_iter(dt)
return
# Get next task to run and continue it
t = _task_queue.pop()
cur_task = t
try:
# Continue running the coroutine, it's responsible for rescheduling itself
exc = t.data
if not exc:
t.coro.send(None)
else:
# If the task is finished and on the run queue and gets here, then it
# had an exception and was not await'ed on. Throwing into it now will
# raise StopIteration and the code below will catch this and run the
# call_exception_handler function.
t.data = None
t.coro.throw(exc)
except excs_all as er:
# Check the task is not on any event queue
assert t.data is None
# This task is done.
if t.state:
# Task was running but is now finished.
waiting = False
if t.state is True:
# "None" indicates that the task is complete and not await'ed on (yet).
t.state = None
elif callable(t.state):
# The task has a callback registered to be called on completion.
t.state(t, er)
t.state = False
waiting = True
else:
# Schedule any other tasks waiting on the completion of this task.
while t.state.peek():
_task_queue.push(t.state.pop())
waiting = True
# "False" indicates that the task is complete and has been await'ed on.
t.state = False
if not waiting and not isinstance(er, excs_stop):
# An exception ended this detached task, so queue it for later
# execution to handle the uncaught exception if no other task retrieves
# the exception in the meantime (this is handled by Task.throw).
_task_queue.push(t)
# Save return value of coro to pass up to caller.
t.data = er
elif t.state is None:
# Task is already finished and nothing await'ed on the task,
# so call the exception handler.
# Save exception raised by the coro for later use.
t.data = exc
# Create exception context and call the exception handler.
_exc_context["exception"] = exc
_exc_context["future"] = t
Loop.call_exception_handler(_exc_context)
# Create and schedule a new task from a coroutine.
def create_task(coro):
if not hasattr(coro, "send"):
raise TypeError("coroutine expected")
t = Task(coro, globals())
_task_queue.push(t)
_schedule_run_iter(0)
return t
################################################################################
# Event loop wrapper
cur_task = None
class Loop:
_exc_handler = None
def create_task(coro):
return create_task(coro)
def close():
pass
def set_exception_handler(handler):
Loop._exc_handler = handler
def get_exception_handler():
return Loop._exc_handler
def default_exception_handler(loop, context):
print(context["message"], file=sys.stderr)
print("future:", context["future"], "coro=", context["future"].coro, file=sys.stderr)
sys.print_exception(context["exception"], sys.stderr)
def call_exception_handler(context):
(Loop._exc_handler or Loop.default_exception_handler)(Loop, context)
def get_event_loop():
return Loop
def current_task():
if cur_task is None:
raise RuntimeError("no running event loop")
return cur_task
def new_event_loop():
global _task_queue
_task_queue = TaskQueue() # TaskQueue of Task instances.
return Loop
# Initialise default event loop.
new_event_loop()

Wyświetl plik

@ -104,7 +104,7 @@ void mp_js_do_import(const char *name, uint32_t *out) {
}
}
void mp_js_do_exec(const char *src, uint32_t *out) {
void mp_js_do_exec(const char *src, size_t len, uint32_t *out) {
// Collect at the top-level, where there are no root pointers from stack/registers.
gc_collect_start();
gc_collect_end();
@ -112,7 +112,7 @@ void mp_js_do_exec(const char *src, uint32_t *out) {
mp_parse_input_kind_t input_kind = MP_PARSE_FILE_INPUT;
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_lexer_t *lex = mp_lexer_new_from_str_len_dedent(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
mp_lexer_t *lex = mp_lexer_new_from_str_len_dedent(MP_QSTR__lt_stdin_gt_, src, len, 0);
qstr source_name = lex->source_name;
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, false);
@ -125,9 +125,9 @@ void mp_js_do_exec(const char *src, uint32_t *out) {
}
}
void mp_js_do_exec_async(const char *src, uint32_t *out) {
void mp_js_do_exec_async(const char *src, size_t len, uint32_t *out) {
mp_compile_allow_top_level_await = true;
mp_js_do_exec(src, out);
mp_js_do_exec(src, len, out);
mp_compile_allow_top_level_await = false;
}

Wyświetl plik

@ -61,12 +61,27 @@ static mp_obj_t mp_jsffi_to_js(mp_obj_t arg) {
}
static MP_DEFINE_CONST_FUN_OBJ_1(mp_jsffi_to_js_obj, mp_jsffi_to_js);
// *FORMAT-OFF*
EM_JS(void, promise_with_timeout_ms, (double ms, uint32_t * out), {
const ret = new Promise((resolve) => setTimeout(resolve, ms));
proxy_convert_js_to_mp_obj_jsside(ret, out);
});
// *FORMAT-ON*
static mp_obj_t mp_jsffi_async_timeout_ms(mp_obj_t arg) {
uint32_t out[PVN];
promise_with_timeout_ms(mp_obj_get_float_to_d(arg), out);
return proxy_convert_js_to_mp_obj_cside(out);
}
static MP_DEFINE_CONST_FUN_OBJ_1(mp_jsffi_async_timeout_ms_obj, mp_jsffi_async_timeout_ms);
static const mp_rom_map_elem_t mp_module_jsffi_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_jsffi) },
{ MP_ROM_QSTR(MP_QSTR_JsProxy), MP_ROM_PTR(&mp_type_jsproxy) },
{ MP_ROM_QSTR(MP_QSTR_create_proxy), MP_ROM_PTR(&mp_jsffi_create_proxy_obj) },
{ MP_ROM_QSTR(MP_QSTR_to_js), MP_ROM_PTR(&mp_jsffi_to_js_obj) },
{ MP_ROM_QSTR(MP_QSTR_async_timeout_ms), MP_ROM_PTR(&mp_jsffi_async_timeout_ms_obj) },
};
static MP_DEFINE_CONST_DICT(mp_module_jsffi_globals, mp_module_jsffi_globals_table);

Wyświetl plik

@ -346,6 +346,12 @@ typedef struct _jsproxy_gen_t {
mp_vm_return_kind_t jsproxy_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) {
jsproxy_gen_t *self = MP_OBJ_TO_PTR(self_in);
if (throw_value) {
*ret_val = throw_value;
return MP_VM_RETURN_EXCEPTION;
}
switch (self->state) {
case JSOBJ_GEN_STATE_WAITING:
self->state = JSOBJ_GEN_STATE_COMPLETED;
@ -468,9 +474,29 @@ static mp_obj_t jsproxy_new_gen(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
/******************************************************************************/
#if MICROPY_PY_ASYNCIO
extern mp_obj_t mp_asyncio_context;
#endif
static mp_obj_t jsproxy_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
mp_obj_jsproxy_t *self = MP_OBJ_TO_PTR(self_in);
if (has_attr(self->ref, "then")) {
#if MICROPY_PY_ASYNCIO
// When asyncio is running and the caller here is a task, wrap the JavaScript
// thenable in a ThenableEvent, and get the task to wait on that event. This
// decouples the task from the thenable and allows cancelling the task.
if (mp_asyncio_context != MP_OBJ_NULL) {
mp_obj_t cur_task = mp_obj_dict_get(mp_asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
if (cur_task != mp_const_none) {
mp_obj_t thenable_event_class = mp_obj_dict_get(mp_asyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_ThenableEvent));
mp_obj_t thenable_event = mp_call_function_1(thenable_event_class, self_in);
mp_obj_t dest[2];
mp_load_method(thenable_event, MP_QSTR_wait, dest);
mp_obj_t wait_gen = mp_call_method_n_kw(0, 0, dest);
return mp_getiter(wait_gen, iter_buf);
}
}
#endif
return jsproxy_new_gen(self_in, iter_buf);
} else {
return jsproxy_new_it(self_in, iter_buf);

Wyświetl plik

@ -296,10 +296,11 @@ EM_JS(void, js_then_resolve, (uint32_t * ret_value, uint32_t * resolve, uint32_t
resolve_js(ret_value_js);
});
EM_JS(void, js_then_reject, (uint32_t * resolve, uint32_t * reject), {
EM_JS(void, js_then_reject, (uint32_t * ret_value, uint32_t * resolve, uint32_t * reject), {
const ret_value_js = proxy_convert_mp_to_js_obj_jsside(ret_value);
const resolve_js = proxy_convert_mp_to_js_obj_jsside(resolve);
const reject_js = proxy_convert_mp_to_js_obj_jsside(reject);
reject_js(null);
reject_js(ret_value_js);
});
// *FORMAT-OFF*
@ -307,14 +308,34 @@ EM_JS(void, js_then_continue, (int jsref, uint32_t * py_resume, uint32_t * resol
const py_resume_js = proxy_convert_mp_to_js_obj_jsside(py_resume);
const resolve_js = proxy_convert_mp_to_js_obj_jsside(resolve);
const reject_js = proxy_convert_mp_to_js_obj_jsside(reject);
const ret = proxy_js_ref[jsref].then((x) => {py_resume_js(x, resolve_js, reject_js);}, reject_js);
const ret = proxy_js_ref[jsref].then(
(result) => {
// The Promise is fulfilled on the JavaScript side. Take the result and
// send it to the encapsulating generator on the Python side, so it
// becomes the result of the "yield from" that deferred to this Promise.
py_resume_js(result, null, resolve_js, reject_js);
},
(reason) => {
// The Promise is rejected on the JavaScript side. Take the reason and
// throw it into the encapsulating generator on the Python side.
py_resume_js(null, reason, resolve_js, reject_js);
},
);
proxy_convert_js_to_mp_obj_jsside(ret, out);
});
// *FORMAT-ON*
static mp_obj_t proxy_resume_execute(mp_obj_t self_in, mp_obj_t value, mp_obj_t resolve, mp_obj_t reject) {
static mp_obj_t proxy_resume_execute(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t resolve, mp_obj_t reject) {
if (throw_value != MP_OBJ_NULL && throw_value != mp_const_none) {
if (send_value == mp_const_none) {
send_value = MP_OBJ_NULL;
}
} else {
throw_value = MP_OBJ_NULL;
}
mp_obj_t ret_value;
mp_vm_return_kind_t ret_kind = mp_resume(self_in, value, MP_OBJ_NULL, &ret_value);
mp_vm_return_kind_t ret_kind = mp_resume(self_in, send_value, throw_value, &ret_value);
uint32_t out_resolve[PVN];
uint32_t out_reject[PVN];
@ -335,17 +356,19 @@ static mp_obj_t proxy_resume_execute(mp_obj_t self_in, mp_obj_t value, mp_obj_t
uint32_t out[PVN];
js_then_continue(ref, out_py_resume, out_resolve, out_reject, out);
return proxy_convert_js_to_mp_obj_cside(out);
} else {
// MP_VM_RETURN_EXCEPTION;
js_then_reject(out_resolve, out_reject);
nlr_raise(ret_value);
} else { // ret_kind == MP_VM_RETURN_EXCEPTION;
// Pass the exception through as an object to reject the promise (don't raise/throw it).
uint32_t out_ret_value[PVN];
proxy_convert_mp_to_js_obj_cside(ret_value, out_ret_value);
js_then_reject(out_ret_value, out_resolve, out_reject);
return mp_const_none;
}
}
static mp_obj_t resume_fun(size_t n_args, const mp_obj_t *args) {
return proxy_resume_execute(args[0], args[1], args[2], args[3]);
return proxy_resume_execute(args[0], args[1], args[2], args[3], args[4]);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(resume_obj, 4, 4, resume_fun);
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(resume_obj, 5, 5, resume_fun);
void proxy_c_to_js_resume(uint32_t c_ref, uint32_t *args) {
nlr_buf_t nlr;
@ -353,7 +376,7 @@ void proxy_c_to_js_resume(uint32_t c_ref, uint32_t *args) {
mp_obj_t obj = proxy_c_get_obj(c_ref);
mp_obj_t resolve = proxy_convert_js_to_mp_obj_cside(args + 1 * 3);
mp_obj_t reject = proxy_convert_js_to_mp_obj_cside(args + 2 * 3);
mp_obj_t ret = proxy_resume_execute(obj, mp_const_none, resolve, reject);
mp_obj_t ret = proxy_resume_execute(obj, mp_const_none, mp_const_none, resolve, reject);
nlr_pop();
return proxy_convert_mp_to_js_obj_cside(ret, args);
} else {

Wyświetl plik

@ -0,0 +1,24 @@
# The asyncio package is built from the standard implementation but with the
# core scheduler replaced with a custom scheduler that uses the JavaScript
# runtime (with setTimeout an Promise's) to contrtol the scheduling.
package(
"asyncio",
(
"event.py",
"funcs.py",
"lock.py",
),
base_path="$(MPY_DIR)/extmod",
opt=3,
)
package(
"asyncio",
(
"__init__.py",
"core.py",
),
base_path="$(PORT_DIR)",
opt=3,
)

Wyświetl plik

@ -1,3 +1,5 @@
include("$(PORT_DIR)/variants/manifest.py")
require("abc")
require("base64")
require("collections")

Wyświetl plik

@ -587,6 +587,12 @@
/*****************************************************************************/
/* Python internal features */
// Use a special long jump in nlrthumb.c, which may be necessary if nlr.o and
// nlrthumb.o are linked far apart from each other.
#ifndef MICROPY_NLR_THUMB_USE_LONG_JUMP
#define MICROPY_NLR_THUMB_USE_LONG_JUMP (0)
#endif
// Whether to enable import of external modules
// When disabled, only importing of built-in modules is supported
// When enabled, a port must implement mp_import_stat (among other things)

Wyświetl plik

@ -38,6 +38,14 @@
__attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
// If you get a linker error here, indicating that a relocation doesn't
// fit, try the following (in that order):
//
// 1. Ensure that nlr.o nlrthumb.o are linked closely together, i.e.
// there aren't too many other files between them in the linker list
// (PY_CORE_O_BASENAME in py/py.mk)
// 2. Set -DMICROPY_NLR_THUMB_USE_LONG_JUMP=1 during the build
//
__asm volatile (
"str r4, [r0, #12] \n" // store r4 into nlr_buf
"str r5, [r0, #16] \n" // store r5 into nlr_buf
@ -71,7 +79,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
"str lr, [r0, #8] \n" // store lr into nlr_buf
#endif
#if !defined(__thumb2__)
#if MICROPY_NLR_THUMB_USE_LONG_JUMP
"ldr r1, nlr_push_tail_var \n"
"bx r1 \n" // do the rest in C
".align 2 \n"

Wyświetl plik

@ -63,7 +63,13 @@ void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian
return;
}
const char *s = addr_str;
const char *s_top = addr_str + addr_len;
const char *s_top;
// Scan for the end of valid address characters
for (s_top = addr_str; s_top < addr_str + addr_len; s_top++) {
if (!(*s_top == '.' || (*s_top >= '0' && *s_top <= '9'))) {
break;
}
}
for (mp_uint_t i = 3; ; i--) {
mp_uint_t val = 0;
for (; s < s_top && *s != '.'; s++) {

Wyświetl plik

@ -0,0 +1,44 @@
// Test asyncio.create_task(), and tasks waiting on a Promise.
const mp = await (await import(process.argv[2])).loadMicroPython();
globalThis.p0 = new Promise((resolve, reject) => {
resolve(123);
});
globalThis.p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("setTimeout resolved");
resolve(456);
}, 200);
});
mp.runPython(`
import js
import asyncio
async def task(id, promise):
print("task start", id)
print("task await", id, await promise)
print("task await", id, await promise)
print("task end", id)
print("start")
t1 = asyncio.create_task(task(1, js.p0))
t2 = asyncio.create_task(task(2, js.p1))
print("t1", t1.done(), t2.done())
print("end")
`);
// Wait for p1 to fulfill so t2 can continue.
await globalThis.p1;
// Wait a little longer so t2 can complete.
await new Promise((resolve, reject) => {
setTimeout(resolve, 10);
});
mp.runPython(`
print("restart")
print("t1", t1.done(), t2.done())
`);

Wyświetl plik

@ -0,0 +1,14 @@
start
t1 False False
end
task start 1
task start 2
task await 1 123
task await 1 123
task end 1
setTimeout resolved
task await 2 456
task await 2 456
task end 2
restart
t1 True True

Wyświetl plik

@ -0,0 +1,25 @@
// Test asyncio.sleep(), both at the top level and within a task.
const mp = await (await import(process.argv[2])).loadMicroPython();
await mp.runPythonAsync(`
import time
import asyncio
print("main start")
t0 = time.time()
await asyncio.sleep(0.25)
dt = time.time() - t0
print(0.2 <= dt <= 0.3)
async def task():
print("task start")
t0 = time.time()
await asyncio.sleep(0.25)
dt = time.time() - t0
print(0.2 <= dt <= 0.3)
print("task end")
asyncio.create_task(task())
print("main end")
`);

Wyświetl plik

@ -0,0 +1,6 @@
main start
True
main end
task start
True
task end

Wyświetl plik

@ -681,6 +681,17 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
elif args.target == "webassembly":
skip_tests.add("basics/string_format_modulo.py") # can't print nulls to stdout
skip_tests.add("basics/string_strip.py") # can't print nulls to stdout
skip_tests.add("extmod/asyncio_basic2.py")
skip_tests.add("extmod/asyncio_cancel_self.py")
skip_tests.add("extmod/asyncio_current_task.py")
skip_tests.add("extmod/asyncio_exception.py")
skip_tests.add("extmod/asyncio_gather_finished_early.py")
skip_tests.add("extmod/asyncio_get_event_loop.py")
skip_tests.add("extmod/asyncio_heaplock.py")
skip_tests.add("extmod/asyncio_loop_stop.py")
skip_tests.add("extmod/asyncio_new_event_loop.py")
skip_tests.add("extmod/asyncio_threadsafeflag.py")
skip_tests.add("extmod/asyncio_wait_for_fwd.py")
skip_tests.add("extmod/binascii_a2b_base64.py")
skip_tests.add("extmod/re_stack_overflow.py")
skip_tests.add("extmod/time_res.py")