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 "lib/utils/pyexec.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
#include "modmachine.h"
|
#include "modmachine.h"
|
||||||
|
#include "modnetwork.h"
|
||||||
#include "mpthreadport.h"
|
#include "mpthreadport.h"
|
||||||
|
|
||||||
// MicroPython runs as a task under FreeRTOS
|
// MicroPython runs as a task under FreeRTOS
|
||||||
@ -110,6 +111,7 @@ soft_reset:
|
|||||||
|
|
||||||
// deinitialise peripherals
|
// deinitialise peripherals
|
||||||
machine_pins_deinit();
|
machine_pins_deinit();
|
||||||
|
usocket_events_deinit();
|
||||||
|
|
||||||
mp_deinit();
|
mp_deinit();
|
||||||
fflush(stdout);
|
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_KW(get_lan_obj);
|
||||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj);
|
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj);
|
||||||
|
|
||||||
|
void usocket_events_deinit(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include "py/mperrno.h"
|
#include "py/mperrno.h"
|
||||||
#include "lib/netutils/netutils.h"
|
#include "lib/netutils/netutils.h"
|
||||||
#include "tcpip_adapter.h"
|
#include "tcpip_adapter.h"
|
||||||
|
#include "modnetwork.h"
|
||||||
|
|
||||||
#include "lwip/sockets.h"
|
#include "lwip/sockets.h"
|
||||||
#include "lwip/netdb.h"
|
#include "lwip/netdb.h"
|
||||||
@ -63,10 +64,79 @@ typedef struct _socket_obj_t {
|
|||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t proto;
|
uint8_t proto;
|
||||||
unsigned int retries;
|
unsigned int retries;
|
||||||
|
#if MICROPY_PY_USOCKET_EVENTS
|
||||||
|
mp_obj_t events_callback;
|
||||||
|
struct _socket_obj_t *events_next;
|
||||||
|
#endif
|
||||||
} socket_obj_t;
|
} socket_obj_t;
|
||||||
|
|
||||||
void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms);
|
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) {
|
NORETURN static void exception_from_errno(int _errno) {
|
||||||
// Here we need to convert from lwip errno values to MicroPython's standard ones
|
// Here we need to convert from lwip errno values to MicroPython's standard ones
|
||||||
if (_errno == EINPROGRESS) {
|
if (_errno == EINPROGRESS) {
|
||||||
@ -209,6 +279,25 @@ STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) {
|
|||||||
break;
|
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
|
// level: IPPROTO_IP
|
||||||
case IP_ADD_MEMBERSHIP: {
|
case IP_ADD_MEMBERSHIP: {
|
||||||
mp_buffer_info_t bufinfo;
|
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;
|
return ret;
|
||||||
} else if (request == MP_STREAM_CLOSE) {
|
} else if (request == MP_STREAM_CLOSE) {
|
||||||
if (socket->fd >= 0) {
|
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);
|
int ret = lwip_close_r(socket->fd);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
*errcode = errno;
|
*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_BEGIN_ATOMIC_SECTION() portENTER_CRITICAL_NESTED()
|
||||||
#define MICROPY_END_ATOMIC_SECTION(state) portEXIT_CRITICAL_NESTED(state)
|
#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
|
#if MICROPY_PY_THREAD
|
||||||
#define MICROPY_EVENT_POLL_HOOK \
|
#define MICROPY_EVENT_POLL_HOOK \
|
||||||
do { \
|
do { \
|
||||||
extern void mp_handle_pending(void); \
|
extern void mp_handle_pending(void); \
|
||||||
mp_handle_pending(); \
|
mp_handle_pending(); \
|
||||||
|
MICROPY_PY_USOCKET_EVENTS_HANDLER \
|
||||||
MP_THREAD_GIL_EXIT(); \
|
MP_THREAD_GIL_EXIT(); \
|
||||||
MP_THREAD_GIL_ENTER(); \
|
MP_THREAD_GIL_ENTER(); \
|
||||||
} while (0);
|
} while (0);
|
||||||
@ -234,6 +241,7 @@ extern const struct _mp_obj_module_t mp_module_onewire;
|
|||||||
do { \
|
do { \
|
||||||
extern void mp_handle_pending(void); \
|
extern void mp_handle_pending(void); \
|
||||||
mp_handle_pending(); \
|
mp_handle_pending(); \
|
||||||
|
MICROPY_PY_USOCKET_EVENTS_HANDLER \
|
||||||
asm("waiti 0"); \
|
asm("waiti 0"); \
|
||||||
} while (0);
|
} while (0);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user