316 lines
9.6 KiB
C
316 lines
9.6 KiB
C
/*
|
|
* This file is part of the MicroPython project, http://micropython.org/
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2022 KY-zhang-X
|
|
*
|
|
* 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/mpconfig.h"
|
|
|
|
#if MICROPY_PY_NETWORK && MICROPY_PY_NETWORK_ESP8266
|
|
|
|
#include "py/mphal.h"
|
|
#include "py/obj.h"
|
|
#include "py/runtime.h"
|
|
#include "py/stream.h"
|
|
#include "py/mperrno.h"
|
|
#include "extmod/modnetwork.h"
|
|
#include "machine_uart.h"
|
|
|
|
#if !(MP_GEN_HDR)
|
|
#include "tos_k.h"
|
|
#include "tos_at_socket.h"
|
|
#include "sal_module_wrapper.h"
|
|
#include "esp8266.h"
|
|
#endif
|
|
|
|
#define MAKE_SOCKADDR(addr, ip, port) \
|
|
struct sockaddr addr; \
|
|
addr.sa_family = AF_INET; \
|
|
addr.sa_data[0] = port >> 8; \
|
|
addr.sa_data[1] = port; \
|
|
addr.sa_data[2] = ip[0]; \
|
|
addr.sa_data[3] = ip[1]; \
|
|
addr.sa_data[4] = ip[2]; \
|
|
addr.sa_data[5] = ip[3];
|
|
|
|
#define UNPACK_SOCKADDR(addr, ip, port) \
|
|
port = (addr.sa_data[0] << 8) | addr.sa_data[1]; \
|
|
ip[0] = addr.sa_data[2]; \
|
|
ip[1] = addr.sa_data[3]; \
|
|
ip[2] = addr.sa_data[4]; \
|
|
ip[3] = addr.sa_data[5];
|
|
|
|
STATIC int esp8266_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *ip_out) {
|
|
char ip_str[16];
|
|
if (0 != tos_sal_module_parse_domain(name, ip_str, sizeof(ip_str))) {
|
|
return -2;
|
|
}
|
|
int ret = sscanf(ip_str, "%hhu.%hhu.%hhu.%hhu",
|
|
&ip_out[0], &ip_out[1], &ip_out[2], &ip_out[3]);
|
|
if (ret != 4) {
|
|
return -2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
STATIC int socket_socket(struct _mod_network_socket_obj_t *skt, int *_errno) {
|
|
// only support IPv4
|
|
if (skt->domain != MOD_NETWORK_AF_INET) {
|
|
*_errno = MP_EAFNOSUPPORT;
|
|
return -1;
|
|
}
|
|
int domain = AF_INET;
|
|
|
|
int type;
|
|
switch (skt->type) {
|
|
case MOD_NETWORK_SOCK_STREAM:
|
|
type = SOCK_STREAM;
|
|
break;
|
|
case MOD_NETWORK_SOCK_DGRAM:
|
|
type = SOCK_DGRAM;
|
|
break;
|
|
case MOD_NETWORK_SOCK_RAW:
|
|
// SOCK_RAW is not supported yet
|
|
default:
|
|
*_errno = MP_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
// open socket
|
|
int fd = socket(domain, type, 0);
|
|
if (fd < 0) {
|
|
// FIXME: no more detailed errno
|
|
*_errno = MP_EMFILE;
|
|
return -1;
|
|
}
|
|
|
|
// store state of this socket
|
|
skt->fileno = fd;
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC void socket_close(struct _mod_network_socket_obj_t *socket) {
|
|
close(socket->fileno);
|
|
}
|
|
|
|
STATIC int socket_bind(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) {
|
|
// FIXME: not support
|
|
*_errno = MP_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
STATIC int socket_listen(struct _mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) {
|
|
// FIXME: not support
|
|
*_errno = MP_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
STATIC int socket_accept(struct _mod_network_socket_obj_t *socket, struct _mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) {
|
|
// FIXME: not support
|
|
*_errno = MP_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
STATIC int socket_connect(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) {
|
|
MAKE_SOCKADDR(addr, ip, port);
|
|
int ret = connect(socket->fileno, &addr, sizeof(addr));
|
|
if (ret != 0) {
|
|
// FIXME: no more detailed errno
|
|
*_errno = MP_ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
STATIC mp_uint_t socket_send(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) {
|
|
int ret = send(socket->fileno, buf, len, 0);
|
|
if (ret < 0) {
|
|
// FIXME: no more detailed errno
|
|
*_errno = MP_EPERM;
|
|
return (mp_uint_t)-1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
STATIC mp_uint_t socket_recv(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) {
|
|
int ret = recv(socket->fileno, buf, len, 0);
|
|
if (ret < 0) {
|
|
// FIXME: no more detailed errno
|
|
*_errno = MP_EPERM;
|
|
return (mp_uint_t)-1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
STATIC mp_uint_t socket_sendto(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) {
|
|
MAKE_SOCKADDR(addr, ip, port);
|
|
int ret = sendto(socket->fileno, buf, len, 0, &addr, sizeof(addr));
|
|
if (ret < 0) {
|
|
// FIXME: no more detailed errno
|
|
*_errno = MP_EPERM;
|
|
return (mp_uint_t)-1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
STATIC mp_uint_t socket_recvfrom(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) {
|
|
struct sockaddr addr;
|
|
socklen_t addr_len = sizeof(addr);
|
|
int ret = recvfrom(socket->fileno, buf, len, 0, &addr, &addr_len);
|
|
if (ret < 0) {
|
|
// FIXME
|
|
*_errno = MP_EPERM;
|
|
return (mp_uint_t)-1;
|
|
}
|
|
UNPACK_SOCKADDR(addr, ip, *port);
|
|
return ret;
|
|
}
|
|
|
|
STATIC int socket_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) {
|
|
// FIXME not support
|
|
*_errno = MP_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
STATIC int socket_settimeout(struct _mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) {
|
|
// FIXME
|
|
*_errno = MP_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
STATIC int socket_ioctl(struct _mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) {
|
|
// FIXME
|
|
if (request == MP_STREAM_POLL) {
|
|
int ret = 0;
|
|
if ((arg & MP_STREAM_POLL_RD) && 1) {
|
|
ret |= MP_STREAM_POLL_RD;
|
|
}
|
|
if ((arg & MP_STREAM_POLL_WR) && 1) {
|
|
ret |= MP_STREAM_POLL_WR;
|
|
}
|
|
return ret;
|
|
} else {
|
|
*_errno = MP_EINVAL;
|
|
return (int)MP_STREAM_ERROR;
|
|
}
|
|
}
|
|
|
|
/********************************************/
|
|
typedef struct _esp8266_obj_t {
|
|
mp_obj_base_t base;
|
|
const machine_uart_obj_t *uart;
|
|
uint8_t init;
|
|
} esp8266_obj_t;
|
|
|
|
STATIC esp8266_obj_t esp8266_obj = {
|
|
{(mp_obj_type_t *)&mod_network_nic_type_esp8266},
|
|
.uart = NULL,
|
|
.init = 0,
|
|
};
|
|
|
|
extern at_agent_t esp8266_agent;
|
|
STATIC mp_obj_t esp8266_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, 1, 1, false);
|
|
|
|
if (!esp8266_obj.init) {
|
|
machine_uart_obj_t *uart = MP_OBJ_TO_PTR(args[0]);
|
|
machine_uart_set_at_agent(uart, &esp8266_agent);
|
|
machine_uart_rx_start(uart);
|
|
if (0 != esp8266_sal_init(machine_uart_get_port(uart))) {
|
|
esp8266_sal_deinit();
|
|
machine_uart_set_at_agent(uart, NULL);
|
|
mp_raise_msg(&mp_type_OSError, "can't setup esp8266");
|
|
}
|
|
|
|
esp8266_obj.uart = uart;
|
|
esp8266_obj.init = 1;
|
|
}
|
|
|
|
mod_network_register_nic(MP_OBJ_FROM_PTR(&esp8266_obj));
|
|
|
|
return MP_OBJ_FROM_PTR(&esp8266_obj);
|
|
}
|
|
|
|
STATIC mp_obj_t esp8266_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
enum { ARG_ssid, ARG_pwd };
|
|
static const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
|
{ MP_QSTR_pwd, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
|
};
|
|
|
|
// 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);
|
|
|
|
// get ssid
|
|
size_t ssid_len;
|
|
const char *ssid = mp_obj_str_get_data(args[ARG_ssid].u_obj, &ssid_len);
|
|
|
|
// get pwd
|
|
size_t pwd_len = 0;
|
|
const char *pwd = "";
|
|
if (args[ARG_pwd].u_obj != mp_const_none) {
|
|
pwd = mp_obj_str_get_data(args[ARG_pwd].u_obj, &pwd_len);
|
|
}
|
|
|
|
// connect to AP
|
|
if (esp8266_join_ap(ssid, pwd) != 0) {
|
|
mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("could not connect to ssid=%s, pwd=%s\n"), ssid, pwd);
|
|
}
|
|
|
|
return mp_const_none;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp8266_connect_obj, 1, esp8266_connect);
|
|
|
|
STATIC const mp_rom_map_elem_t esp8266_locals_dict_table[] = {
|
|
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&esp8266_connect_obj) },
|
|
};
|
|
STATIC MP_DEFINE_CONST_DICT(esp8266_locals_dict, esp8266_locals_dict_table);
|
|
|
|
const mod_network_nic_type_t mod_network_nic_type_esp8266 = {
|
|
.base = {
|
|
{ &mp_type_type },
|
|
.name = MP_QSTR_ESP8266,
|
|
.make_new = esp8266_make_new,
|
|
.locals_dict = (mp_obj_dict_t *)&esp8266_locals_dict,
|
|
},
|
|
.gethostbyname = esp8266_gethostbyname,
|
|
.socket = socket_socket,
|
|
.close = socket_close,
|
|
.bind = socket_bind,
|
|
.listen = socket_listen,
|
|
.accept = socket_accept,
|
|
.connect = socket_connect,
|
|
.send = socket_send,
|
|
.recv = socket_recv,
|
|
.sendto = socket_sendto,
|
|
.recvfrom = socket_recvfrom,
|
|
.setsockopt = socket_setsockopt,
|
|
.settimeout = socket_settimeout,
|
|
.ioctl = socket_ioctl,
|
|
};
|
|
|
|
#endif /* MICROPY_PY_NETWORK && MICROPY_PY_NETWORK_ESP8266*/
|