esp32/modsocket: Add support for registering socket event callbacks.
The esp8266 uses modlwip.c for its usocket implementation, which allows to easily support callbacks on socket events (like when a socket becomes ready for reading). This is not as easy to do for the esp32 which uses the ESP-IDF-provided lwIP POSIX socket API. Socket events are needed to get WebREPL working, and this patch provides a way for such events to work by explicitly polling registered sockets for readability, and then calling the associated callback if the socket is readable.
This commit is contained in:
parent
98b05e3614
commit
999c8b9711
@ -47,6 +47,7 @@
|
||||
#include "lib/utils/pyexec.h"
|
||||
#include "uart.h"
|
||||
#include "modmachine.h"
|
||||
#include "modnetwork.h"
|
||||
#include "mpthreadport.h"
|
||||
|
||||
// MicroPython runs as a task under FreeRTOS
|
||||
@ -110,6 +111,7 @@ soft_reset:
|
||||
|
||||
// deinitialise peripherals
|
||||
machine_pins_deinit();
|
||||
usocket_events_deinit();
|
||||
|
||||
mp_deinit();
|
||||
fflush(stdout);
|
||||
|
@ -31,4 +31,6 @@ enum { PHY_LAN8720, PHY_TLK110 };
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(get_lan_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj);
|
||||
|
||||
void usocket_events_deinit(void);
|
||||
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "py/mperrno.h"
|
||||
#include "lib/netutils/netutils.h"
|
||||
#include "tcpip_adapter.h"
|
||||
#include "modnetwork.h"
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netdb.h"
|
||||
@ -63,10 +64,79 @@ typedef struct _socket_obj_t {
|
||||
uint8_t type;
|
||||
uint8_t proto;
|
||||
unsigned int retries;
|
||||
#if MICROPY_PY_USOCKET_EVENTS
|
||||
mp_obj_t events_callback;
|
||||
struct _socket_obj_t *events_next;
|
||||
#endif
|
||||
} socket_obj_t;
|
||||
|
||||
void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms);
|
||||
|
||||
#if MICROPY_PY_USOCKET_EVENTS
|
||||
// Support for callbacks on asynchronous socket events (when socket becomes readable)
|
||||
|
||||
// This divisor is used to reduce the load on the system, so it doesn't poll sockets too often
|
||||
#define USOCKET_EVENTS_DIVISOR (8)
|
||||
|
||||
STATIC uint8_t usocket_events_divisor;
|
||||
STATIC socket_obj_t *usocket_events_head;
|
||||
|
||||
void usocket_events_deinit(void) {
|
||||
usocket_events_head = NULL;
|
||||
}
|
||||
|
||||
// Assumes the socket is not already in the linked list, and adds it
|
||||
STATIC void usocket_events_add(socket_obj_t *sock) {
|
||||
sock->events_next = usocket_events_head;
|
||||
usocket_events_head = sock;
|
||||
}
|
||||
|
||||
// Assumes the socket is already in the linked list, and removes it
|
||||
STATIC void usocket_events_remove(socket_obj_t *sock) {
|
||||
for (socket_obj_t **s = &usocket_events_head;; s = &(*s)->events_next) {
|
||||
if (*s == sock) {
|
||||
*s = (*s)->events_next;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Polls all registered sockets for readability and calls their callback if they are readable
|
||||
void usocket_events_handler(void) {
|
||||
if (usocket_events_head == NULL) {
|
||||
return;
|
||||
}
|
||||
if (--usocket_events_divisor) {
|
||||
return;
|
||||
}
|
||||
usocket_events_divisor = USOCKET_EVENTS_DIVISOR;
|
||||
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
int max_fd = 0;
|
||||
|
||||
for (socket_obj_t *s = usocket_events_head; s != NULL; s = s->events_next) {
|
||||
FD_SET(s->fd, &rfds);
|
||||
max_fd = MAX(max_fd, s->fd);
|
||||
}
|
||||
|
||||
// Poll the sockets
|
||||
struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
|
||||
int r = select(max_fd + 1, &rfds, NULL, NULL, &timeout);
|
||||
if (r <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the callbacks
|
||||
for (socket_obj_t *s = usocket_events_head; s != NULL; s = s->events_next) {
|
||||
if (FD_ISSET(s->fd, &rfds)) {
|
||||
mp_call_function_1_protected(s->events_callback, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_USOCKET_EVENTS
|
||||
|
||||
NORETURN static void exception_from_errno(int _errno) {
|
||||
// Here we need to convert from lwip errno values to MicroPython's standard ones
|
||||
if (_errno == EINPROGRESS) {
|
||||
@ -209,6 +279,25 @@ STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) {
|
||||
break;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_USOCKET_EVENTS
|
||||
// level: SOL_SOCKET
|
||||
// special "register callback" option
|
||||
case 20: {
|
||||
if (args[3] == mp_const_none) {
|
||||
if (self->events_callback != MP_OBJ_NULL) {
|
||||
usocket_events_remove(self);
|
||||
self->events_callback = MP_OBJ_NULL;
|
||||
}
|
||||
} else {
|
||||
if (self->events_callback == MP_OBJ_NULL) {
|
||||
usocket_events_add(self);
|
||||
}
|
||||
self->events_callback = args[3];
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// level: IPPROTO_IP
|
||||
case IP_ADD_MEMBERSHIP: {
|
||||
mp_buffer_info_t bufinfo;
|
||||
@ -439,6 +528,12 @@ STATIC mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintpt
|
||||
return ret;
|
||||
} else if (request == MP_STREAM_CLOSE) {
|
||||
if (socket->fd >= 0) {
|
||||
#if MICROPY_PY_USOCKET_EVENTS
|
||||
if (socket->events_callback != MP_OBJ_NULL) {
|
||||
usocket_events_remove(socket);
|
||||
socket->events_callback = MP_OBJ_NULL;
|
||||
}
|
||||
#endif
|
||||
int ret = lwip_close_r(socket->fd);
|
||||
if (ret != 0) {
|
||||
*errcode = errno;
|
||||
|
@ -221,11 +221,18 @@ extern const struct _mp_obj_module_t mp_module_onewire;
|
||||
#define MICROPY_BEGIN_ATOMIC_SECTION() portENTER_CRITICAL_NESTED()
|
||||
#define MICROPY_END_ATOMIC_SECTION(state) portEXIT_CRITICAL_NESTED(state)
|
||||
|
||||
#if MICROPY_PY_USOCKET_EVENTS
|
||||
#define MICROPY_PY_USOCKET_EVENTS_HANDLER extern void usocket_events_handler(void); usocket_events_handler();
|
||||
#else
|
||||
#define MICROPY_PY_USOCKET_EVENTS_HANDLER
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
#define MICROPY_EVENT_POLL_HOOK \
|
||||
do { \
|
||||
extern void mp_handle_pending(void); \
|
||||
mp_handle_pending(); \
|
||||
MICROPY_PY_USOCKET_EVENTS_HANDLER \
|
||||
MP_THREAD_GIL_EXIT(); \
|
||||
MP_THREAD_GIL_ENTER(); \
|
||||
} while (0);
|
||||
@ -234,6 +241,7 @@ extern const struct _mp_obj_module_t mp_module_onewire;
|
||||
do { \
|
||||
extern void mp_handle_pending(void); \
|
||||
mp_handle_pending(); \
|
||||
MICROPY_PY_USOCKET_EVENTS_HANDLER \
|
||||
asm("waiti 0"); \
|
||||
} while (0);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user