| /** |
| * Copyright (c) 2018 Linaro |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #define LOG_LEVEL CONFIG_WIFI_LOG_LEVEL |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(wifi_eswifi_core); |
| |
| #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_THREAD_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) |
| { |
| /* fnt => '"SSID"' */ |
| |
| if (!*str || (*str != '"')) { |
| return -EINVAL; |
| } |
| |
| str++; |
| while (*str && (*str != '"')) { |
| *ssid++ = *str++; |
| } |
| |
| *ssid = '\0'; |
| |
| if (*str != '"') { |
| return -EINVAL; |
| } |
| |
| return -EINVAL; |
| } |
| |
| 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 */ |
| __parse_ssid(str, res->ssid); |
| res->ssid_length = strlen(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(u8_t iface) |
| { |
| /* only one instance */ |
| LOG_DBG("%d", iface); |
| return &eswifi0; |
| } |
| |
| static int __parse_ipv4_address(char *str, char *ssid, u8_t ip[4]) |
| { |
| unsigned int byte = -1; |
| |
| /* fmt => [JOIN ] SSID,192.168.2.18,0,0 */ |
| while (*str) { |
| if (byte == -1) { |
| if (!strncmp(str, ssid, strlen(ssid))) { |
| byte = 0U; |
| 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++; |
| } |
| } |
| } |
| |
| 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)", eswifi->sta.ssid, |
| eswifi->sta.pass); |
| |
| eswifi_lock(eswifi); |
| |
| /* Set SSID */ |
| snprintf(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 */ |
| snprintf(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 */ |
| snprintf(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, |
| (u8_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, u8_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; |
| u8_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(struct device *dev, scan_result_cb_t cb) |
| { |
| struct eswifi_dev *eswifi = dev->driver_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(struct device *dev) |
| { |
| struct eswifi_dev *eswifi = dev->driver_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(struct device *dev, |
| struct wifi_connect_req_params *params) |
| { |
| struct eswifi_dev *eswifi = dev->driver_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(struct device *dev, |
| struct wifi_connect_req_params *params) |
| { |
| struct eswifi_dev *eswifi = dev->driver_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 */ |
| snprintf(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) { |
| snprintf(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) */ |
| snprintf(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 */ |
| snprintf(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; |
| } |
| |
| snprintf(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 */ |
| snprintf(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(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(struct device *dev) |
| { |
| struct eswifi_dev *eswifi = dev->driver_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(struct device *dev) |
| { |
| struct eswifi_dev *eswifi = dev->driver_data; |
| |
| LOG_DBG(""); |
| |
| eswifi->role = ESWIFI_ROLE_CLIENT; |
| k_mutex_init(&eswifi->mutex); |
| |
| eswifi->bus = &eswifi_bus_ops_spi; |
| eswifi->bus->init(eswifi); |
| |
| eswifi->resetn.dev = device_get_binding( |
| DT_INVENTEK_ESWIFI_ESWIFI0_RESETN_GPIOS_CONTROLLER); |
| if (!eswifi->resetn.dev) { |
| LOG_ERR("Failed to initialize GPIO driver: %s", |
| DT_INVENTEK_ESWIFI_ESWIFI0_RESETN_GPIOS_CONTROLLER); |
| return -ENODEV; |
| } |
| eswifi->resetn.pin = DT_INVENTEK_ESWIFI_ESWIFI0_RESETN_GPIOS_PIN; |
| gpio_pin_configure(eswifi->resetn.dev, eswifi->resetn.pin, |
| DT_INVENTEK_ESWIFI_ESWIFI0_RESETN_GPIOS_FLAGS | |
| GPIO_OUTPUT_INACTIVE); |
| |
| eswifi->wakeup.dev = device_get_binding( |
| DT_INVENTEK_ESWIFI_ESWIFI0_WAKEUP_GPIOS_CONTROLLER); |
| if (!eswifi->wakeup.dev) { |
| LOG_ERR("Failed to initialize GPIO driver: %s", |
| DT_INVENTEK_ESWIFI_ESWIFI0_WAKEUP_GPIOS_CONTROLLER); |
| return -ENODEV; |
| } |
| eswifi->wakeup.pin = DT_INVENTEK_ESWIFI_ESWIFI0_WAKEUP_GPIOS_PIN; |
| gpio_pin_configure(eswifi->wakeup.dev, eswifi->wakeup.pin, |
| DT_INVENTEK_ESWIFI_ESWIFI0_WAKEUP_GPIOS_FLAGS | |
| GPIO_OUTPUT_ACTIVE); |
| |
| k_work_q_start(&eswifi->work_q, eswifi_work_q_stack, |
| K_THREAD_STACK_SIZEOF(eswifi_work_q_stack), |
| CONFIG_SYSTEM_WORKQUEUE_PRIORITY - 1); |
| |
| k_work_init(&eswifi->request_work, eswifi_request_work); |
| |
| 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, CONFIG_WIFI_ESWIFI_NAME, |
| eswifi_init, &eswifi0, NULL, |
| CONFIG_WIFI_INIT_PRIORITY, &eswifi_offload_api, 1500); |