text: /**
   * Copyright (c) 2018 Linaro
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #if defined(CONFIG_WIFI_ESWIFI_BUS_UART)
  #define DT_DRV_COMPAT inventek_eswifi_uart
  #else
  #define DT_DRV_COMPAT inventek_eswifi
  #endif
  
  #include "eswifi_log.h"
  LOG_MODULE_REGISTER(LOG_MODULE_NAME);
  
  #include <zephyr.h>
  #include <kernel.h>
  #include <device.h>
  #include <string.h>
  #include <errno.h>
  #include <drivers/gpio.h>
  #include <net/net_pkt.h>
  #include <net/net_if.h>
  #include <net/net_context.h>
  #include <net/net_offload.h>
  #include <net/wifi_mgmt.h>
  
  #include <net/ethernet.h>
  #include <net_private.h>
  #include <net/net_core.h>
  #include <net/net_pkt.h>
  
  #include <stdio.h>
  #include <stdlib.h>
  
  #include <sys/printk.h>
  
  #include "eswifi.h"
  
  #define ESWIFI_WORKQUEUE_STACK_SIZE 1024
  K_KERNEL_STACK_DEFINE(eswifi_work_q_stack, ESWIFI_WORKQUEUE_STACK_SIZE);
  
  static struct eswifi_dev eswifi0; /* static instance */
  
  static int eswifi_reset(struct eswifi_dev *eswifi)
  {
  	gpio_pin_set(eswifi->resetn.dev, eswifi->resetn.pin, 0);
  	k_sleep(K_MSEC(10));
  	gpio_pin_set(eswifi->resetn.dev, eswifi->resetn.pin, 1);
  	gpio_pin_set(eswifi->wakeup.dev, eswifi->wakeup.pin, 1);
  	k_sleep(K_MSEC(500));
  
  	/* fetch the cursor */
  	return eswifi_request(eswifi, NULL, 0, eswifi->buf,
  			      sizeof(eswifi->buf));
  }
  
  static inline int __parse_ssid(char *str, char *ssid)
  {
  	int i = 0;
  
  	/* fmt => "SSID" */
  
  	if (*str != '"') {
  		return 0;
  	}
  	str++;
  
  	while (*str && (*str != '"') && i < WIFI_SSID_MAX_LEN) {
  		ssid[i++] = *str++;
  	}
  
  	if (*str != '"') {
  		return 0;
  	}
  
  	return i;
  }
  
  static void __parse_scan_res(char *str, struct wifi_scan_result *res)
  {
  	int field = 0;
  
  	/* fmt => #001,"SSID",MACADDR,RSSI,BITRATE,MODE,SECURITY,BAND,CHANNEL */
  
  	while (*str) {
  		if (*str != ',') {
  			str++;
  			continue;
  		}
  
  		if (!*++str) {
  			break;
  		}
  
  		switch (++field) {
  		case 1: /* SSID */
  			res->ssid_length = __parse_ssid(str, res->ssid);
  			str += res->ssid_length;
  			break;
  		case 2: /* mac addr */
  			break;
  		case 3: /* RSSI */
  			res->rssi = atoi(str);
  			break;
  		case 4: /* bitrate */
  			break;
  		case 5: /* mode */
  			break;
  		case 6: /* security */
  			if (!strncmp(str, "Open", 4)) {
  				res->security = WIFI_SECURITY_TYPE_NONE;
  			} else {
  				res->security = WIFI_SECURITY_TYPE_PSK;
  			}
  			break;
  		case 7: /* band */
  			break;
  		case 8: /* channel */
  			res->channel = atoi(str);
  			break;
  		}
  
  	}
  }
  
  int eswifi_at_cmd_rsp(struct eswifi_dev *eswifi, char *cmd, char **rsp)
  {
  	const char startstr[] = "\r\n";
  	const char endstr[] = "\r\nOK\r\n>";
  	int i, len, rsplen = -EINVAL;
  
  	len = eswifi_request(eswifi, cmd, strlen(cmd), eswifi->buf,
  			     sizeof(eswifi->buf));
  	if (len < 0) {
  		return -EIO;
  	}
  
  	/*
  	 * Check response, format should be "\r\n[DATA]\r\nOK\r\n>"
  	 * Data is in arbitrary format (not only ASCII)
  	 */
  
  	/* Check start characters */
  	if (strncmp(eswifi->buf, startstr, strlen(startstr))) {
  		return -EINVAL;
  	}
  
  	if (len < sizeof(endstr) - 1 + sizeof(startstr) - 1) {
  		return -EINVAL;
  	}
  
  	/* Check end characters */
  	for (i = len - sizeof(endstr); i > 0; i--) {
  		if (!strncmp(&eswifi->buf[i], endstr, 7)) {
  			if (rsp) {
  				eswifi->buf[i] = '\0';
  				*rsp = &eswifi->buf[2];
  				rsplen = &eswifi->buf[i] - *rsp;
  			} else {
  				rsplen = 0;
  			}
  			break;
  		}
  	}
  
  	return rsplen;
  }
  
  int eswifi_at_cmd(struct eswifi_dev *eswifi, char *cmd)
  {
  	return eswifi_at_cmd_rsp(eswifi, cmd, NULL);
  }
  
  struct eswifi_dev *eswifi_by_iface_idx(uint8_t iface)
  {
  	/* only one instance */
  	LOG_DBG("%d", iface);
  	return &eswifi0;
  }
  
  static int __parse_ipv4_address(char *str, char *ssid, uint8_t ip[4])
  {
  	int byte = -1;
  
  	/* fmt => [JOIN   ] SSID,192.168.2.18,0,0 */
  	while (*str && byte < 4) {
  		if (byte == -1) {
  			if (!strncmp(str, ssid, strlen(ssid))) {
  				byte = 0;
  				str += strlen(ssid);
  			}
  			str++;
  			continue;
  		}
  
  		ip[byte++] = atoi(str);
  		while (*str && (*str++ != '.')) {
  		}
  	}
  
  	return 0;
  }
  
  static void eswifi_scan(struct eswifi_dev *eswifi)
  {
  	char cmd[] = "F0\r";
  	char *data;
  	int i, ret;
  
  	LOG_DBG("");
  
  	eswifi_lock(eswifi);
  
  	ret = eswifi_at_cmd_rsp(eswifi, cmd, &data);
  	if (ret < 0) {
  		eswifi->scan_cb(eswifi->iface, -EIO, NULL);
  		eswifi_unlock(eswifi);
  		return;
  	}
  
  	for (i = 0; i < ret; i++) {
  		if (data[i] == '#') {
  			struct wifi_scan_result res = {0};
  
  			__parse_scan_res(&data[i], &res);
  
  			eswifi->scan_cb(eswifi->iface, 0, &res);
  			k_yield();
  
  			while (data[i] && data[i] != '\n') {
  				i++;
  			}
  		}
  	}
  
  	/* WiFi scan is done. */
  	eswifi->scan_cb(eswifi->iface, 0, NULL);
  
  	eswifi_unlock(eswifi);
  }
  
  static int eswifi_connect(struct eswifi_dev *eswifi)
  {
  	char connect[] = "C0\r";
  	struct in_addr addr;
  	char *rsp;
  	int err;
  
  	LOG_DBG("Connecting to %s (pass=%s)", log_strdup(eswifi->sta.ssid),
  		log_strdup(eswifi->sta.pass));
  
  	eswifi_lock(eswifi);
  
  	/* Set SSID */
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "C1=%s\r", eswifi->sta.ssid);
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to set SSID");
  		goto error;
  	}
  
  	/* Set passphrase */
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "C2=%s\r", eswifi->sta.pass);
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to set passphrase");
  		goto error;
  	}
  
  	/* Set Security type */
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "C3=%u\r",
  		 eswifi->sta.security);
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to configure security");
  		goto error;
  	}
  
  	/* Join Network */
  	err = eswifi_at_cmd_rsp(eswifi, connect, &rsp);
  	if (err < 0) {
  		LOG_ERR("Unable to join network");
  		goto error;
  	}
  
  	/* Any IP assigned ? (dhcp offload or manually) */
  	err = __parse_ipv4_address(rsp, eswifi->sta.ssid,
  				   (uint8_t *)&addr.s4_addr);
  	if (err < 0) {
  		LOG_ERR("Unable to retrieve IP address");
  		goto error;
  	}
  
  	LOG_DBG("ip = %d.%d.%d.%d", addr.s4_addr[0], addr.s4_addr[1],
  		   addr.s4_addr[2], addr.s4_addr[3]);
  
  	net_if_ipv4_addr_add(eswifi->iface, &addr, NET_ADDR_DHCP, 0);
  
  	LOG_DBG("Connected!");
  
  	eswifi_unlock(eswifi);
  	return 0;
  
  error:
  	eswifi_unlock(eswifi);
  	return -EIO;
  }
  
  static int eswifi_disconnect(struct eswifi_dev *eswifi)
  {
  	char disconnect[] = "CD\r";
  	int err;
  
  	LOG_DBG("");
  
  	eswifi_lock(eswifi);
  
  	err = eswifi_at_cmd(eswifi, disconnect);
  	if (err < 0) {
  		LOG_ERR("Unable to disconnect network");
  		err = -EIO;
  	}
  
  	eswifi_unlock(eswifi);
  
  	return err;
  }
  
  static void eswifi_request_work(struct k_work *item)
  {
  	struct eswifi_dev *eswifi;
  	int err;
  
  	LOG_DBG("");
  
  	eswifi = CONTAINER_OF(item, struct eswifi_dev, request_work);
  
  	switch (eswifi->req) {
  	case ESWIFI_REQ_CONNECT:
  		err = eswifi_connect(eswifi);
  		wifi_mgmt_raise_connect_result_event(eswifi->iface, err);
  		break;
  	case ESWIFI_REQ_DISCONNECT:
  		err = eswifi_disconnect(eswifi);
  		wifi_mgmt_raise_disconnect_result_event(eswifi->iface, err);
  		break;
  	case ESWIFI_REQ_SCAN:
  		eswifi_scan(eswifi);
  		break;
  	case ESWIFI_REQ_NONE:
  	default:
  		break;
  	}
  }
  
  static int eswifi_get_mac_addr(struct eswifi_dev *eswifi, uint8_t addr[6])
  {
  	char cmd[] = "Z5\r";
  	int ret, i, byte = 0;
  	char *rsp;
  
  	ret = eswifi_at_cmd_rsp(eswifi, cmd, &rsp);
  	if (ret < 0) {
  		return ret;
  	}
  
  	/* format is "ff:ff:ff:ff:ff:ff" */
  	for (i = 0; i < ret && byte < 6; i++) {
  		addr[byte++] = strtol(&rsp[i], NULL, 16);
  		i += 2;
  	}
  
  	if (byte != 6) {
  		return -EIO;
  	}
  
  	return 0;
  }
  
  static void eswifi_iface_init(struct net_if *iface)
  {
  	struct eswifi_dev *eswifi = &eswifi0;
  	uint8_t mac[6];
  
  	LOG_DBG("");
  
  	eswifi_lock(eswifi);
  
  	if (eswifi_reset(eswifi) < 0) {
  		LOG_ERR("Unable to reset device");
  		return;
  	}
  
  	if (eswifi_get_mac_addr(eswifi, mac) < 0) {
  		LOG_ERR("Unable to read MAC address");
  		return;
  	}
  
  	LOG_DBG("MAC Address %02X:%02X:%02X:%02X:%02X:%02X",
  		   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  
  	memcpy(eswifi->mac, mac, sizeof(eswifi->mac));
  	net_if_set_link_addr(iface, eswifi->mac, sizeof(eswifi->mac),
  			     NET_LINK_ETHERNET);
  
  	eswifi->iface = iface;
  
  	eswifi_unlock(eswifi);
  
  	eswifi_offload_init(eswifi);
  #if defined(CONFIG_NET_SOCKETS_OFFLOAD)
  	eswifi_socket_offload_init(eswifi);
  #endif
  
  }
  
  static int eswifi_mgmt_scan(const struct device *dev, scan_result_cb_t cb)
  {
  	struct eswifi_dev *eswifi = dev->data;
  
  	LOG_DBG("");
  
  	eswifi_lock(eswifi);
  
  	eswifi->scan_cb = cb;
  	eswifi->req = ESWIFI_REQ_SCAN;
  	k_work_submit_to_queue(&eswifi->work_q, &eswifi->request_work);
  
  	eswifi_unlock(eswifi);
  
  	return 0;
  }
  
  static int eswifi_mgmt_disconnect(const struct device *dev)
  {
  	struct eswifi_dev *eswifi = dev->data;
  
  	LOG_DBG("");
  
  	eswifi_lock(eswifi);
  
  	eswifi->req = ESWIFI_REQ_DISCONNECT;
  	k_work_submit_to_queue(&eswifi->work_q, &eswifi->request_work);
  
  	eswifi_unlock(eswifi);
  
  	return 0;
  }
  
  static int __eswifi_sta_config(struct eswifi_dev *eswifi,
  			       struct wifi_connect_req_params *params)
  {
  	memcpy(eswifi->sta.ssid, params->ssid, params->ssid_length);
  	eswifi->sta.ssid[params->ssid_length] = '\0';
  
  	switch (params->security) {
  	case WIFI_SECURITY_TYPE_NONE:
  		eswifi->sta.pass[0] = '\0';
  		eswifi->sta.security = ESWIFI_SEC_OPEN;
  		break;
  	case WIFI_SECURITY_TYPE_PSK:
  		memcpy(eswifi->sta.pass, params->psk, params->psk_length);
  		eswifi->sta.pass[params->psk_length] = '\0';
  		eswifi->sta.security = ESWIFI_SEC_WPA2_MIXED;
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	if (params->channel == WIFI_CHANNEL_ANY) {
  		eswifi->sta.channel = 0U;
  	} else {
  		eswifi->sta.channel = params->channel;
  	}
  
  	return 0;
  }
  
  static int eswifi_mgmt_connect(const struct device *dev,
  			       struct wifi_connect_req_params *params)
  {
  	struct eswifi_dev *eswifi = dev->data;
  	int err;
  
  	LOG_DBG("");
  
  	eswifi_lock(eswifi);
  
  	err = __eswifi_sta_config(eswifi, params);
  	if (!err) {
  		eswifi->req = ESWIFI_REQ_CONNECT;
  		k_work_submit_to_queue(&eswifi->work_q,
  				       &eswifi->request_work);
  	}
  
  	eswifi_unlock(eswifi);
  
  	return err;
  }
  
  void eswifi_async_msg(struct eswifi_dev *eswifi, char *msg, size_t len)
  {
  	eswifi_offload_async_msg(eswifi, msg, len);
  }
  
  #if defined(CONFIG_NET_IPV4)
  static int eswifi_mgmt_ap_enable(const struct device *dev,
  				 struct wifi_connect_req_params *params)
  {
  	struct eswifi_dev *eswifi = dev->data;
  	struct net_if_ipv4 *ipv4 = eswifi->iface->config.ip.ipv4;
  	struct net_if_addr *unicast = NULL;
  	int err = -EIO, i;
  
  	LOG_DBG("");
  
  	eswifi_lock(eswifi);
  
  	if (eswifi->role == ESWIFI_ROLE_AP) {
  		err = -EALREADY;
  		goto error;
  	}
  
  	err = __eswifi_sta_config(eswifi, params);
  	if (err) {
  		goto error;
  	}
  
  	/* security */
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "A1=%u\r",
  		 eswifi->sta.security);
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to set Security");
  		goto error;
  	}
  
  	/* Passkey */
  	if (eswifi->sta.security != ESWIFI_SEC_OPEN) {
  		snprintk(eswifi->buf, sizeof(eswifi->buf), "A2=%s\r",
  			 eswifi->sta.pass);
  		err = eswifi_at_cmd(eswifi, eswifi->buf);
  		if (err < 0) {
  			LOG_ERR("Unable to set passkey");
  			goto error;
  		}
  	}
  
  	/* Set SSID (0=no MAC, 1=append MAC) */
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "AS=0,%s\r",
  		 eswifi->sta.ssid);
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to set SSID");
  		goto error;
  	}
  
  	/* Set Channel */
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "AC=%u\r",
  		 eswifi->sta.channel);
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to set Channel");
  		goto error;
  	}
  
  	/* Set IP Address */
  	for (i = 0; ipv4 && i < NET_IF_MAX_IPV4_ADDR; i++) {
  		if (ipv4->unicast[i].is_used) {
  			unicast = &ipv4->unicast[i];
  			break;
  		}
  	}
  
  	if (!unicast) {
  		LOG_ERR("No IPv4 assigned for AP mode");
  		err = -EADDRNOTAVAIL;
  		goto error;
  	}
  
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "Z6=%s\r",
  		 net_sprint_ipv4_addr(&unicast->address.in_addr));
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to active access point");
  		goto error;
  	}
  
  	/* Enable AP */
  	snprintk(eswifi->buf, sizeof(eswifi->buf), "AD\r");
  	err = eswifi_at_cmd(eswifi, eswifi->buf);
  	if (err < 0) {
  		LOG_ERR("Unable to active access point");
  		goto error;
  	}
  
  	eswifi->role = ESWIFI_ROLE_AP;
  
  	eswifi_unlock(eswifi);
  	return 0;
  error:
  	eswifi_unlock(eswifi);
  	return err;
  }
  #else
  static int eswifi_mgmt_ap_enable(const struct device *dev,
  				 struct wifi_connect_req_params *params)
  {
  	LOG_ERR("IPv4 requested for AP mode");
  	return -ENOTSUP;
  }
  #endif /* CONFIG_NET_IPV4 */
  
  static int eswifi_mgmt_ap_disable(const struct device *dev)
  {
  	struct eswifi_dev *eswifi = dev->data;
  	char cmd[] = "AE\r";
  	int err;
  
  	eswifi_lock(eswifi);
  
  	err = eswifi_at_cmd(eswifi, cmd);
  	if (err < 0) {
  		eswifi_unlock(eswifi);
  		return -EIO;
  	}
  
  	eswifi->role = ESWIFI_ROLE_CLIENT;
  
  	eswifi_unlock(eswifi);
  
  	return 0;
  }
  
  static int eswifi_init(const struct device *dev)
  {
  	struct eswifi_dev *eswifi = dev->data;
  
  	LOG_DBG("");
  
  	eswifi->role = ESWIFI_ROLE_CLIENT;
  	k_mutex_init(&eswifi->mutex);
  
  	eswifi->bus = eswifi_get_bus();
  	eswifi->bus->init(eswifi);
  
  	eswifi->resetn.dev = device_get_binding(
  			DT_INST_GPIO_LABEL(0, resetn_gpios));
  	if (!eswifi->resetn.dev) {
  		LOG_ERR("Failed to initialize GPIO driver: %s",
  			    DT_INST_GPIO_LABEL(0, resetn_gpios));
  		return -ENODEV;
  	}
  	eswifi->resetn.pin = DT_INST_GPIO_PIN(0, resetn_gpios);
  	gpio_pin_configure(eswifi->resetn.dev, eswifi->resetn.pin,
  			   DT_INST_GPIO_FLAGS(0, resetn_gpios) |
  			   GPIO_OUTPUT_INACTIVE);
  
  	eswifi->wakeup.dev = device_get_binding(
  			DT_INST_GPIO_LABEL(0, wakeup_gpios));
  	if (!eswifi->wakeup.dev) {
  		LOG_ERR("Failed to initialize GPIO driver: %s",
  			    DT_INST_GPIO_LABEL(0, wakeup_gpios));
  		return -ENODEV;
  	}
  	eswifi->wakeup.pin = DT_INST_GPIO_PIN(0, wakeup_gpios);
  	gpio_pin_configure(eswifi->wakeup.dev, eswifi->wakeup.pin,
  			   DT_INST_GPIO_FLAGS(0, wakeup_gpios) |
  			   GPIO_OUTPUT_ACTIVE);
  
  	k_work_q_start(&eswifi->work_q, eswifi_work_q_stack,
  		       K_KERNEL_STACK_SIZEOF(eswifi_work_q_stack),
  		       CONFIG_SYSTEM_WORKQUEUE_PRIORITY - 1);
  
  	k_work_init(&eswifi->request_work, eswifi_request_work);
  
  	eswifi_shell_register(eswifi);
  
  	return 0;
  }
  
  static const struct net_wifi_mgmt_offload eswifi_offload_api = {
  	.iface_api.init = eswifi_iface_init,
  	.scan		= eswifi_mgmt_scan,
  	.connect	= eswifi_mgmt_connect,
  	.disconnect	= eswifi_mgmt_disconnect,
  	.ap_enable	= eswifi_mgmt_ap_enable,
  	.ap_disable	= eswifi_mgmt_ap_disable,
  };
  
  NET_DEVICE_OFFLOAD_INIT(eswifi_mgmt, DT_INST_LABEL(0),
  			eswifi_init, device_pm_control_nop, &eswifi0, NULL,
  			CONFIG_WIFI_INIT_PRIORITY, &eswifi_offload_api, 1500);
  