zephyr/drivers/wifi/winc1500/wifi_winc1500.c

1117 lines
27 KiB
C

/**
* Copyright (c) 2017 IpTronix
*
* SPDX-License-Identifier: Apache-2.0
*/
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_WIFI_LEVEL
#define SYS_LOG_DOMAIN "dev/winc1500"
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_OFF)
#define NET_LOG_ENABLED 1
#endif
#include <logging/sys_log.h>
#include <zephyr.h>
#include <kernel.h>
#include <device.h>
#include <string.h>
#include <errno.h>
#include <net/net_pkt.h>
#include <net/net_if.h>
#include <net/net_l2.h>
#include <net/net_context.h>
#include <net/net_offload.h>
#include <net/wifi_mgmt.h>
#include <misc/printk.h>
/* We do not need <socket/include/socket.h>
* It seems there is a bug in ASF side: if OS is already defining sockaddr
* and all, ASF will not need to define it. Unfortunately its socket.h does
* but also defines some NM API functions there (??), so we need to redefine
* those here.
*/
#define __SOCKET_H__
#define HOSTNAME_MAX_SIZE (64)
#include <driver/include/m2m_wifi.h>
#include <socket/include/m2m_socket_host_if.h>
NMI_API void socketInit(void);
typedef void (*tpfAppSocketCb) (SOCKET sock, uint8 u8Msg, void *pvMsg);
typedef void (*tpfAppResolveCb) (uint8 *pu8DomainName, uint32 u32ServerIP);
NMI_API void registerSocketCallback(tpfAppSocketCb socket_cb,
tpfAppResolveCb resolve_cb);
NMI_API SOCKET socket(uint16 u16Domain, uint8 u8Type, uint8 u8Flags);
NMI_API sint8 bind(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen);
NMI_API sint8 listen(SOCKET sock, uint8 backlog);
NMI_API sint8 accept(SOCKET sock, struct sockaddr *addr, uint8 *addrlen);
NMI_API sint8 connect(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen);
NMI_API sint16 recv(SOCKET sock, void *pvRecvBuf,
uint16 u16BufLen, uint32 u32Timeoutmsec);
NMI_API sint16 send(SOCKET sock, void *pvSendBuffer,
uint16 u16SendLength, uint16 u16Flags);
NMI_API sint16 sendto(SOCKET sock, void *pvSendBuffer,
uint16 u16SendLength, uint16 flags,
struct sockaddr *pstrDestAddr, uint8 u8AddrLen);
enum socket_errors {
SOCK_ERR_NO_ERROR = 0,
SOCK_ERR_INVALID_ADDRESS = -1,
SOCK_ERR_ADDR_ALREADY_IN_USE = -2,
SOCK_ERR_MAX_TCP_SOCK = -3,
SOCK_ERR_MAX_UDP_SOCK = -4,
SOCK_ERR_INVALID_ARG = -6,
SOCK_ERR_MAX_LISTEN_SOCK = -7,
SOCK_ERR_INVALID = -9,
SOCK_ERR_ADDR_IS_REQUIRED = -11,
SOCK_ERR_CONN_ABORTED = -12,
SOCK_ERR_TIMEOUT = -13,
SOCK_ERR_BUFFER_FULL = -14,
};
enum socket_messages {
SOCKET_MSG_BIND = 1,
SOCKET_MSG_LISTEN,
SOCKET_MSG_DNS_RESOLVE,
SOCKET_MSG_ACCEPT,
SOCKET_MSG_CONNECT,
SOCKET_MSG_RECV,
SOCKET_MSG_SEND,
SOCKET_MSG_SENDTO,
SOCKET_MSG_RECVFROM,
};
typedef struct {
sint8 status;
} tstrSocketBindMsg;
typedef struct {
sint8 status;
} tstrSocketListenMsg;
typedef struct {
SOCKET sock;
struct sockaddr_in strAddr;
} tstrSocketAcceptMsg;
typedef struct {
SOCKET sock;
sint8 s8Error;
} tstrSocketConnectMsg;
typedef struct {
uint8 *pu8Buffer;
sint16 s16BufferSize;
uint16 u16RemainingSize;
struct sockaddr_in strRemoteAddr;
} tstrSocketRecvMsg;
#include <driver/include/m2m_wifi.h>
#include <socket/include/m2m_socket_host_if.h>
#if defined(CONFIG_WIFI_WINC1500_REGION_NORTH_AMERICA)
#define WINC1500_REGION NORTH_AMERICA
#elif defined(CONFIG_WIFI_WINC1500_REGION_EUROPE)
#define WINC1500_REGION EUROPE
#elif defined(CONFIG_WIFI_WINC1500_REGION_ASIA)
#define WINC1500_REGION ASIA
#endif
#define WINC1500_BIND_TIMEOUT 500
#define WINC1500_LISTEN_TIMEOUT 500
NET_BUF_POOL_DEFINE(winc1500_tx_pool, CONFIG_WIFI_WINC1500_BUF_CTR,
CONFIG_WIFI_WINC1500_MAX_PACKET_SIZE, 0, NULL);
NET_BUF_POOL_DEFINE(winc1500_rx_pool, CONFIG_WIFI_WINC1500_BUF_CTR,
CONFIG_WIFI_WINC1500_MAX_PACKET_SIZE, 0, NULL);
K_THREAD_STACK_MEMBER(winc1500_stack, CONFIG_WIFI_WINC1500_THREAD_STACK_SIZE);
struct k_thread winc1500_thread_data;
struct socket_data {
struct net_context *context;
net_context_connect_cb_t connect_cb;
net_tcp_accept_cb_t accept_cb;
net_context_send_cb_t send_cb;
net_context_recv_cb_t recv_cb;
void *connect_user_data;
void *send_user_data;
void *recv_user_data;
void *accept_user_data;
struct net_pkt *rx_pkt;
struct net_buf *pkt_buf;
int ret_code;
struct k_sem wait_sem;
};
struct winc1500_data {
struct socket_data socket_data[
CONFIG_WIFI_WINC1500_OFFLOAD_MAX_SOCKETS];
struct net_if *iface;
unsigned char mac[6];
scan_result_cb_t scan_cb;
u8_t scan_result;
bool connecting;
bool connected;
};
static struct winc1500_data w1500_data;
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_OFF)
static void stack_stats(void)
{
net_analyze_stack("WINC1500 stack",
K_THREAD_STACK_BUFFER(winc1500_stack),
K_THREAD_STACK_SIZEOF(winc1500_stack));
}
static char *socket_error_string(s8_t err)
{
switch (err) {
case SOCK_ERR_NO_ERROR:
return "Successful socket operation";
case SOCK_ERR_INVALID_ADDRESS:
return "Socket address is invalid."
"The socket operation cannot be completed successfully"
" without specifying a specific address. "
"For example: bind is called without specifying"
" a port number";
case SOCK_ERR_ADDR_ALREADY_IN_USE:
return "Socket operation cannot bind on the given address."
" With socket operations, only one IP address per "
"socket is permitted. Any attempt for a new socket "
"to bind with an IP address already bound to another "
"open socket, will return the following error code. "
"States that bind operation failed.";
case SOCK_ERR_MAX_TCP_SOCK:
return "Exceeded the maximum number of TCP sockets."
"A maximum number of TCP sockets opened simultaneously"
" is defined through TCP_SOCK_MAX. It is not permitted"
" to exceed that number at socket creation."
" Identifies that @ref socket operation failed.";
case SOCK_ERR_MAX_UDP_SOCK:
return "Exceeded the maximum number of UDP sockets."
"A maximum number of UDP sockets opened simultaneously"
" is defined through UDP_SOCK_MAX. It is not permitted"
" to exceed that number at socket creation."
" Identifies that socket operation failed";
case SOCK_ERR_INVALID_ARG:
return "An invalid argument is passed to a function.";
case SOCK_ERR_MAX_LISTEN_SOCK:
return "Exceeded the maximum number of TCP passive listening "
"sockets. Identifies Identifies that listen operation"
" failed.";
case SOCK_ERR_INVALID:
return "The requested socket operation is not valid in the "
"current socket state. For example: @ref accept is "
"called on a TCP socket before bind or listen.";
case SOCK_ERR_ADDR_IS_REQUIRED:
return "Destination address is required. Failure to provide "
"the socket address required for the socket operation "
"to be completed. It is generated as an error to the "
"sendto function when the address required to send the"
" data to is not known.";
case SOCK_ERR_CONN_ABORTED:
return "The socket is closed by the peer. The local socket is "
"also closed.";
case SOCK_ERR_TIMEOUT:
return "The socket pending operation has timedout.";
case SOCK_ERR_BUFFER_FULL:
return "No buffer space available to be used for the requested"
" socket operation.";
default:
return "Unknown";
}
}
static char *wifi_cb_msg_2_str(u8_t message_type)
{
switch (message_type) {
case M2M_WIFI_RESP_CURRENT_RSSI:
return "M2M_WIFI_RESP_CURRENT_RSSI";
case M2M_WIFI_REQ_WPS:
return "M2M_WIFI_REQ_WPS";
case M2M_WIFI_RESP_IP_CONFIGURED:
return "M2M_WIFI_RESP_IP_CONFIGURED";
case M2M_WIFI_RESP_IP_CONFLICT:
return "M2M_WIFI_RESP_IP_CONFLICT";
case M2M_WIFI_RESP_CLIENT_INFO:
return "M2M_WIFI_RESP_CLIENT_INFO";
case M2M_WIFI_RESP_GET_SYS_TIME:
return "M2M_WIFI_RESP_GET_SYS_TIME";
case M2M_WIFI_REQ_SEND_ETHERNET_PACKET:
return "M2M_WIFI_REQ_SEND_ETHERNET_PACKET";
case M2M_WIFI_RESP_ETHERNET_RX_PACKET:
return "M2M_WIFI_RESP_ETHERNET_RX_PACKET";
case M2M_WIFI_RESP_CON_STATE_CHANGED:
return "M2M_WIFI_RESP_CON_STATE_CHANGED";
case M2M_WIFI_REQ_DHCP_CONF:
return "Response indicating that IP address was obtained.";
case M2M_WIFI_RESP_SCAN_DONE:
return "M2M_WIFI_RESP_SCAN_DONE";
case M2M_WIFI_RESP_SCAN_RESULT:
return "M2M_WIFI_RESP_SCAN_RESULT";
case M2M_WIFI_RESP_PROVISION_INFO:
return "M2M_WIFI_RESP_PROVISION_INFO";
default:
return "UNKNOWN";
}
}
static char *socket_message_to_string(u8_t message)
{
switch (message) {
case SOCKET_MSG_BIND:
return "Bind socket event.";
case SOCKET_MSG_LISTEN:
return "Listen socket event.";
case SOCKET_MSG_DNS_RESOLVE:
return "DNS Resolution event.";
case SOCKET_MSG_ACCEPT:
return "Accept socket event.";
case SOCKET_MSG_CONNECT:
return "Connect socket event.";
case SOCKET_MSG_RECV:
return "Receive socket event.";
case SOCKET_MSG_SEND:
return "Send socket event.";
case SOCKET_MSG_SENDTO:
return "Sendto socket event.";
case SOCKET_MSG_RECVFROM:
return "Recvfrom socket event.";
default:
return "UNKNOWN.";
}
}
#endif /* (SYS_LOG_LEVEL > SYS_LOG_LEVEL_OFF) */
/**
* This function is called when the socket is to be opened.
*/
static int winc1500_get(sa_family_t family,
enum net_sock_type type,
enum net_ip_protocol ip_proto,
struct net_context **context)
{
struct socket_data *sd;
if (family != AF_INET) {
SYS_LOG_ERR("Only AF_INET is supported!");
return -1;
}
(*context)->offload_context = (void *)(sint32)socket(family, type, 0);
if ((*context)->offload_context < 0) {
SYS_LOG_ERR("socket error!");
return -1;
}
sd = &w1500_data.socket_data[(int)(*context)->offload_context];
k_sem_init(&sd->wait_sem, 0, 1);
sd->context = *context;
return 0;
}
/**
* This function is called when user wants to bind to local IP address.
*/
static int winc1500_bind(struct net_context *context,
const struct sockaddr *addr,
socklen_t addrlen)
{
SOCKET socket = (int)context->offload_context;
int ret;
/* FIXME atmel winc1500 don't support bind on null port */
if (net_sin(addr)->sin_port == 0) {
return 0;
}
ret = bind((int)context->offload_context, (struct sockaddr *)addr,
addrlen);
if (ret) {
SYS_LOG_ERR("bind error %d %s!",
ret, socket_message_to_string(ret));
return ret;
}
if (k_sem_take(&w1500_data.socket_data[socket].wait_sem,
WINC1500_BIND_TIMEOUT)) {
SYS_LOG_ERR("bind error timeout expired");
return -ETIMEDOUT;
}
return w1500_data.socket_data[socket].ret_code;
}
/**
* This function is called when user wants to mark the socket
* to be a listening one.
*/
static int winc1500_listen(struct net_context *context, int backlog)
{
SOCKET socket = (int)context->offload_context;
int ret;
ret = listen((int)context->offload_context, backlog);
if (ret) {
SYS_LOG_ERR("listen error %d %s!",
ret, socket_error_string(ret));
return ret;
}
if (k_sem_take(&w1500_data.socket_data[socket].wait_sem,
WINC1500_LISTEN_TIMEOUT)) {
return -ETIMEDOUT;
}
return w1500_data.socket_data[socket].ret_code;
}
/**
* This function is called when user wants to create a connection
* to a peer host.
*/
static int winc1500_connect(struct net_context *context,
const struct sockaddr *addr,
socklen_t addrlen,
net_context_connect_cb_t cb,
s32_t timeout,
void *user_data)
{
SOCKET socket = (int)context->offload_context;
int ret;
w1500_data.socket_data[socket].connect_cb = cb;
w1500_data.socket_data[socket].connect_user_data = user_data;
w1500_data.socket_data[socket].ret_code = 0;
ret = connect(socket, (struct sockaddr *)addr, addrlen);
if (ret) {
SYS_LOG_ERR("connect error %d %s!",
ret, socket_error_string(ret));
return ret;
}
if (timeout != 0 &&
k_sem_take(&w1500_data.socket_data[socket].wait_sem, timeout)) {
return -ETIMEDOUT;
}
return w1500_data.socket_data[socket].ret_code;
}
/**
* This function is called when user wants to accept a connection
* being established.
*/
static int winc1500_accept(struct net_context *context,
net_tcp_accept_cb_t cb,
s32_t timeout,
void *user_data)
{
SOCKET socket = (int)context->offload_context;
int ret;
w1500_data.socket_data[socket].accept_cb = cb;
ret = accept(socket, NULL, 0);
if (ret) {
SYS_LOG_ERR("accept error %d %s!",
ret, socket_error_string(ret));
return ret;
}
if (timeout) {
if (k_sem_take(&w1500_data.socket_data[socket].wait_sem,
timeout)) {
return -ETIMEDOUT;
}
} else {
k_sem_take(&w1500_data.socket_data[socket].wait_sem,
K_FOREVER);
}
return w1500_data.socket_data[socket].ret_code;
}
/**
* This function is called when user wants to send data to peer host.
*/
static int winc1500_send(struct net_pkt *pkt,
net_context_send_cb_t cb,
s32_t timeout,
void *token,
void *user_data)
{
struct net_context *context = pkt->context;
SOCKET socket = (int)context->offload_context;
bool first_frag;
struct net_buf *frag;
int ret;
w1500_data.socket_data[socket].send_cb = cb;
w1500_data.socket_data[socket].send_user_data = user_data;
first_frag = true;
for (frag = pkt->frags; frag; frag = frag->frags) {
u8_t *data_ptr;
u16_t data_len;
if (first_frag) {
data_ptr = net_pkt_ll(pkt);
data_len = net_pkt_ll_reserve(pkt) + frag->len;
first_frag = false;
} else {
data_ptr = frag->data;
data_len = frag->len;
}
ret = send(socket, data_ptr, data_len, 0);
if (ret) {
SYS_LOG_ERR("send error %d %s!",
ret, socket_error_string(ret));
return ret;
}
}
net_pkt_unref(pkt);
return 0;
}
/**
* This function is called when user wants to send data to peer host.
*/
static int winc1500_sendto(struct net_pkt *pkt,
const struct sockaddr *dst_addr,
socklen_t addrlen,
net_context_send_cb_t cb,
s32_t timeout,
void *token,
void *user_data)
{
struct net_context *context = pkt->context;
SOCKET socket = (int)context->offload_context;
bool first_frag;
struct net_buf *frag;
int ret;
w1500_data.socket_data[socket].send_cb = cb;
w1500_data.socket_data[socket].send_user_data = user_data;
first_frag = true;
for (frag = pkt->frags; frag; frag = frag->frags) {
u8_t *data_ptr;
u16_t data_len;
if (first_frag) {
data_ptr = net_pkt_ll(pkt);
data_len = net_pkt_ll_reserve(pkt) + frag->len;
first_frag = false;
} else {
data_ptr = frag->data;
data_len = frag->len;
}
ret = sendto(socket, data_ptr, data_len, 0,
(struct sockaddr *)dst_addr, addrlen);
if (ret) {
SYS_LOG_ERR("send error %d %s!",
ret, socket_error_string(ret));
return ret;
}
}
net_pkt_unref(pkt);
return 0;
}
/**
*/
static int prepare_pkt(struct socket_data *sock_data)
{
/* Get the frame from the buffer */
sock_data->rx_pkt = net_pkt_get_reserve_rx(0, K_NO_WAIT);
if (!sock_data->rx_pkt) {
SYS_LOG_ERR("Could not allocate rx packet");
return -1;
}
/* Reserve a data frag to receive the frame */
sock_data->pkt_buf = net_pkt_get_frag(sock_data->rx_pkt, K_NO_WAIT);
if (!sock_data->pkt_buf) {
SYS_LOG_ERR("Could not allocate data frag");
net_pkt_unref(sock_data->rx_pkt);
return -1;
}
net_pkt_frag_insert(sock_data->rx_pkt, sock_data->pkt_buf);
return 0;
}
/**
* This function is called when user wants to receive data from peer
* host.
*/
static int winc1500_recv(struct net_context *context,
net_context_recv_cb_t cb,
s32_t timeout,
void *user_data)
{
SOCKET socket = (int) context->offload_context;
int ret;
ret = prepare_pkt(&w1500_data.socket_data[socket]);
if (ret) {
SYS_LOG_ERR("Could not reserve packet buffer");
return -ENOMEM;
}
w1500_data.socket_data[socket].recv_cb = cb;
w1500_data.socket_data[socket].recv_user_data = user_data;
ret = recv(socket, w1500_data.socket_data[socket].pkt_buf->data,
CONFIG_WIFI_WINC1500_MAX_PACKET_SIZE, timeout);
if (ret) {
SYS_LOG_ERR("recv error %d %s!",
ret, socket_error_string(ret));
return ret;
}
return 0;
}
/**
* This function is called when user wants to close the socket.
*/
static int winc1500_put(struct net_context *context)
{
return 0;
}
static struct net_offload winc1500_offload = {
.get = winc1500_get,
.bind = winc1500_bind,
.listen = winc1500_listen,
.connect = winc1500_connect,
.accept = winc1500_accept,
.send = winc1500_send,
.sendto = winc1500_sendto,
.recv = winc1500_recv,
.put = winc1500_put,
};
static void handle_wifi_con_state_changed(void *pvMsg)
{
tstrM2mWifiStateChanged *pstrWifiState =
(tstrM2mWifiStateChanged *)pvMsg;
switch (pstrWifiState->u8CurrState) {
case M2M_WIFI_DISCONNECTED:
SYS_LOG_DBG("Disconnected (%u)", pstrWifiState->u8ErrCode);
if (w1500_data.connecting) {
wifi_mgmt_raise_connect_result_event(w1500_data.iface,
pstrWifiState->u8ErrCode ? -EIO : 0);
w1500_data.connecting = false;
break;
}
w1500_data.connected = false;
wifi_mgmt_raise_disconnect_result_event(w1500_data.iface, 0);
break;
case M2M_WIFI_CONNECTED:
SYS_LOG_DBG("Connected (%u)", pstrWifiState->u8ErrCode);
w1500_data.connected = true;
wifi_mgmt_raise_connect_result_event(w1500_data.iface, 0);
break;
case M2M_WIFI_UNDEF:
/* TODO status undefined*/
SYS_LOG_DBG("Undefined?");
break;
}
}
static void handle_wifi_dhcp_conf(void *pvMsg)
{
u8_t *pu8IPAddress = (u8_t *)pvMsg;
struct in_addr addr;
u8_t i;
/* Connected and got IP address*/
SYS_LOG_DBG("Wi-Fi connected, IP is %u.%u.%u.%u",
pu8IPAddress[0], pu8IPAddress[1],
pu8IPAddress[2], pu8IPAddress[3]);
/* TODO at this point the standby mode should be enable
* status = WiFi connected IP assigned
*/
for (i = 0; i < 4; i++) {
addr.s4_addr[i] = pu8IPAddress[i];
}
/* TODO fill in net mask, gateway and lease time */
net_if_ipv4_addr_add(w1500_data.iface, &addr, NET_ADDR_DHCP, 0);
}
static void reset_scan_data(void)
{
w1500_data.scan_cb = NULL;
w1500_data.scan_result = 0;
}
static void handle_scan_result(void *pvMsg)
{
tstrM2mWifiscanResult *pstrScanResult = (tstrM2mWifiscanResult *)pvMsg;
struct wifi_scan_result result;
if (!w1500_data.scan_cb) {
return;
}
if (pstrScanResult->u8AuthType == M2M_WIFI_SEC_OPEN) {
result.security = WIFI_SECURITY_TYPE_NONE;
} else if (pstrScanResult->u8AuthType == M2M_WIFI_SEC_WPA_PSK) {
result.security = WIFI_SECURITY_TYPE_PSK;
} else {
SYS_LOG_DBG("Security %u not supported",
pstrScanResult->u8AuthType);
goto out;
}
memcpy(result.ssid, pstrScanResult->au8SSID, WIFI_SSID_MAX_LEN);
result.ssid_length = strlen(result.ssid);
result.channel = pstrScanResult->u8ch;
result.rssi = pstrScanResult->s8rssi;
w1500_data.scan_cb(w1500_data.iface, 0, &result);
k_yield();
out:
if (w1500_data.scan_result < m2m_wifi_get_num_ap_found()) {
m2m_wifi_req_scan_result(w1500_data.scan_result);
w1500_data.scan_result++;
} else {
w1500_data.scan_cb(w1500_data.iface, 0, NULL);
reset_scan_data();
}
}
static void handle_scan_done(void *pvMsg)
{
tstrM2mScanDone *pstrInfo = (tstrM2mScanDone *)pvMsg;
if (!w1500_data.scan_cb) {
return;
}
if (pstrInfo->s8ScanState != M2M_SUCCESS) {
w1500_data.scan_cb(w1500_data.iface, -EIO, NULL);
reset_scan_data();
SYS_LOG_ERR("Scan failed.");
return;
}
w1500_data.scan_result = 0;
if (pstrInfo->u8NumofCh >= 1) {
SYS_LOG_DBG("Requesting results (%u)",
m2m_wifi_get_num_ap_found());
m2m_wifi_req_scan_result(w1500_data.scan_result);
w1500_data.scan_result++;
} else {
SYS_LOG_DBG("No AP found");
w1500_data.scan_cb(w1500_data.iface, 0, NULL);
reset_scan_data();
}
}
static void winc1500_wifi_cb(u8_t message_type, void *pvMsg)
{
SYS_LOG_DBG("Msg Type %d %s",
message_type, wifi_cb_msg_2_str(message_type));
switch (message_type) {
case M2M_WIFI_RESP_CON_STATE_CHANGED:
handle_wifi_con_state_changed(pvMsg);
break;
case M2M_WIFI_REQ_DHCP_CONF:
handle_wifi_dhcp_conf(pvMsg);
break;
case M2M_WIFI_RESP_SCAN_RESULT:
handle_scan_result(pvMsg);
break;
case M2M_WIFI_RESP_SCAN_DONE:
handle_scan_done(pvMsg);
break;
default:
break;
}
stack_stats();
}
static void handle_socket_msg_connect(struct socket_data *sd, void *pvMsg)
{
tstrSocketConnectMsg *strConnMsg = (tstrSocketConnectMsg *)pvMsg;
SYS_LOG_ERR("CONNECT: socket %d error %d",
strConnMsg->sock, strConnMsg->s8Error);
if (sd->connect_cb) {
sd->connect_cb(sd->context,
strConnMsg->s8Error,
sd->connect_user_data);
}
sd->ret_code = strConnMsg->s8Error;
}
static bool handle_socket_msg_recv(SOCKET sock,
struct socket_data *sd, void *pvMsg)
{
tstrSocketRecvMsg *pstrRx = (tstrSocketRecvMsg *)pvMsg;
if ((pstrRx->pu8Buffer != NULL) && (pstrRx->s16BufferSize > 0)) {
net_buf_add(sd->pkt_buf, pstrRx->s16BufferSize);
net_pkt_set_appdata(sd->rx_pkt, sd->pkt_buf->data);
net_pkt_set_appdatalen(sd->rx_pkt, pstrRx->s16BufferSize);
if (sd->recv_cb) {
sd->recv_cb(sd->context,
sd->rx_pkt,
0,
sd->recv_user_data);
}
} else if (pstrRx->pu8Buffer == NULL) {
if (pstrRx->s16BufferSize == SOCK_ERR_CONN_ABORTED) {
net_pkt_unref(sd->rx_pkt);
return false;
}
}
if (prepare_pkt(sd)) {
SYS_LOG_ERR("Could not reserve packet buffer");
return false;
}
recv(sock, sd->pkt_buf->data,
CONFIG_WIFI_WINC1500_MAX_PACKET_SIZE, K_NO_WAIT);
return true;
}
static void handle_socket_msg_bind(struct socket_data *sd, void *pvMsg)
{
tstrSocketBindMsg *bind_msg = (tstrSocketBindMsg *)pvMsg;
/* Holding a value of ZERO for a successful bind or otherwise
* a negative error code corresponding to the type of error.
*/
if (bind_msg->status) {
SYS_LOG_ERR("BIND: error %d %s",
bind_msg->status,
socket_message_to_string(bind_msg->status));
sd->ret_code = bind_msg->status;
}
}
static void handle_socket_msg_listen(struct socket_data *sd, void *pvMsg)
{
tstrSocketListenMsg *listen_msg = (tstrSocketListenMsg *)pvMsg;
/* Holding a value of ZERO for a successful listen or otherwise
* a negative error code corresponding to the type of error.
*/
if (listen_msg->status) {
SYS_LOG_ERR("winc1500_socket_cb:LISTEN: error %d %s",
listen_msg->status,
socket_message_to_string(listen_msg->status));
sd->ret_code = listen_msg->status;
}
}
static void handle_socket_msg_accept(struct socket_data *sd, void *pvMsg)
{
tstrSocketAcceptMsg *accept_msg = (tstrSocketAcceptMsg *)pvMsg;
/* On a successful accept operation, the return information is
* the socket ID for the accepted connection with the remote peer.
* Otherwise a negative error code is returned to indicate failure
* of the accept operation.
*/
SYS_LOG_DBG("ACCEPT: from %d.%d.%d.%d:%d, new socket is %d",
accept_msg->strAddr.sin_addr.s4_addr[0],
accept_msg->strAddr.sin_addr.s4_addr[1],
accept_msg->strAddr.sin_addr.s4_addr[2],
accept_msg->strAddr.sin_addr.s4_addr[3],
ntohs(accept_msg->strAddr.sin_port),
accept_msg->sock);
if (accept_msg->sock < 0) {
SYS_LOG_ERR("ACCEPT: error %d %s",
accept_msg->sock,
socket_message_to_string(accept_msg->sock));
sd->ret_code = accept_msg->sock;
}
if (sd->accept_cb) {
struct socket_data *a_sd;
int ret;
a_sd = &w1500_data.socket_data[accept_msg->sock];
memcpy(a_sd, sd, sizeof(struct socket_data));
ret = net_context_get(AF_INET, SOCK_STREAM,
IPPROTO_TCP, &a_sd->context);
if (ret < 0) {
SYS_LOG_ERR("Cannot get new net context for ACCEPT");
} else {
a_sd->context->offload_context =
(void *)((int)accept_msg->sock);
sd->accept_cb(a_sd->context,
(struct sockaddr *)&accept_msg->strAddr,
sizeof(struct sockaddr_in),
(accept_msg->sock > 0) ?
0 : accept_msg->sock,
sd->accept_user_data);
}
}
}
static void winc1500_socket_cb(SOCKET sock, uint8 message, void *pvMsg)
{
struct socket_data *sd = &w1500_data.socket_data[sock];
if (message != 6) {
SYS_LOG_DBG(": sock %d Msg %d %s",
sock, message, socket_message_to_string(message));
}
sd->ret_code = 0;
switch (message) {
case SOCKET_MSG_CONNECT:
handle_socket_msg_connect(sd, pvMsg);
k_sem_give(&sd->wait_sem);
break;
case SOCKET_MSG_SEND:
break;
case SOCKET_MSG_RECV:
if (!handle_socket_msg_recv(sock, sd, pvMsg)) {
return;
}
break;
case SOCKET_MSG_BIND:
handle_socket_msg_bind(sd, pvMsg);
k_sem_give(&sd->wait_sem);
break;
case SOCKET_MSG_LISTEN:
handle_socket_msg_listen(sd, pvMsg);
k_sem_give(&sd->wait_sem);
break;
case SOCKET_MSG_ACCEPT:
handle_socket_msg_accept(sd, pvMsg);
k_sem_give(&sd->wait_sem);
break;
}
stack_stats();
}
static void winc1500_thread(void)
{
while (1) {
while (m2m_wifi_handle_events(NULL) != 0) {
}
k_sleep(K_MSEC(1));
}
}
static int winc1500_mgmt_scan(struct device *dev, scan_result_cb_t cb)
{
if (w1500_data.scan_cb) {
return -EALREADY;
}
w1500_data.scan_cb = cb;
if (m2m_wifi_request_scan(M2M_WIFI_CH_ALL)) {
w1500_data.scan_cb = NULL;
return -EIO;
}
return 0;
}
static int winc1500_mgmt_connect(struct device *dev,
struct wifi_connect_req_params *params)
{
u8_t ssid[M2M_MAX_SSID_LEN];
tuniM2MWifiAuth psk;
u8_t security;
u16_t channel;
void *auth;
memcpy(ssid, params->ssid, params->ssid_length);
ssid[params->ssid_length] = '\0';
if (params->security == WIFI_SECURITY_TYPE_PSK) {
memcpy(psk.au8PSK, params->psk, params->psk_length);
psk.au8PSK[params->psk_length] = '\0';
auth = &psk;
security = M2M_WIFI_SEC_WPA_PSK;
} else {
auth = NULL;
security = M2M_WIFI_SEC_OPEN;
}
if (params->channel == WIFI_CHANNEL_ANY) {
channel = M2M_WIFI_CH_ALL;
} else {
channel = params->channel;
}
SYS_LOG_DBG("Connecting to %s (%u) on channel %u %s security (%s)",
ssid, params->ssid_length,
channel == M2M_WIFI_CH_ALL ? "unknown" : channel,
security == M2M_WIFI_SEC_OPEN ? "without" : "with",
params->psk ? (char *)psk.au8PSK : "");
if (m2m_wifi_connect((char *)ssid, params->ssid_length,
security, auth, channel)) {
return -EIO;
}
w1500_data.connecting = true;
return 0;
}
static int winc1500_mgmt_disconnect(struct device *device)
{
if (!w1500_data.connected) {
return -EALREADY;
}
if (m2m_wifi_disconnect()) {
return -EIO;
}
return 0;
}
static void winc1500_iface_init(struct net_if *iface)
{
SYS_LOG_DBG("eth_init:net_if_set_link_addr:"
"MAC Address %02X:%02X:%02X:%02X:%02X:%02X",
w1500_data.mac[0], w1500_data.mac[1], w1500_data.mac[2],
w1500_data.mac[3], w1500_data.mac[4], w1500_data.mac[5]);
net_if_set_link_addr(iface, w1500_data.mac, sizeof(w1500_data.mac),
NET_LINK_ETHERNET);
iface->if_dev->offload = &winc1500_offload;
w1500_data.iface = iface;
}
static const struct net_wifi_mgmt_offload winc1500_api = {
.iface_api.init = winc1500_iface_init,
.scan = winc1500_mgmt_scan,
.connect = winc1500_mgmt_connect,
.disconnect = winc1500_mgmt_disconnect,
};
static int winc1500_init(struct device *dev)
{
tstrWifiInitParam param = {
.pfAppWifiCb = winc1500_wifi_cb,
};
unsigned char is_valid;
int ret;
ARG_UNUSED(dev);
w1500_data.connecting = false;
w1500_data.connected = false;
ret = m2m_wifi_init(&param);
if (ret) {
SYS_LOG_ERR("m2m_wifi_init return error!(%d)", ret);
return -EIO;
}
m2m_wifi_set_scan_region(WINC1500_REGION);
socketInit();
registerSocketCallback(winc1500_socket_cb, NULL);
m2m_wifi_get_otp_mac_address(w1500_data.mac, &is_valid);
SYS_LOG_DBG("WINC1500 MAC Address from OTP (%d) "
"%02X:%02X:%02X:%02X:%02X:%02X",
is_valid,
w1500_data.mac[0], w1500_data.mac[1], w1500_data.mac[2],
w1500_data.mac[3], w1500_data.mac[4], w1500_data.mac[5]);
m2m_wifi_set_power_profile(PWR_LOW1);
m2m_wifi_set_tx_power(TX_PWR_LOW);
/* monitoring thread for winc wifi callbacks */
k_thread_create(&winc1500_thread_data, winc1500_stack,
CONFIG_WIFI_WINC1500_THREAD_STACK_SIZE,
(k_thread_entry_t)winc1500_thread, NULL, NULL, NULL,
K_PRIO_COOP(CONFIG_WIFI_WINC1500_THREAD_PRIO),
0, K_NO_WAIT);
SYS_LOG_DBG("WINC1500 driver Initialized");
return 0;
}
NET_DEVICE_OFFLOAD_INIT(winc1500, CONFIG_WIFI_WINC1500_NAME,
winc1500_init, &w1500_data, NULL,
CONFIG_WIFI_INIT_PRIORITY, &winc1500_api,
CONFIG_WIFI_WINC1500_MAX_PACKET_SIZE);