circuitpython/drivers/ninaw10/nina_wifi_drv.c
iabdalkader b9c1e4c205 drivers/ninaw10: Connect to WiFi asynchronously.
Before this patch, WiFi connection was blocking, and could raise exceptions
if the connection failed for any reason (including timeouts).  This doesn't
match the behavior of other WiFi modules, which connect asynchronously, and
requires handling of exceptions on connect.  This change makes `connect()`
work asynchronously by scheduling code to poll connection status, and
handle reconnects (if needed), and return immediately without blocking.
2022-11-16 09:46:43 +11:00

903 lines
27 KiB
C

/*
* This file is part of the OpenMV project, https://openmv.io.
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* 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.
*
* NINA-W10 WiFi driver.
*/
#include "py/mphal.h"
#include "py/mperrno.h"
#if MICROPY_PY_NETWORK_NINAW10
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "nina_bsp.h"
#include "nina_wifi_drv.h"
#define SPI_ACK (1)
#define SPI_ERR (0xFF)
#define NO_SOCKET_AVAIL (255)
#define CMD_START (0xE0)
#define CMD_END (0xEE)
#define CMD_ERROR (0xEF)
#define CMD_REPLY (1 << 7)
#define ARG_8BITS (1)
#define ARG_16BITS (2)
#define ARG_STR(x) {strlen(x), (const void *)x}
#define ARG_BYTE(x) {1, (uint8_t [1]) {x}}
#define ARG_SHORT(x) {2, (uint16_t [1]) {x}}
#define ARG_WORD(x) {4, (uint32_t [1]) {x}}
#define NINA_ARGS(...) (nina_args_t []) {__VA_ARGS__}
#define NINA_VALS(...) (nina_vals_t []) {__VA_ARGS__}
#define NINA_SSELECT_TIMEOUT (1000)
#define NINA_CONNECT_TIMEOUT (10000)
#if NINA_DEBUG
#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__)
#else
#define debug_printf(...)
#endif
#ifndef __REVSH
#define __REVSH(x) ((((uint16_t)x) << 8) | (((uint16_t)x) >> 8))
#endif
typedef struct {
uint16_t size;
const void *data;
} nina_args_t;
typedef struct {
uint16_t *size;
void *data;
} nina_vals_t;
typedef enum {
// STA mode commands.
NINA_CMD_CONNECT_OPEN = 0x10,
NINA_CMD_CONNECT_WEP = 0x11,
NINA_CMD_CONNECT_WPA = 0x12,
NINA_CMD_GET_SSID = 0x23,
NINA_CMD_GET_BSSID = 0x24,
NINA_CMD_GET_RSSI = 0x25,
NINA_CMD_GET_ENCRYPT = 0x26,
// AP mode commands.
NINA_CMD_START_AP_OPEN = 0x18,
NINA_CMD_START_AP_WEP = 0x19,
// AP mode scan commands.
NINA_CMD_AP_START_SCAN = 0x36,
NINA_CMD_AP_SCAN_RESULT = 0x27,
NINA_CMD_AP_GET_RSSI = 0x32,
NINA_CMD_AP_GET_ENCRYPT = 0x33,
NINA_CMD_AP_GET_BSSID = 0x3C,
NINA_CMD_AP_GET_CHANNEL = 0x3D,
// Disonnect/status commands.
NINA_CMD_DISCONNECT = 0x30,
NINA_CMD_CONN_STATUS = 0x20,
NINA_CMD_CONN_REASON = 0x1F,
// Interface config commands.
NINA_CMD_SET_IF_CONFIG = 0x14,
NINA_CMD_GET_IF_CONFIG = 0x21,
NINA_CMD_SET_DNS_CONFIG = 0x15,
// Hostname/Resolv commands.
NINA_CMD_SET_HOSTNAME = 0x16,
NINA_CMD_HOST_BY_NAME = 0x34,
NINA_CMD_GET_HOST_BY_NAME = 0x35,
// Misc commands.
NINA_CMD_SET_POWER = 0x17,
NINA_CMD_PING = 0x3E,
NINA_CMD_GET_TIME = 0x3B,
NINA_CMD_GET_FW_VERSION = 0x37,
NINA_CMD_DEBUG_MODE = 0x1A,
NINA_CMD_TEMP_SENSOR = 0x1B,
NINA_CMD_GET_MAC_ADDR = 0x22,
// Sockets commands.
NINA_CMD_SOCKET_REMOTE_ADDR = 0x3A,
NINA_CMD_SOCKET_SOCKET = 0x70,
NINA_CMD_SOCKET_CLOSE = 0x71,
NINA_CMD_SOCKET_ERRNO = 0x72,
NINA_CMD_SOCKET_BIND = 0x73,
NINA_CMD_SOCKET_LISTEN = 0x74,
NINA_CMD_SOCKET_ACCEPT = 0x75,
NINA_CMD_SOCKET_CONNECT = 0x76,
NINA_CMD_SOCKET_SEND = 0x77,
NINA_CMD_SOCKET_RECV = 0x78,
NINA_CMD_SOCKET_SENDTO = 0x79,
NINA_CMD_SOCKET_RECVFROM = 0x7A,
NINA_CMD_SOCKET_IOCTL = 0x7B,
NINA_CMD_SOCKET_POLL = 0x7C,
NINA_CMD_SOCKET_SETSOCKOPT = 0x7D,
NINA_CMD_SOCKET_GETSOCKOPT = 0x7E,
NINA_CMD_SOCKET_GETPEERNAME = 0x7F,
// UDP commands.
NINA_CMD_UDP_SEND = 0x46,
NINA_CMD_UDP_RECV = 0x45,
NINA_CMD_UDP_ACK = 0x39,
// Pin control commands.
NINA_CMD_SET_PIN_MODE = 0x50,
NINA_CMD_SET_DIGITAL_WRITE = 0x51,
NINA_CMD_GET_DIGITAL_READ = 0x53,
NINA_CMD_SET_ANALOG_WRITE = 0x52,
NINA_CMD_GET_ANALOG_READ = 0x54,
// File send/recv commands.
NINA_CMD_CMD_WRITE_FILE = 0x60,
NINA_CMD_CMD_READ_FILE = 0x61,
NINA_CMD_CMD_DELETE_FILE = 0x62,
NINA_CMD_CMD_EXISTS_FILE = 0x63,
NINA_CMD_CMD_DOWNLOAD_FILE = 0x64,
// OTA upgrade commands.
NINA_CMD_CMD_APPLY_OTA = 0x65,
NINA_CMD_CMD_RENAME_FILE = 0x66,
NINA_CMD_CMD_DOWNLOAD_OTA = 0x67,
} nina_cmd_t;
typedef enum {
SOCKET_STATE_CLOSED = 0,
SOCKET_STATE_LISTEN,
SOCKET_STATE_SYN_SENT,
SOCKET_STATE_SYN_RCVD,
SOCKET_STATE_ESTABLISHED,
SOCKET_STATE_FIN_WAIT_1,
SOCKET_STATE_FIN_WAIT_2,
SOCKET_STATE_CLOSE_WAIT,
SOCKET_STATE_CLOSING,
SOCKET_STATE_LAST_ACK,
SOCKET_STATE_TIME_WAIT
} nina_sock_state_t;
static uint8_t nina_bsp_spi_read_byte(void) {
uint8_t byte = 0;
nina_bsp_spi_transfer(NULL, &byte, 1);
return byte;
}
static int nina_send_command(uint32_t cmd, uint32_t nargs, uint32_t width, nina_args_t *args) {
int ret = -1;
uint32_t length = 4; // 3 bytes header + 1 end byte
debug_printf("nina_send_command (cmd 0x%x nargs %d width %d): ", cmd, nargs, width);
nina_bsp_spi_slave_deselect();
if (nina_bsp_spi_slave_select(NINA_SSELECT_TIMEOUT) != 0) {
goto error_out;
}
// Send command header.
uint8_t cmdbuf_hdr[3] = {CMD_START, cmd, nargs};
if (nina_bsp_spi_transfer(cmdbuf_hdr, NULL, sizeof(cmdbuf_hdr)) != 0) {
goto error_out;
}
// Send command arg(s).
for (uint32_t i = 0; i < nargs; i++) {
// Send size MSB first if 2 bytes.
uint16_t size = (width == ARG_8BITS) ? args[i].size : __REVSH(args[i].size);
// Send arg length.
if (nina_bsp_spi_transfer((uint8_t *)&size, NULL, width) != 0) {
goto error_out;
}
// Send arg value.
if (nina_bsp_spi_transfer(args[i].data, NULL, args[i].size) != 0) {
goto error_out;
}
length += args[i].size + width;
}
// Send END byte + padding to multiple of 4.
uint8_t cmdbuf_end[4] = {CMD_END, 0xFF, 0xFF, 0xFF};
if (nina_bsp_spi_transfer(cmdbuf_end, NULL, 1 + (length % 4)) != 0) {
goto error_out;
}
// All good
ret = 0;
error_out:
debug_printf("\n");
nina_bsp_spi_slave_deselect();
return ret;
}
static int nina_read_response(uint32_t cmd, uint32_t nvals, uint32_t width, nina_vals_t *vals) {
int ret = -1;
uint32_t length = 3; // 3 bytes response header
uint8_t header[3] = {0, 0, 0};
debug_printf("nina_read_response(cmd 0x%x nvals %d width %d): ", cmd, nvals, width);
// Read reply
nina_bsp_spi_slave_deselect();
if (nina_bsp_spi_slave_select(0) != 0) {
goto error_out;
}
if (nina_bsp_spi_transfer(NULL, header, sizeof(header)) != 0
|| header[1] != (cmd | CMD_REPLY)) {
// Read padding.
uint8_t header_padding = nina_bsp_spi_read_byte();
(void)header_padding;
debug_printf("nina_read_response() hdr 0x%x 0x%x 0x%x 0x%x\n",
header[0], header[1], header[2], header_padding);
goto error_out;
}
// Sanity check the number of returned values.
// NOTE: This is to handle the special case for the scan command.
if (nvals > header[2]) {
nvals = header[2];
}
// Read return value(s).
for (uint32_t i = 0; i < nvals; i++) {
// Read return value size.
uint16_t bytes = nina_bsp_spi_read_byte();
if (width == ARG_16BITS) {
bytes = (bytes << 8) | nina_bsp_spi_read_byte();
}
// Check the val fits the buffer.
if (*(vals[i].size) < bytes) {
goto error_out;
}
// Read the returned value.
if (nina_bsp_spi_transfer(NULL, vals[i].data, bytes) != 0) {
goto error_out;
}
// Set the size.
*(vals[i].size) = bytes;
length += bytes + width;
}
// Read CMD_END and padding.
uint8_t rspbuf_end[4];
if (nina_bsp_spi_transfer(NULL, rspbuf_end, ((length + 1) % 4) + 1) != 0 || rspbuf_end[0] != CMD_END) {
goto error_out;
}
// All good
ret = 0;
error_out:
debug_printf("\n");
nina_bsp_spi_slave_deselect();
return ret;
}
static int nina_send_command_read_ack(uint32_t cmd, uint32_t nargs, uint32_t width, nina_args_t *args) {
uint16_t size = 1;
uint8_t rval = SPI_ERR;
nina_bsp_atomic_enter();
if (nina_send_command(cmd, nargs, width, args) != 0 ||
nina_read_response(cmd, 1, ARG_8BITS, NINA_VALS({&size, &rval})) != 0) {
rval = -1;
}
nina_bsp_atomic_exit();
return rval;
}
static int nina_send_command_read_vals(uint32_t cmd, uint32_t nargs,
uint32_t argsw, nina_args_t *args, uint32_t nvals, uint32_t valsw, nina_vals_t *vals) {
int ret = 0;
nina_bsp_atomic_enter();
if (nina_send_command(cmd, nargs, argsw, args) != 0 ||
nina_read_response(cmd, nvals, valsw, vals) != 0) {
ret = -1;
}
nina_bsp_atomic_exit();
return ret;
}
static void nina_fix_mac_addr(uint8_t *mac) {
for (int i = 0; i < 3; i++) {
uint8_t b = mac[i];
mac[i] = mac[5 - i];
mac[5 - i] = b;
}
}
int nina_init(void) {
// Initialize the BSP.
nina_bsp_init();
return 0;
}
int nina_deinit(void) {
return nina_bsp_deinit();
}
int nina_connection_status(void) {
return nina_send_command_read_ack(NINA_CMD_CONN_STATUS, 0, ARG_8BITS, NULL);
}
int nina_connection_reason(void) {
return nina_send_command_read_ack(NINA_CMD_CONN_REASON, 0, ARG_8BITS, NULL);
}
int nina_connect(const char *ssid, uint8_t security, const char *key, uint16_t channel) {
if (key == NULL && security != NINA_SEC_OPEN) {
return -1;
}
switch (security) {
case NINA_SEC_OPEN:
if (nina_send_command_read_ack(NINA_CMD_CONNECT_OPEN,
1, ARG_8BITS, NINA_ARGS(ARG_STR(ssid))) != SPI_ACK) {
return -1;
}
break;
case NINA_SEC_WEP:
if (nina_send_command_read_ack(NINA_CMD_CONNECT_WEP,
2, ARG_8BITS, NINA_ARGS(ARG_STR(ssid), ARG_STR(key))) != SPI_ACK) {
return -1;
}
break;
case NINA_SEC_WPA_PSK:
if (nina_send_command_read_ack(NINA_CMD_CONNECT_WPA,
3, ARG_8BITS, NINA_ARGS(ARG_STR(ssid), ARG_BYTE(0), ARG_STR(key))) != SPI_ACK) {
return -1;
}
break;
default:
return -1;
}
return 0;
}
int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t channel) {
uint8_t status = NINA_STATUS_AP_FAILED;
if ((key == NULL && security != NINA_SEC_OPEN) ||
(security != NINA_SEC_OPEN && security != NINA_SEC_WEP)) {
return -1;
}
switch (security) {
case NINA_SEC_OPEN:
if (nina_send_command_read_ack(NINA_CMD_START_AP_OPEN,
2, ARG_8BITS, NINA_ARGS(ARG_STR(ssid), ARG_BYTE(channel))) != SPI_ACK) {
return -1;
}
break;
case NINA_SEC_WEP:
if (nina_send_command_read_ack(NINA_CMD_START_AP_WEP,
3, ARG_8BITS, NINA_ARGS(ARG_STR(ssid), ARG_STR(key), ARG_BYTE(channel))) != SPI_ACK) {
return -1;
}
break;
default:
return -1;
}
for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) {
status = nina_connection_status();
if ((status != NINA_STATUS_IDLE) && (status != NINA_STATUS_NO_SSID_AVAIL)) {
break;
}
if ((mp_hal_ticks_ms() - start) >= NINA_CONNECT_TIMEOUT) {
break;
}
}
return (status == NINA_STATUS_AP_LISTENING) ? 0 : -1;
}
int nina_disconnect(void) {
if (nina_send_command_read_ack(NINA_CMD_DISCONNECT,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF))) != SPI_ACK) {
return -1;
}
return 0;
}
int nina_isconnected(void) {
return nina_connection_status() == NINA_STATUS_CONNECTED;
}
int nina_connected_sta(uint32_t *sta_ip) {
return -1;
}
int nina_ifconfig(nina_ifconfig_t *ifconfig, bool set) {
uint16_t ip_len = NINA_IPV4_ADDR_LEN;
uint16_t sub_len = NINA_IPV4_ADDR_LEN;
uint16_t gw_len = NINA_IPV4_ADDR_LEN;
uint16_t dns_len = NINA_IPV4_ADDR_LEN;
if (set) {
if (nina_send_command_read_ack(NINA_CMD_SET_IF_CONFIG,
4, ARG_8BITS,
NINA_ARGS(
ARG_BYTE(3), // Valid number of args.
{ip_len, ifconfig->ip_addr},
{gw_len, ifconfig->gateway_addr},
{sub_len, ifconfig->subnet_addr})) != 0) {
return -1;
}
uint8_t dns2[4] = {8, 8, 8, 8};
if (nina_send_command_read_ack(NINA_CMD_SET_DNS_CONFIG,
3, ARG_8BITS,
NINA_ARGS(
ARG_BYTE(1), // Valid number of args.
{dns_len, ifconfig->dns_addr},
{dns_len, dns2})) != SPI_ACK) {
return -1;
}
} else {
if (nina_send_command_read_vals(NINA_CMD_GET_IF_CONFIG,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)),
3, ARG_8BITS,
NINA_VALS(
{&ip_len, ifconfig->ip_addr},
{&sub_len, ifconfig->subnet_addr},
{&gw_len, ifconfig->gateway_addr})) != 0) {
return -1;
}
// No command to get DNS ?
memcpy(ifconfig->dns_addr, ifconfig->gateway_addr, NINA_IPV4_ADDR_LEN);
}
return 0;
}
int nina_netinfo(nina_netinfo_t *netinfo) {
uint16_t rssi_len = 4;
uint16_t sec_len = 1;
uint16_t ssid_len = NINA_MAX_SSID_LEN;
uint16_t bssid_len = NINA_MAC_ADDR_LEN;
if (nina_send_command_read_vals(NINA_CMD_GET_RSSI,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)),
1, ARG_8BITS, NINA_VALS({&rssi_len, &netinfo->rssi})) != 0) {
return -1;
}
if (nina_send_command_read_vals(NINA_CMD_GET_ENCRYPT,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)),
1, ARG_8BITS, NINA_VALS({&sec_len, &netinfo->security})) != 0) {
return -1;
}
if (nina_send_command_read_vals(NINA_CMD_GET_SSID,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)),
1, ARG_8BITS, NINA_VALS({&ssid_len, &netinfo->ssid})) != 0) {
return -1;
}
// Null terminate SSID.
netinfo->ssid[MIN((NINA_MAX_SSID_LEN - 1), ssid_len)] = 0;
if (nina_send_command_read_vals(NINA_CMD_GET_BSSID,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)),
1, ARG_8BITS, NINA_VALS({&bssid_len, &netinfo->bssid})) != 0) {
return -1;
}
// The MAC address is read in reverse from the firmware.
nina_fix_mac_addr(netinfo->bssid);
return 0;
}
int nina_scan(nina_scan_callback_t scan_callback, void *arg, uint32_t timeout) {
uint16_t sizes[NINA_MAX_NETWORK_LIST];
char ssids[NINA_MAX_NETWORK_LIST][NINA_MAX_SSID_LEN];
nina_vals_t vals[NINA_MAX_NETWORK_LIST];
// Initialize the values list.
for (int i = 0; i < NINA_MAX_NETWORK_LIST; i++) {
sizes[i] = NINA_MAX_SSID_LEN - 1;
memset(ssids[i], 0, NINA_MAX_SSID_LEN);
vals[i].size = &sizes[i];
vals[i].data = ssids[i];
}
if (nina_send_command_read_ack(NINA_CMD_AP_START_SCAN,
0, ARG_8BITS, NULL) != SPI_ACK) {
return -1;
}
for (mp_uint_t start = mp_hal_ticks_ms(); ;) {
if (nina_send_command_read_vals(NINA_CMD_AP_SCAN_RESULT,
0, ARG_8BITS, NULL,
NINA_MAX_NETWORK_LIST, ARG_8BITS, vals) != 0) {
return -1;
}
if (ssids[0][0] != 0) {
// Found at least 1 network.
break;
}
if (timeout && (mp_hal_ticks_ms() - start) >= timeout) {
// Timeout, no networks.
return -MP_ETIMEDOUT;
}
mp_hal_delay_ms(100);
}
for (int i = 0; i < NINA_MAX_NETWORK_LIST; i++) {
uint16_t rssi_len = 4;
uint16_t sec_len = 1;
uint16_t chan_len = 1;
uint16_t bssid_len = NINA_MAC_ADDR_LEN;
nina_scan_result_t scan_result;
if (ssids[i][0] == 0) {
break;
}
// Set AP SSID
strncpy(scan_result.ssid, ssids[i], NINA_MAX_SSID_LEN);
// Read AP RSSI
if (nina_send_command_read_vals(NINA_CMD_AP_GET_RSSI,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(i)),
1, ARG_8BITS, NINA_VALS({&rssi_len, &scan_result.rssi})) != 0) {
return -1;
}
// Read AP encryption type
if (nina_send_command_read_vals(NINA_CMD_AP_GET_ENCRYPT,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(i)),
1, ARG_8BITS, NINA_VALS({&sec_len, &scan_result.security})) != 0) {
return -1;
}
// Read AP channel
if (nina_send_command_read_vals(NINA_CMD_AP_GET_CHANNEL,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(i)),
1, ARG_8BITS, NINA_VALS({&chan_len, &scan_result.channel})) != 0) {
return -1;
}
// Read AP bssid
if (nina_send_command_read_vals(NINA_CMD_AP_GET_BSSID,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(i)),
1, ARG_8BITS, NINA_VALS({&bssid_len, scan_result.bssid})) != 0) {
return -1;
}
// The MAC address is read in reverse from the firmware.
nina_fix_mac_addr(scan_result.bssid);
scan_callback(&scan_result, arg);
}
return 0;
}
int nina_get_rssi(void) {
uint16_t size = 4;
int32_t rssi = 0;
if (nina_send_command_read_vals(NINA_CMD_GET_RSSI,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)),
1, ARG_8BITS, NINA_VALS({&size, &rssi})) != 0) {
return -1;
}
return rssi;
}
int nina_fw_version(uint8_t *fw_ver) {
uint16_t size = NINA_FW_VER_LEN;
if (nina_send_command_read_vals(NINA_CMD_GET_FW_VERSION,
0, ARG_8BITS, NULL,
1, ARG_8BITS, NINA_VALS({&size, fw_ver})) != 0) {
return -1;
}
return 0;
}
int nina_set_hostname(const char *hostname) {
if (nina_send_command_read_ack(NINA_CMD_SET_HOSTNAME,
1, ARG_8BITS, NINA_ARGS(ARG_STR(hostname))) != SPI_ACK) {
return -1;
}
return 0;
}
int nina_gethostbyname(const char *name, uint8_t *out_ip) {
uint16_t size = 4;
if (nina_send_command_read_ack(NINA_CMD_HOST_BY_NAME,
1, ARG_8BITS, NINA_ARGS(ARG_STR(name))) != SPI_ACK) {
return -1;
}
if (nina_send_command_read_vals(NINA_CMD_GET_HOST_BY_NAME,
0, ARG_8BITS, NULL,
1, ARG_8BITS, NINA_VALS({&size, out_ip})) != 0) {
return -1;
}
return 0;
}
int nina_ioctl(uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface) {
switch (cmd) {
case NINA_CMD_SET_PIN_MODE:
if (len != 2 || nina_send_command_read_ack(NINA_CMD_SET_PIN_MODE,
2, ARG_8BITS, NINA_ARGS(ARG_BYTE(buf[0]), ARG_BYTE(buf[1]))) != SPI_ACK) {
return -1;
}
break;
case NINA_CMD_SET_DIGITAL_WRITE:
if (len != 2 || nina_send_command_read_ack(NINA_CMD_SET_DIGITAL_WRITE,
2, ARG_8BITS, NINA_ARGS(ARG_BYTE(buf[0]), ARG_BYTE(buf[1]))) != SPI_ACK) {
return -1;
}
break;
case NINA_CMD_GET_DIGITAL_READ:
if (len != 1 || nina_send_command_read_vals(NINA_CMD_GET_DIGITAL_READ,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(buf[0])),
1, ARG_8BITS, NINA_VALS({(uint16_t *)&len, buf})) != 0) {
return -1;
}
break;
default:
return 0;
}
return 0;
}
int nina_socket_socket(uint8_t type, uint8_t proto) {
uint16_t size = 1;
uint8_t sock = 0;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_SOCKET,
2, ARG_8BITS, NINA_ARGS(ARG_BYTE(type), ARG_BYTE(proto)),
1, ARG_8BITS, NINA_VALS({&size, &sock})) != 0) {
return -1;
}
return (sock == NO_SOCKET_AVAIL) ? -1 : sock;
}
int nina_socket_close(int fd) {
if (nina_send_command_read_ack(NINA_CMD_SOCKET_CLOSE,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd))) != SPI_ACK) {
return -1;
}
return 0;
}
int nina_socket_errno(int *_errno) {
uint16_t size = 1;
*_errno = 0;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_ERRNO,
0, ARG_8BITS, NULL,
1, ARG_8BITS, NINA_VALS({&size, _errno})) != 0) {
return -1;
}
return 0;
}
int nina_socket_bind(int fd, uint8_t *ip, uint16_t port) {
if (nina_send_command_read_ack(NINA_CMD_SOCKET_BIND,
2, ARG_8BITS,
NINA_ARGS(
ARG_BYTE(fd),
ARG_SHORT(__REVSH(port)))) != SPI_ACK) {
return -1;
}
return 0;
}
int nina_socket_listen(int fd, uint32_t backlog) {
if (nina_send_command_read_ack(NINA_CMD_SOCKET_LISTEN,
2, ARG_8BITS,
NINA_ARGS(
ARG_BYTE(fd),
ARG_BYTE(backlog))) != SPI_ACK) {
return -1;
}
return 0;
}
int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out) {
uint16_t fd_len = 1;
uint16_t port_len = 2;
uint16_t ip_len = NINA_IPV4_ADDR_LEN;
*fd_out = 0;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_ACCEPT,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd)),
3, ARG_8BITS, NINA_VALS({&fd_len, fd_out}, {&ip_len, ip}, {&port_len, port})) != 0) {
return -1;
}
return (*fd_out == NO_SOCKET_AVAIL) ? -1 : 0;
}
int nina_socket_connect(int fd, uint8_t *ip, uint16_t port) {
if (nina_send_command_read_ack(NINA_CMD_SOCKET_CONNECT,
3, ARG_8BITS,
NINA_ARGS(
ARG_BYTE(fd),
ARG_WORD((*(uint32_t *)ip)),
ARG_SHORT(__REVSH(port)))) != SPI_ACK) {
return -1;
}
return 0;
}
int nina_socket_send(int fd, const uint8_t *buf, uint32_t len) {
uint16_t size = 2;
uint16_t bytes = 0;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_SEND,
2, ARG_16BITS, NINA_ARGS(ARG_BYTE(fd), {len, buf}),
1, ARG_8BITS, NINA_VALS({&size, &bytes})) != 0) {
return -1;
}
// Only args sizes (not args themselves) are reversed in read_response().
bytes = __REVSH(bytes);
if (bytes == 0) {
int _errno = 0;
if (nina_socket_errno(&_errno) != 0 || _errno != 0) {
return -1;
}
}
return bytes;
}
int nina_socket_recv(int fd, uint8_t *buf, uint32_t len) {
uint16_t bytes = len;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_RECV,
2, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd), ARG_SHORT(bytes)),
1, ARG_16BITS, NINA_VALS({&bytes, buf})) != 0) {
return -1;
}
if (bytes == 0) {
int _errno = 0;
if (nina_socket_errno(&_errno) != 0 || _errno != 0) {
return -1;
}
}
return bytes;
}
// Check from the upper layer if the socket is bound, if not then auto-bind it first.
int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t port) {
uint16_t size = 2;
uint16_t bytes = 0;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_SENDTO,
4, ARG_16BITS, NINA_ARGS(ARG_BYTE(fd), ARG_WORD((*(uint32_t *)ip)), ARG_SHORT(__REVSH(port)), {len, buf}),
1, ARG_8BITS, NINA_VALS({&size, &bytes})) != 0) {
return -1;
}
// Only args sizes (not args themselves) are reversed in read_response().
bytes = __REVSH(bytes);
if (bytes == 0) {
int _errno = 0;
if (nina_socket_errno(&_errno) != 0 || _errno != 0) {
return -1;
}
}
return bytes;
}
// Check from the upper layer if the socket is bound, if not then auto-bind it first.
int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t *port) {
uint16_t bytes = len;
uint16_t port_len = 2;
uint16_t ip_len = NINA_IPV4_ADDR_LEN;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_RECVFROM,
2, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd), ARG_SHORT(bytes)),
3, ARG_16BITS, NINA_VALS({&ip_len, ip}, {&port_len, port}, {&bytes, buf})) != 0) {
return -1;
}
if (bytes == 0) {
int _errno = 0;
if (nina_socket_errno(&_errno) != 0 || _errno != 0) {
return -1;
}
}
return bytes;
}
int nina_socket_ioctl(int fd, uint32_t cmd, void *argval, uint32_t arglen) {
uint16_t len = arglen;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_IOCTL,
3, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd), ARG_WORD(cmd), {len, argval}),
1, ARG_8BITS, NINA_VALS({&len, argval})) != 0 || arglen == 0) {
return -1;
}
return 0;
}
int nina_socket_poll(int fd, uint8_t *flags) {
uint16_t flags_len = 1;
if (nina_send_command_read_vals(NINA_CMD_SOCKET_POLL,
1, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd)),
1, ARG_8BITS, NINA_VALS({&flags_len, flags})) != 0) {
return -1;
}
if (*flags & 0x80) {
// lwip_select() failed.
return -1;
}
return 0;
}
int nina_socket_setsockopt(int fd, uint32_t level, uint32_t optname, const void *optval, uint16_t optlen) {
if (nina_send_command_read_ack(
NINA_CMD_SOCKET_SETSOCKOPT,
3, ARG_8BITS,
NINA_ARGS(ARG_BYTE(fd), ARG_WORD(optname), {optlen, optval})) != SPI_ACK) {
return -1;
}
return 0;
}
int nina_socket_getsockopt(int fd, uint32_t level, uint32_t optname, void *optval, uint16_t optlen) {
if (nina_send_command_read_vals(
NINA_CMD_SOCKET_GETSOCKOPT,
3, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd), ARG_WORD(optname), ARG_BYTE(optlen)),
1, ARG_8BITS, NINA_VALS({&optlen, optval})) != 0 || optlen == 0) {
return -1;
}
return 0;
}
#endif // MICROPY_PY_NINAW10