pico w: implement bind, listen, accept

this works with some simple tcp & udp echo service code
This commit is contained in:
Jeff Epler 2022-09-29 10:01:53 -05:00
parent 12ea04ca70
commit 74cdf42ece
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE
1 changed files with 140 additions and 6 deletions

View File

@ -37,6 +37,7 @@
#include "py/stream.h"
#include "shared-bindings/socketpool/SocketPool.h"
#include "shared/runtime/interrupt_char.h"
#include "shared/netutils/netutils.h"
#include "supervisor/port.h"
#include "supervisor/shared/tick.h"
#include "supervisor/workflow.h"
@ -115,6 +116,10 @@ static inline void poll_sockets(void) {
#ifdef MICROPY_EVENT_POLL_HOOK
MICROPY_EVENT_POLL_HOOK;
#else
RUN_BACKGROUND_TASKS;
if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) {
mp_handle_pending(true);
}
mp_hal_delay_ms(1);
#endif
}
@ -739,14 +744,117 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, uint8_t *ip, uint32_
return -MP_EBADF;
}
socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self,
socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *socket,
uint8_t *ip, uint32_t *port) {
mp_raise_NotImplementedError(NULL);
if (socket->type != MOD_NETWORK_SOCK_STREAM) {
mp_raise_OSError(MP_EOPNOTSUPP);
}
// Create new socket object, do it here because we must not raise an out-of-memory
// exception when the LWIP concurrency lock is held
socketpool_socket_obj_t *socket2 = m_new_ll_obj_with_finaliser(socketpool_socket_obj_t);
socket2->base.type = &socketpool_socket_type;
MICROPY_PY_LWIP_ENTER
if (socket->pcb.tcp == NULL) {
MICROPY_PY_LWIP_EXIT
m_del_obj(socketpool_socket_obj_t, socket2);
mp_raise_OSError(MP_EBADF);
}
// I need to do this because "tcp_accepted", later, is a macro.
struct tcp_pcb *listener = socket->pcb.tcp;
if (listener->state != LISTEN) {
MICROPY_PY_LWIP_EXIT
m_del_obj(socketpool_socket_obj_t, socket2);
mp_raise_OSError(MP_EINVAL);
}
// accept incoming connection
struct tcp_pcb *volatile *incoming_connection = &lwip_socket_incoming_array(socket)[socket->incoming.connection.iget];
if (*incoming_connection == NULL) {
if (socket->timeout == 0) {
MICROPY_PY_LWIP_EXIT
m_del_obj(socketpool_socket_obj_t, socket2);
mp_raise_OSError(MP_EAGAIN);
} else if (socket->timeout != -1) {
mp_uint_t retries = socket->timeout / 100;
while (*incoming_connection == NULL) {
MICROPY_PY_LWIP_EXIT
if (retries-- == 0) {
m_del_obj(socketpool_socket_obj_t, socket2);
mp_raise_OSError(MP_ETIMEDOUT);
}
mp_hal_delay_ms(100);
MICROPY_PY_LWIP_REENTER
}
} else {
while (*incoming_connection == NULL) {
MICROPY_PY_LWIP_EXIT
poll_sockets();
MICROPY_PY_LWIP_REENTER
}
}
}
// We get a new pcb handle...
socket2->pcb.tcp = *incoming_connection;
if (++socket->incoming.connection.iget >= socket->incoming.connection.alloc) {
socket->incoming.connection.iget = 0;
}
*incoming_connection = NULL;
// ...and set up the new socket for it.
socket2->domain = MOD_NETWORK_AF_INET;
socket2->type = MOD_NETWORK_SOCK_STREAM;
socket2->incoming.pbuf = NULL;
socket2->timeout = socket->timeout;
socket2->state = STATE_CONNECTED;
socket2->recv_offset = 0;
socket2->callback = MP_OBJ_NULL;
tcp_arg(socket2->pcb.tcp, (void *)socket2);
tcp_err(socket2->pcb.tcp, _lwip_tcp_error);
tcp_recv(socket2->pcb.tcp, _lwip_tcp_recv);
tcp_accepted(listener);
MICROPY_PY_LWIP_EXIT
// output values
memcpy(ip, &(socket2->pcb.tcp->remote_ip), NETUTILS_IPV4ADDR_BUFSIZE);
*port = (mp_uint_t)socket2->pcb.tcp->remote_port;
return MP_OBJ_FROM_PTR(socket2);
}
bool common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self,
bool common_hal_socketpool_socket_bind(socketpool_socket_obj_t *socket,
const char *host, size_t hostlen, uint32_t port) {
mp_raise_NotImplementedError(NULL);
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
// get address
ip_addr_t bind_addr;
int error = socketpool_resolve_host(socket->pool, host, &bind_addr);
if (error != 0) {
mp_raise_OSError(EHOSTUNREACH);
}
err_t err = ERR_ARG;
switch (socket->type) {
case MOD_NETWORK_SOCK_STREAM: {
err = tcp_bind(socket->pcb.tcp, &bind_addr, port);
break;
}
case MOD_NETWORK_SOCK_DGRAM: {
err = udp_bind(socket->pcb.udp, &bind_addr, port);
break;
}
}
if (err != ERR_OK) {
mp_raise_OSError(error_lookup_table[-err]);
}
return mp_const_none;
}
STATIC err_t _lwip_tcp_close_poll(void *arg, struct tcp_pcb *pcb) {
@ -891,8 +999,34 @@ bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *socket)
return socket->state == STATE_CONNECTED;
}
bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) {
mp_raise_NotImplementedError(NULL);
bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *socket, int backlog) {
if (socket->type != MOD_NETWORK_SOCK_STREAM) {
mp_raise_OSError(MP_EOPNOTSUPP);
}
struct tcp_pcb *new_pcb = tcp_listen_with_backlog(socket->pcb.tcp, (u8_t)backlog);
if (new_pcb == NULL) {
mp_raise_OSError(MP_ENOMEM);
}
socket->pcb.tcp = new_pcb;
// Allocate memory for the backlog of connections
if (backlog <= 1) {
socket->incoming.connection.alloc = 0;
socket->incoming.connection.tcp.item = NULL;
} else {
socket->incoming.connection.alloc = backlog;
socket->incoming.connection.tcp.array = m_new0(struct tcp_pcb *, backlog);
}
socket->incoming.connection.iget = 0;
socket->incoming.connection.iput = 0;
tcp_accept(new_pcb, _lwip_tcp_accept);
// Socket is no longer considered "new" for purposes of polling
socket->state = STATE_LISTENING;
return mp_const_none;
}
mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *socket,