| /** |
| * Copyright (c) 2017 IpTronix |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define LOG_MODULE_NAME wifi_winc1500 |
| #define LOG_LEVEL CONFIG_WIFI_LOG_LEVEL |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/debug/stack.h> |
| #include <zephyr/device.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <zephyr/net/net_pkt.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/net_l2.h> |
| #include <zephyr/net/net_context.h> |
| #include <zephyr/net/net_offload.h> |
| #include <zephyr/net/wifi_mgmt.h> |
| |
| #include <zephyr/sys/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); |
| NMI_API sint8 winc1500_close(SOCKET sock); |
| |
| 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 K_MSEC(500) |
| #define WINC1500_LISTEN_TIMEOUT K_MSEC(500) |
| #define WINC1500_BUF_TIMEOUT K_MSEC(100) |
| |
| 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_KERNEL_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_recv_cb_t recv_cb; |
| void *connect_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; |
| uint8_t scan_result; |
| bool connecting; |
| bool connected; |
| }; |
| |
| static struct winc1500_data w1500_data; |
| |
| #if LOG_LEVEL > LOG_LEVEL_OFF |
| |
| static void stack_stats(void) |
| { |
| log_stack_usage(&winc1500_thread_data); |
| } |
| |
| static char *socket_error_string(int8_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(uint8_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(uint8_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 /* LOG_LEVEL > 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; |
| SOCKET sock; |
| |
| if (family != AF_INET) { |
| LOG_ERR("Only AF_INET is supported!"); |
| return -1; |
| } |
| |
| /* winc1500 atmel uses AF_INET 2 instead of zephyrs AF_INET 1 |
| * we have checked if family is AF_INET so we can hardcode this |
| * for now. |
| */ |
| sock = socket(2, type, 0); |
| if (sock < 0) { |
| LOG_ERR("socket error!"); |
| return -1; |
| } |
| |
| (*context)->offload_context = (void *)(intptr_t)sock; |
| sd = &w1500_data.socket_data[sock]; |
| |
| 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 == 0U) { |
| return 0; |
| } |
| |
| ret = bind((int)context->offload_context, (struct sockaddr *)addr, |
| addrlen); |
| if (ret) { |
| 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)) { |
| 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) { |
| 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, |
| int32_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) { |
| 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, K_MSEC(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, |
| int32_t timeout, |
| void *user_data) |
| { |
| SOCKET socket = (int)context->offload_context; |
| int ret; |
| |
| w1500_data.socket_data[socket].accept_cb = cb; |
| w1500_data.socket_data[socket].accept_user_data = user_data; |
| |
| ret = accept(socket, NULL, 0); |
| if (ret) { |
| 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, |
| K_MSEC(timeout))) { |
| return -ETIMEDOUT; |
| } |
| } |
| |
| 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, |
| int32_t timeout, |
| void *user_data) |
| { |
| struct net_context *context = pkt->context; |
| SOCKET socket = (int)context->offload_context; |
| int ret = 0; |
| struct net_buf *buf; |
| |
| buf = net_buf_alloc(&winc1500_tx_pool, WINC1500_BUF_TIMEOUT); |
| if (!buf) { |
| return -ENOBUFS; |
| } |
| |
| if (net_pkt_read(pkt, buf->data, net_pkt_get_len(pkt))) { |
| ret = -ENOBUFS; |
| goto out; |
| } |
| |
| net_buf_add(buf, net_pkt_get_len(pkt)); |
| |
| ret = send(socket, buf->data, buf->len, 0); |
| if (ret) { |
| LOG_ERR("send error %d %s!", ret, socket_error_string(ret)); |
| goto out; |
| } |
| |
| net_pkt_unref(pkt); |
| out: |
| net_buf_unref(buf); |
| |
| return ret; |
| } |
| |
| /** |
| * 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, |
| int32_t timeout, |
| void *user_data) |
| { |
| struct net_context *context = pkt->context; |
| SOCKET socket = (int)context->offload_context; |
| int ret = 0; |
| struct net_buf *buf; |
| |
| buf = net_buf_alloc(&winc1500_tx_pool, WINC1500_BUF_TIMEOUT); |
| if (!buf) { |
| return -ENOBUFS; |
| } |
| |
| if (net_pkt_read(pkt, buf->data, net_pkt_get_len(pkt))) { |
| ret = -ENOBUFS; |
| goto out; |
| } |
| |
| net_buf_add(buf, net_pkt_get_len(pkt)); |
| |
| ret = sendto(socket, buf->data, buf->len, 0, |
| (struct sockaddr *)dst_addr, addrlen); |
| if (ret) { |
| LOG_ERR("sendto error %d %s!", ret, socket_error_string(ret)); |
| goto out; |
| } |
| |
| net_pkt_unref(pkt); |
| out: |
| net_buf_unref(buf); |
| |
| return ret; |
| } |
| |
| /** |
| */ |
| static int prepare_pkt(struct socket_data *sock_data) |
| { |
| /* Get the frame from the buffer */ |
| sock_data->rx_pkt = net_pkt_rx_alloc_on_iface(w1500_data.iface, |
| K_NO_WAIT); |
| if (!sock_data->rx_pkt) { |
| LOG_ERR("Could not allocate rx packet"); |
| return -1; |
| } |
| |
| /* Reserve a data buffer to receive the frame */ |
| sock_data->pkt_buf = net_buf_alloc(&winc1500_rx_pool, K_NO_WAIT); |
| if (!sock_data->pkt_buf) { |
| LOG_ERR("Could not allocate data buffer"); |
| net_pkt_unref(sock_data->rx_pkt); |
| return -1; |
| } |
| |
| net_pkt_append_buffer(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, |
| int32_t timeout, |
| void *user_data) |
| { |
| SOCKET socket = (int) context->offload_context; |
| int ret; |
| |
| w1500_data.socket_data[socket].recv_cb = cb; |
| w1500_data.socket_data[socket].recv_user_data = user_data; |
| if (!cb) { |
| return 0; |
| } |
| |
| ret = prepare_pkt(&w1500_data.socket_data[socket]); |
| if (ret) { |
| LOG_ERR("Could not reserve packet buffer"); |
| return -ENOMEM; |
| } |
| |
| |
| ret = recv(socket, w1500_data.socket_data[socket].pkt_buf->data, |
| CONFIG_WIFI_WINC1500_MAX_PACKET_SIZE, timeout); |
| if (ret) { |
| 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) |
| { |
| SOCKET sock = (int) context->offload_context; |
| struct socket_data *sd = &w1500_data.socket_data[sock]; |
| int ret; |
| |
| memset(&(context->remote), 0, sizeof(struct sockaddr_in)); |
| context->flags &= ~NET_CONTEXT_REMOTE_ADDR_SET; |
| ret = winc1500_close(sock); |
| |
| net_pkt_unref(sd->rx_pkt); |
| |
| memset(sd, 0, sizeof(struct socket_data)); |
| |
| return ret; |
| } |
| |
| 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: |
| 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: |
| 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*/ |
| LOG_DBG("Undefined?"); |
| break; |
| } |
| } |
| |
| static void handle_wifi_dhcp_conf(void *pvMsg) |
| { |
| uint8_t *pu8IPAddress = (uint8_t *)pvMsg; |
| struct in_addr addr; |
| uint8_t i; |
| |
| /* Connected and got IP address*/ |
| 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 = 0U; 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 = 0U; |
| } |
| |
| 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 { |
| 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(); |
| |
| LOG_ERR("Scan failed."); |
| |
| return; |
| } |
| |
| w1500_data.scan_result = 0U; |
| |
| if (pstrInfo->u8NumofCh >= 1) { |
| 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 { |
| LOG_DBG("No AP found"); |
| |
| w1500_data.scan_cb(w1500_data.iface, 0, NULL); |
| reset_scan_data(); |
| } |
| } |
| |
| static void winc1500_wifi_cb(uint8_t message_type, void *pvMsg) |
| { |
| 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; |
| |
| LOG_ERR("CONNECT: socket %d error %d", |
| strConnMsg->sock, strConnMsg->s8Error); |
| |
| if (!strConnMsg->s8Error) { |
| net_context_set_state(sd->context, NET_CONTEXT_CONNECTED); |
| } |
| |
| 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_cursor_init(sd->rx_pkt); |
| |
| if (sd->recv_cb) { |
| sd->recv_cb(sd->context, |
| sd->rx_pkt, |
| NULL, NULL, |
| 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; |
| } |
| } |
| 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) { |
| 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) { |
| 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. |
| */ |
| |
| 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) { |
| 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) { |
| LOG_ERR("Cannot get new net context for ACCEPT"); |
| return; |
| } |
| /* We get a new socket from accept_msg but we need a new |
| * context as well. The new context gives us another socket |
| * so we have to close that one first. |
| */ |
| winc1500_close((int)a_sd->context->offload_context); |
| |
| a_sd->context->offload_context = |
| (void *)((int)accept_msg->sock); |
| /** The iface is reset when getting a new context. */ |
| a_sd->context->iface = sd->context->iface; |
| |
| /** Setup remote */ |
| a_sd->context->remote.sa_family = AF_INET; |
| net_sin(&a_sd->context->remote)->sin_port = |
| accept_msg->strAddr.sin_port; |
| net_sin(&a_sd->context->remote)->sin_addr.s_addr = |
| accept_msg->strAddr.sin_addr.s_addr; |
| a_sd->context->flags |= NET_CONTEXT_REMOTE_ADDR_SET; |
| |
| 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) { |
| 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); |
| |
| 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(const 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; |
| LOG_ERR("Failed to request scan"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int winc1500_mgmt_connect(const struct device *dev, |
| struct wifi_connect_req_params *params) |
| { |
| uint8_t ssid[M2M_MAX_SSID_LEN]; |
| tuniM2MWifiAuth psk; |
| uint8_t security; |
| uint16_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; |
| } |
| |
| LOG_DBG("Connecting to %s (%u) on %s %u %s security (%s)", |
| ssid, params->ssid_length, |
| channel == M2M_WIFI_CH_ALL ? "channel unknown" : "channel", |
| 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(const struct device *dev) |
| { |
| if (!w1500_data.connected) { |
| return -EALREADY; |
| } |
| |
| if (m2m_wifi_disconnect()) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int winc1500_mgmt_ap_enable(const struct device *dev, |
| struct wifi_connect_req_params *params) |
| { |
| tstrM2MAPConfig strM2MAPConfig; |
| |
| memset(&strM2MAPConfig, 0x00, sizeof(tstrM2MAPConfig)); |
| strcpy((char *)&strM2MAPConfig.au8SSID, params->ssid); |
| strM2MAPConfig.u8ListenChannel = params->channel; |
| /** security is hardcoded as open for now */ |
| strM2MAPConfig.u8SecType = M2M_WIFI_SEC_OPEN; |
| /** DHCP: 192.168.1.1 */ |
| strM2MAPConfig.au8DHCPServerIP[0] = 0xC0; |
| strM2MAPConfig.au8DHCPServerIP[1] = 0xA8; |
| strM2MAPConfig.au8DHCPServerIP[2] = 0x01; |
| strM2MAPConfig.au8DHCPServerIP[3] = 0x01; |
| |
| if (m2m_wifi_enable_ap(&strM2MAPConfig) != M2M_SUCCESS) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int winc1500_mgmt_ap_disable(const struct device *dev) |
| { |
| if (m2m_wifi_disable_ap() != M2M_SUCCESS) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static void winc1500_iface_init(struct net_if *iface) |
| { |
| 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 = { |
| .wifi_iface.iface_api.init = winc1500_iface_init, |
| .scan = winc1500_mgmt_scan, |
| .connect = winc1500_mgmt_connect, |
| .disconnect = winc1500_mgmt_disconnect, |
| .ap_enable = winc1500_mgmt_ap_enable, |
| .ap_disable = winc1500_mgmt_ap_disable, |
| }; |
| |
| static int winc1500_init(const 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(¶m); |
| if (ret != M2M_SUCCESS) { |
| LOG_ERR("m2m_wifi_init return error!(%d)", ret); |
| return -EIO; |
| } |
| |
| socketInit(); |
| registerSocketCallback(winc1500_socket_cb, NULL); |
| |
| if (m2m_wifi_get_otp_mac_address(w1500_data.mac, &is_valid) != M2M_SUCCESS) { |
| LOG_ERR("Failed to get MAC address"); |
| } |
| |
| 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]); |
| |
| if (m2m_wifi_set_scan_region(WINC1500_REGION) != M2M_SUCCESS) { |
| LOG_ERR("Failed set scan region"); |
| } |
| |
| if (m2m_wifi_set_power_profile(PWR_LOW1) != M2M_SUCCESS) { |
| LOG_ERR("Failed set power profile"); |
| } |
| |
| if (m2m_wifi_set_tx_power(TX_PWR_LOW) != M2M_SUCCESS) { |
| LOG_ERR("Failed set tx power"); |
| } |
| |
| /* 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); |
| k_thread_name_set(&winc1500_thread_data, "WINC1500"); |
| |
| LOG_DBG("WINC1500 driver Initialized"); |
| |
| return 0; |
| } |
| |
| NET_DEVICE_OFFLOAD_INIT(winc1500, CONFIG_WIFI_WINC1500_NAME, |
| winc1500_init, NULL, &w1500_data, NULL, |
| CONFIG_WIFI_INIT_PRIORITY, &winc1500_api, |
| CONFIG_WIFI_WINC1500_MAX_PACKET_SIZE); |