Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
/*
 * 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 <kernel.h>
#include <net/net_context.h>
#include <net/net_if.h>
#include <net/net_ip.h>
#include <net/net_pkt.h>
#include <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_BUS(DT_DRV_INST(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

#define ESP_CMD_CWMODE(mode) \
	"AT+"_CWMODE"="STRINGIFY(_CONCAT(ESP_MODE_, mode))

#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 "\""

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_ */