| /* |
| * Copyright (c) 2019 Tobias Svehagen |
| * Copyright (c) 2020 Grinn |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_DRIVERS_WIFI_ESP_AT_ESP_H_ |
| #define ZEPHYR_INCLUDE_DRIVERS_WIFI_ESP_AT_ESP_H_ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/net/net_context.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/net_ip.h> |
| #include <zephyr/net/net_pkt.h> |
| #include <zephyr/net/wifi_mgmt.h> |
| |
| #include "modem_context.h" |
| #include "modem_cmd_handler.h" |
| #include "modem_iface_uart.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /* Define the commands that differ between the AT versions */ |
| #if defined(CONFIG_WIFI_ESP_AT_VERSION_1_7) |
| #define _CWMODE "CWMODE_CUR" |
| #define _CWSAP "CWSAP_CUR" |
| #define _CWJAP "CWJAP_CUR" |
| #define _CIPSTA "CIPSTA_CUR" |
| #define _CIPSTAMAC "CIPSTAMAC_CUR" |
| #define _CIPRECVDATA "+CIPRECVDATA," |
| #define _CIPRECVDATA_END ':' |
| #else |
| #define _CWMODE "CWMODE" |
| #define _CWSAP "CWSAP" |
| #define _CWJAP "CWJAP" |
| #define _CIPSTA "CIPSTA" |
| #define _CIPSTAMAC "CIPSTAMAC" |
| #define _CIPRECVDATA "+CIPRECVDATA:" |
| #define _CIPRECVDATA_END ',' |
| #endif |
| |
| /* |
| * Passive mode differs a bit between firmware versions and the macro |
| * ESP_PROTO_PASSIVE is therefore used to determine what protocol operates in |
| * passive mode. For AT version 1.7 passive mode only affects TCP but in AT |
| * version 2.0 it affects both TCP and UDP. |
| */ |
| #if defined(CONFIG_WIFI_ESP_AT_PASSIVE_MODE) |
| #if defined(CONFIG_WIFI_ESP_AT_VERSION_1_7) |
| #define ESP_PROTO_PASSIVE(proto) (proto == IPPROTO_TCP) |
| #else |
| #define ESP_PROTO_PASSIVE(proto) \ |
| (proto == IPPROTO_TCP || proto == IPPROTO_UDP) |
| #endif /* CONFIG_WIFI_ESP_AT_VERSION_1_7 */ |
| #else |
| #define ESP_PROTO_PASSIVE(proto) 0 |
| #endif /* CONFIG_WIFI_ESP_AT_PASSIVE_MODE */ |
| |
| #define ESP_BUS DT_INST_BUS(0) |
| |
| #if DT_PROP(ESP_BUS, hw_flow_control) == 1 |
| #define _FLOW_CONTROL "3" |
| #else |
| #define _FLOW_CONTROL "0" |
| #endif |
| |
| #if DT_INST_NODE_HAS_PROP(0, target_speed) |
| #define _UART_BAUD DT_INST_PROP(0, target_speed) |
| #else |
| #define _UART_BAUD DT_PROP(ESP_BUS, current_speed) |
| #endif |
| |
| #define _UART_CUR \ |
| STRINGIFY(_UART_BAUD)",8,1,0,"_FLOW_CONTROL |
| |
| #define CONN_CMD_MAX_LEN (sizeof("AT+"_CWJAP"=\"\",\"\"") + \ |
| WIFI_SSID_MAX_LEN + WIFI_PSK_MAX_LEN) |
| |
| #if defined(CONFIG_WIFI_ESP_AT_DNS_USE) |
| #define ESP_MAX_DNS MIN(3, CONFIG_DNS_RESOLVER_MAX_SERVERS) |
| #else |
| #define ESP_MAX_DNS 0 |
| #endif |
| |
| #define ESP_MAX_SOCKETS 5 |
| |
| /* Maximum amount that can be sent with CIPSEND and read with CIPRECVDATA */ |
| #define ESP_MTU 2048 |
| #define CIPRECVDATA_MAX_LEN ESP_MTU |
| |
| #define INVALID_LINK_ID 255 |
| |
| #define MDM_RING_BUF_SIZE CONFIG_WIFI_ESP_AT_MDM_RING_BUF_SIZE |
| #define MDM_RECV_MAX_BUF CONFIG_WIFI_ESP_AT_MDM_RX_BUF_COUNT |
| #define MDM_RECV_BUF_SIZE CONFIG_WIFI_ESP_AT_MDM_RX_BUF_SIZE |
| |
| #define ESP_CMD_TIMEOUT K_SECONDS(10) |
| #define ESP_SCAN_TIMEOUT K_SECONDS(10) |
| #define ESP_CONNECT_TIMEOUT K_SECONDS(20) |
| #define ESP_INIT_TIMEOUT K_SECONDS(10) |
| |
| #define ESP_MODE_NONE 0 |
| #define ESP_MODE_STA 1 |
| #define ESP_MODE_AP 2 |
| #define ESP_MODE_STA_AP 3 |
| |
| #if defined(CONFIG_WIFI_ESP_AT_VERSION_1_7) |
| #define ESP_CMD_CWMODE(mode) "AT+"_CWMODE"="STRINGIFY(_CONCAT(ESP_MODE_, mode)) |
| #else |
| #define ESP_CMD_CWMODE(mode) "AT+"_CWMODE"="STRINGIFY(_CONCAT(ESP_MODE_, mode))",0" |
| #endif |
| |
| #define ESP_CWDHCP_MODE_STATION "1" |
| #if defined(CONFIG_WIFI_ESP_AT_VERSION_1_7) |
| #define ESP_CWDHCP_MODE_SOFTAP "0" |
| #else |
| #define ESP_CWDHCP_MODE_SOFTAP "2" |
| #endif |
| |
| #if defined(CONFIG_WIFI_ESP_AT_VERSION_1_7) |
| #define _ESP_CMD_DHCP_ENABLE(mode, enable) \ |
| "AT+CWDHCP_CUR=" mode "," STRINGIFY(enable) |
| #else |
| #define _ESP_CMD_DHCP_ENABLE(mode, enable) \ |
| "AT+CWDHCP=" STRINGIFY(enable) "," mode |
| #endif |
| |
| #define ESP_CMD_DHCP_ENABLE(mode, enable) \ |
| _ESP_CMD_DHCP_ENABLE(_CONCAT(ESP_CWDHCP_MODE_, mode), enable) |
| |
| #define ESP_CMD_SET_IP(ip, gateway, mask) "AT+"_CIPSTA"=\"" \ |
| ip "\",\"" gateway "\",\"" mask "\"" |
| |
| #if defined(CONFIG_WIFI_ESP_AT_SCAN_PASSIVE) |
| #define ESP_CMD_CWLAP "AT+CWLAP=,,,1,," |
| #else |
| #define ESP_CMD_CWLAP "AT+CWLAP" |
| #endif |
| |
| #if defined(CONFIG_WIFI_ESP_AT_SCAN_RESULT_RSSI_ORDERED) |
| #define ESP_CMD_CWLAPOPT_ORDERED "1" |
| #else |
| #define ESP_CMD_CWLAPOPT_ORDERED "0" |
| #endif |
| |
| #if defined(CONFIG_WIFI_ESP_AT_SCAN_MAC_ADDRESS) |
| /* We need ecn,ssid,rssi,mac,channel */ |
| #define ESP_CMD_CWLAPOPT_MASK "31" |
| #else |
| /* no mac: only need ecn,ssid,rssi,channel */ |
| #define ESP_CMD_CWLAPOPT_MASK "23" |
| #endif |
| |
| #define ESP_CMD_CWLAPOPT(sort, mask) "AT+CWLAPOPT=" sort "," mask |
| |
| extern struct esp_data esp_driver_data; |
| |
| enum esp_socket_flags { |
| ESP_SOCK_IN_USE = BIT(1), |
| ESP_SOCK_CONNECTING = BIT(2), |
| ESP_SOCK_CONNECTED = BIT(3), |
| ESP_SOCK_CLOSE_PENDING = BIT(4), |
| ESP_SOCK_WORKQ_STOPPED = BIT(5), |
| }; |
| |
| struct esp_socket { |
| /* internal */ |
| struct k_mutex lock; |
| atomic_t refcount; |
| |
| uint8_t idx; |
| uint8_t link_id; |
| atomic_t flags; |
| |
| /* socket info */ |
| struct sockaddr dst; |
| |
| /* sem */ |
| union { |
| /* handles blocking receive */ |
| struct k_sem sem_data_ready; |
| |
| /* notifies about reaching 0 refcount */ |
| struct k_sem sem_free; |
| }; |
| |
| /* work */ |
| struct k_work connect_work; |
| struct k_work recvdata_work; |
| struct k_work send_work; |
| struct k_work close_work; |
| |
| /* TX packets fifo */ |
| struct k_fifo tx_fifo; |
| |
| /* net context */ |
| struct net_context *context; |
| net_context_connect_cb_t connect_cb; |
| net_context_recv_cb_t recv_cb; |
| |
| /* callback data */ |
| void *conn_user_data; |
| void *recv_user_data; |
| }; |
| |
| enum esp_data_flag { |
| EDF_STA_CONNECTING = BIT(1), |
| EDF_STA_CONNECTED = BIT(2), |
| EDF_STA_LOCK = BIT(3), |
| EDF_AP_ENABLED = BIT(4), |
| }; |
| |
| /* driver data */ |
| struct esp_data { |
| struct net_if *net_iface; |
| |
| uint8_t flags; |
| uint8_t mode; |
| |
| char conn_cmd[CONN_CMD_MAX_LEN]; |
| |
| /* addresses */ |
| struct in_addr ip; |
| struct in_addr gw; |
| struct in_addr nm; |
| uint8_t mac_addr[6]; |
| struct sockaddr_in dns_addresses[ESP_MAX_DNS]; |
| |
| /* modem context */ |
| struct modem_context mctx; |
| |
| /* modem interface */ |
| struct modem_iface_uart_data iface_data; |
| uint8_t iface_rb_buf[MDM_RING_BUF_SIZE]; |
| |
| /* modem cmds */ |
| struct modem_cmd_handler_data cmd_handler_data; |
| uint8_t cmd_match_buf[MDM_RECV_BUF_SIZE]; |
| |
| /* socket data */ |
| struct esp_socket sockets[ESP_MAX_SOCKETS]; |
| struct esp_socket *rx_sock; |
| |
| /* work */ |
| struct k_work_q workq; |
| struct k_work init_work; |
| struct k_work_delayable ip_addr_work; |
| struct k_work scan_work; |
| struct k_work connect_work; |
| struct k_work mode_switch_work; |
| struct k_work dns_work; |
| |
| scan_result_cb_t scan_cb; |
| |
| /* semaphores */ |
| struct k_sem sem_tx_ready; |
| struct k_sem sem_response; |
| struct k_sem sem_if_ready; |
| }; |
| |
| int esp_offload_init(struct net_if *iface); |
| |
| struct esp_socket *esp_socket_get(struct esp_data *data, |
| struct net_context *context); |
| int esp_socket_put(struct esp_socket *sock); |
| void esp_socket_init(struct esp_data *data); |
| void esp_socket_close(struct esp_socket *sock); |
| void esp_socket_rx(struct esp_socket *sock, struct net_buf *buf, |
| size_t offset, size_t len); |
| void esp_socket_workq_stop_and_flush(struct esp_socket *sock); |
| struct esp_socket *esp_socket_ref(struct esp_socket *sock); |
| void esp_socket_unref(struct esp_socket *sock); |
| |
| static inline |
| struct esp_socket *esp_socket_ref_from_link_id(struct esp_data *data, |
| uint8_t link_id) |
| { |
| if (link_id >= ARRAY_SIZE(data->sockets)) { |
| return NULL; |
| } |
| |
| return esp_socket_ref(&data->sockets[link_id]); |
| } |
| |
| static inline atomic_val_t esp_socket_flags_update(struct esp_socket *sock, |
| atomic_val_t value, |
| atomic_val_t mask) |
| { |
| atomic_val_t flags; |
| |
| do { |
| flags = atomic_get(&sock->flags); |
| } while (!atomic_cas(&sock->flags, flags, (flags & ~mask) | value)); |
| |
| return flags; |
| } |
| |
| static inline |
| atomic_val_t esp_socket_flags_clear_and_set(struct esp_socket *sock, |
| atomic_val_t clear_flags, |
| atomic_val_t set_flags) |
| { |
| return esp_socket_flags_update(sock, set_flags, |
| clear_flags | set_flags); |
| } |
| |
| static inline atomic_val_t esp_socket_flags_set(struct esp_socket *sock, |
| atomic_val_t flags) |
| { |
| return atomic_or(&sock->flags, flags); |
| } |
| |
| static inline bool esp_socket_flags_test_and_clear(struct esp_socket *sock, |
| atomic_val_t flags) |
| { |
| return (atomic_and(&sock->flags, ~flags) & flags); |
| } |
| |
| static inline bool esp_socket_flags_test_and_set(struct esp_socket *sock, |
| atomic_val_t flags) |
| { |
| return (atomic_or(&sock->flags, flags) & flags); |
| } |
| |
| static inline atomic_val_t esp_socket_flags_clear(struct esp_socket *sock, |
| atomic_val_t flags) |
| { |
| return atomic_and(&sock->flags, ~flags); |
| } |
| |
| static inline atomic_val_t esp_socket_flags(struct esp_socket *sock) |
| { |
| return atomic_get(&sock->flags); |
| } |
| |
| static inline struct esp_data *esp_socket_to_dev(struct esp_socket *sock) |
| { |
| return CONTAINER_OF(sock - sock->idx, struct esp_data, sockets); |
| } |
| |
| static inline void __esp_socket_work_submit(struct esp_socket *sock, |
| struct k_work *work) |
| { |
| struct esp_data *data = esp_socket_to_dev(sock); |
| |
| k_work_submit_to_queue(&data->workq, work); |
| } |
| |
| static inline int esp_socket_work_submit(struct esp_socket *sock, |
| struct k_work *work) |
| { |
| int ret = -EBUSY; |
| |
| k_mutex_lock(&sock->lock, K_FOREVER); |
| if (!(esp_socket_flags(sock) & ESP_SOCK_WORKQ_STOPPED)) { |
| __esp_socket_work_submit(sock, work); |
| ret = 0; |
| } |
| k_mutex_unlock(&sock->lock); |
| |
| return ret; |
| } |
| |
| static inline int esp_socket_queue_tx(struct esp_socket *sock, |
| struct net_pkt *pkt) |
| { |
| int ret = -EBUSY; |
| |
| k_mutex_lock(&sock->lock, K_FOREVER); |
| if (!(esp_socket_flags(sock) & ESP_SOCK_WORKQ_STOPPED)) { |
| k_fifo_put(&sock->tx_fifo, pkt); |
| __esp_socket_work_submit(sock, &sock->send_work); |
| ret = 0; |
| } |
| k_mutex_unlock(&sock->lock); |
| |
| return ret; |
| } |
| |
| static inline bool esp_socket_connected(struct esp_socket *sock) |
| { |
| return (esp_socket_flags(sock) & ESP_SOCK_CONNECTED) != 0; |
| } |
| |
| static inline void esp_flags_set(struct esp_data *dev, uint8_t flags) |
| { |
| dev->flags |= flags; |
| } |
| |
| static inline void esp_flags_clear(struct esp_data *dev, uint8_t flags) |
| { |
| dev->flags &= (~flags); |
| } |
| |
| static inline bool esp_flags_are_set(struct esp_data *dev, uint8_t flags) |
| { |
| return (dev->flags & flags) != 0; |
| } |
| |
| static inline enum net_sock_type esp_socket_type(struct esp_socket *sock) |
| { |
| return net_context_get_type(sock->context); |
| } |
| |
| static inline enum net_ip_protocol esp_socket_ip_proto(struct esp_socket *sock) |
| { |
| return net_context_get_ip_proto(sock->context); |
| } |
| |
| static inline int esp_cmd_send(struct esp_data *data, |
| const struct modem_cmd *handlers, |
| size_t handlers_len, const char *buf, |
| k_timeout_t timeout) |
| { |
| return modem_cmd_send(&data->mctx.iface, &data->mctx.cmd_handler, |
| handlers, handlers_len, buf, &data->sem_response, |
| timeout); |
| } |
| |
| void esp_connect_work(struct k_work *work); |
| void esp_recvdata_work(struct k_work *work); |
| void esp_close_work(struct k_work *work); |
| void esp_send_work(struct k_work *work); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* ZEPHYR_INCLUDE_DRIVERS_WIFI_ESP_AT_ESP_H_ */ |