1 /* Iperf Example - iperf implementation
2
3 This example code is in the Public Domain (or CC0 licensed, at your option.)
4
5 Unless required by applicable law or agreed to in writing, this
6 software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7 CONDITIONS OF ANY KIND, either express or implied.
8 */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/socket.h>
13 #include "freertos/FreeRTOS.h"
14 #include "freertos/task.h"
15 #include "esp_check.h"
16 #include "esp_log.h"
17 #include "esp_rom_sys.h"
18 #include "esp_timer.h"
19 #include "iperf.h"
20
21 typedef struct {
22 iperf_cfg_t cfg;
23 bool finish;
24 uint32_t actual_len;
25 uint32_t buffer_len;
26 uint8_t *buffer;
27 uint32_t sockfd;
28 } iperf_ctrl_t;
29
30 static bool s_iperf_is_running = false;
31 static iperf_ctrl_t s_iperf_ctrl;
32 static const char *TAG = "iperf";
33
iperf_is_udp_client(void)34 inline static bool iperf_is_udp_client(void)
35 {
36 return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
37 }
38
iperf_is_udp_server(void)39 inline static bool iperf_is_udp_server(void)
40 {
41 return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
42 }
43
iperf_is_tcp_client(void)44 inline static bool iperf_is_tcp_client(void)
45 {
46 return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
47 }
48
iperf_is_tcp_server(void)49 inline static bool iperf_is_tcp_server(void)
50 {
51 return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
52 }
53
iperf_get_socket_error_code(int sockfd)54 static int iperf_get_socket_error_code(int sockfd)
55 {
56 return errno;
57 }
58
iperf_show_socket_error_reason(const char * str,int sockfd)59 static int iperf_show_socket_error_reason(const char *str, int sockfd)
60 {
61 int err = errno;
62 if (err != 0) {
63 ESP_LOGW(TAG, "%s error, error code: %d, reason: %s", str, err, strerror(err));
64 }
65
66 return err;
67 }
68
iperf_report_task(void * arg)69 static void iperf_report_task(void *arg)
70 {
71 uint32_t interval = s_iperf_ctrl.cfg.interval;
72 uint32_t time = s_iperf_ctrl.cfg.time;
73 TickType_t delay_interval = (interval * 1000) / portTICK_PERIOD_MS;
74 uint32_t cur = 0;
75 double average = 0;
76 double actual_bandwidth = 0;
77 int k = 1;
78
79 printf("\n%16s %s\n", "Interval", "Bandwidth");
80 while (!s_iperf_ctrl.finish) {
81 vTaskDelay(delay_interval);
82 actual_bandwidth = (s_iperf_ctrl.actual_len / 1e6 * 8) / interval;
83 printf("%4d-%4d sec %.2f Mbits/sec\n", cur, cur + interval,
84 actual_bandwidth);
85 cur += interval;
86 average = ((average * (k - 1) / k) + (actual_bandwidth / k));
87 k++;
88 s_iperf_ctrl.actual_len = 0;
89 if (cur >= time) {
90 printf("%4d-%4d sec %.2f Mbits/sec\n", 0, time,
91 average);
92 break;
93 }
94 }
95
96 s_iperf_ctrl.finish = true;
97 vTaskDelete(NULL);
98 }
99
iperf_start_report(void)100 static esp_err_t iperf_start_report(void)
101 {
102 int ret;
103
104 ret = xTaskCreatePinnedToCore(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL, portNUM_PROCESSORS - 1);
105
106 if (ret != pdPASS) {
107 ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
108 return ESP_FAIL;
109 }
110
111 return ESP_OK;
112 }
113
socket_recv(int recv_socket,struct sockaddr_storage listen_addr,uint8_t type)114 static void socket_recv(int recv_socket, struct sockaddr_storage listen_addr, uint8_t type)
115 {
116 bool udp_recv_start = true;
117 uint8_t *buffer;
118 int want_recv = 0;
119 int actual_recv = 0;
120 socklen_t addr_len = sizeof(struct sockaddr);
121
122 buffer = s_iperf_ctrl.buffer;
123 want_recv = s_iperf_ctrl.buffer_len;
124
125 while (!s_iperf_ctrl.finish) {
126 if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
127 addr_len = sizeof(struct sockaddr_in6);
128 actual_recv = recvfrom(recv_socket, buffer, want_recv, 0, (struct sockaddr *)&listen_addr, &addr_len);
129 } else if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
130 addr_len = sizeof(struct sockaddr_in);
131 actual_recv = recvfrom(recv_socket, buffer, want_recv, 0, (struct sockaddr *)&listen_addr, &addr_len);
132 }
133 if (actual_recv < 0) {
134 if (type == IPERF_TRANS_TYPE_TCP) {
135 iperf_show_socket_error_reason("tcp server recv", recv_socket);
136 }
137 if (type == IPERF_TRANS_TYPE_UDP) {
138 iperf_show_socket_error_reason("udp server recv", recv_socket);
139 }
140 s_iperf_ctrl.finish = true;
141 break;
142 } else {
143 if (udp_recv_start) {
144 iperf_start_report();
145 udp_recv_start = false;
146 }
147 s_iperf_ctrl.actual_len += actual_recv;
148 }
149 }
150 }
151
socket_send(int send_socket,struct sockaddr_storage dest_addr,uint8_t type,int bw_lim)152 static void socket_send(int send_socket, struct sockaddr_storage dest_addr, uint8_t type, int bw_lim)
153 {
154 uint8_t *buffer;
155 int actual_send = 0;
156 int want_send = 0;
157 int period_us = -1;
158 int delay_us = 0;
159 int64_t prev_time = 0;
160 int64_t send_time = 0;
161 int err = 0;
162
163 buffer = s_iperf_ctrl.buffer;
164 want_send = s_iperf_ctrl.buffer_len;
165 iperf_start_report();
166
167 if (bw_lim > 0) {
168 period_us = want_send * 8 / bw_lim;
169 }
170
171 while (!s_iperf_ctrl.finish) {
172 if (period_us > 0) {
173 send_time = esp_timer_get_time();
174 if (actual_send > 0){
175 // Last packet "send" was successful, check how much off the previous loop duration was to the ideal send period. Result will adjust the
176 // next send delay.
177 delay_us += period_us + (int32_t)(prev_time - send_time);
178 } else {
179 // Last packet "send" was not successful. Ideally we should try to catch up the whole previous loop duration (e.g. prev_time - send_time).
180 // However, that's not possible since the most probable reason why the send was unsuccessful is the HW was not able to process the packet.
181 // Hence, we cannot queue more packets with shorter (or no) delay to catch up since we are already at the performance edge. The best we
182 // can do is to reset the send delay (which is probably big negative number) and start all over again.
183 delay_us = 0;
184 }
185 prev_time = send_time;
186 }
187 if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
188 actual_send = sendto(send_socket, buffer, want_send, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
189 } else if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
190 actual_send = sendto(send_socket, buffer, want_send, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in));
191 }
192 if (actual_send != want_send) {
193 if (type == IPERF_TRANS_TYPE_UDP) {
194 err = iperf_get_socket_error_code(send_socket);
195 // ENOMEM is expected under heavy load => do not print it
196 if (err != ENOMEM) {
197 iperf_show_socket_error_reason("udp client send", send_socket);
198 }
199 }
200 if (type == IPERF_TRANS_TYPE_TCP) {
201 iperf_show_socket_error_reason("tcp client send", send_socket);
202 ESP_LOGI(TAG, "tcp client send error\n");
203 break;
204 }
205 } else {
206 s_iperf_ctrl.actual_len += actual_send;
207 }
208 // The send delay may be negative, it indicates we are trying to catch up and hence to not delay the loop at all.
209 if (delay_us > 0) {
210 esp_rom_delay_us(delay_us);
211 }
212 }
213 }
214
iperf_run_tcp_server(void)215 static esp_err_t IRAM_ATTR iperf_run_tcp_server(void)
216 {
217 int listen_socket = -1;
218 int client_socket = -1;
219 int opt = 1;
220 int err = 0;
221 esp_err_t ret = ESP_OK;
222 struct sockaddr_in remote_addr;
223 struct timeval timeout = { 0 };
224 socklen_t addr_len = sizeof(struct sockaddr);
225 struct sockaddr_storage listen_addr = { 0 };
226 struct sockaddr_in6 listen_addr6 = { 0 };
227 struct sockaddr_in listen_addr4 = { 0 };
228
229 ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
230
231 if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
232 // The TCP server listen at the address "::", which means all addresses can be listened to.
233 inet6_aton("::", &listen_addr6.sin6_addr);
234 listen_addr6.sin6_family = AF_INET6;
235 listen_addr6.sin6_port = htons(s_iperf_ctrl.cfg.sport);
236
237 listen_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6);
238 ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
239
240 setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
241 setsockopt(listen_socket, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
242
243 ESP_LOGI(TAG, "Socket created");
244
245 err = bind(listen_socket, (struct sockaddr *)&listen_addr6, sizeof(listen_addr6));
246 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d, IPPROTO: %d", errno, AF_INET6);
247 err = listen(listen_socket, 1);
248 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Error occurred during listen: errno %d", errno);
249
250 timeout.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
251 setsockopt(listen_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
252
253 memcpy(&listen_addr, &listen_addr6, sizeof(listen_addr6));
254 } else if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
255 listen_addr4.sin_family = AF_INET;
256 listen_addr4.sin_port = htons(s_iperf_ctrl.cfg.sport);
257 listen_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.source_ip4;
258
259 listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
260 ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
261
262 setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
263
264 ESP_LOGI(TAG, "Socket created");
265
266 err = bind(listen_socket, (struct sockaddr *)&listen_addr4, sizeof(listen_addr4));
267 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d, IPPROTO: %d", errno, AF_INET);
268
269 err = listen(listen_socket, 5);
270 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Error occurred during listen: errno %d", errno);
271 memcpy(&listen_addr, &listen_addr4, sizeof(listen_addr4));
272 }
273
274 client_socket = accept(listen_socket, (struct sockaddr *)&remote_addr, &addr_len);
275 ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to accept connection: errno %d", errno);
276 ESP_LOGI(TAG, "accept: %s,%d\n", inet_ntoa(remote_addr.sin_addr), htons(remote_addr.sin_port));
277
278 timeout.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
279 setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
280
281 socket_recv(client_socket, listen_addr, IPERF_TRANS_TYPE_TCP);
282 exit:
283 if (client_socket != -1) {
284 close(client_socket);
285 }
286
287 if (listen_socket != -1) {
288 shutdown(listen_socket, 0);
289 close(listen_socket);
290 ESP_LOGI(TAG, "TCP Socket server is closed.");
291 }
292 s_iperf_ctrl.finish = true;
293 return ret;
294 }
295
iperf_run_tcp_client(void)296 static esp_err_t iperf_run_tcp_client(void)
297 {
298 int client_socket = -1;
299 int err = 0;
300 esp_err_t ret = ESP_OK;
301 struct sockaddr_storage dest_addr = { 0 };
302 struct sockaddr_in6 dest_addr6 = { 0 };
303 struct sockaddr_in dest_addr4 = { 0 };
304
305 ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
306
307 if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
308 client_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6);
309 ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
310
311 inet6_aton(s_iperf_ctrl.cfg.destination_ip6, &dest_addr6.sin6_addr);
312 dest_addr6.sin6_family = AF_INET6;
313 dest_addr6.sin6_port = htons(s_iperf_ctrl.cfg.dport);
314
315 err = connect(client_socket, (struct sockaddr *)&dest_addr6, sizeof(struct sockaddr_in6));
316 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to connect: errno %d", errno);
317 ESP_LOGI(TAG, "Successfully connected");
318 memcpy(&dest_addr, &dest_addr6, sizeof(dest_addr6));
319 } else if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
320 client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
321 ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
322
323 dest_addr4.sin_family = AF_INET;
324 dest_addr4.sin_port = htons(s_iperf_ctrl.cfg.dport);
325 dest_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.destination_ip4;
326 err = connect(client_socket, (struct sockaddr *)&dest_addr4, sizeof(struct sockaddr_in));
327 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to connect: errno %d", errno);
328 ESP_LOGI(TAG, "Successfully connected");
329 memcpy(&dest_addr, &dest_addr4, sizeof(dest_addr4));
330 }
331
332 socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_TCP, s_iperf_ctrl.cfg.bw_lim);
333 exit:
334 if (client_socket != -1) {
335 shutdown(client_socket, 0);
336 close(client_socket);
337 ESP_LOGI(TAG, "TCP Socket client is closed.");
338 }
339 s_iperf_ctrl.finish = true;
340 return ret;
341 }
342
iperf_run_udp_server(void)343 static esp_err_t IRAM_ATTR iperf_run_udp_server(void)
344 {
345 int listen_socket = -1;
346 int opt = 1;
347 int err = 0;
348 esp_err_t ret = ESP_OK;
349 struct timeval timeout = { 0 };
350 struct sockaddr_storage listen_addr = { 0 };
351 struct sockaddr_in6 listen_addr6 = { 0 };
352 struct sockaddr_in listen_addr4 = { 0 };
353
354 ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
355
356 if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
357 // The UDP server listen at the address "::", which means all addresses can be listened to.
358 inet6_aton("::", &listen_addr6.sin6_addr);
359 listen_addr6.sin6_family = AF_INET6;
360 listen_addr6.sin6_port = htons(s_iperf_ctrl.cfg.sport);
361
362 listen_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
363 ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
364 ESP_LOGI(TAG, "Socket created");
365
366 setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
367
368 err = bind(listen_socket, (struct sockaddr *)&listen_addr6, sizeof(struct sockaddr_in6));
369 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d", errno);
370 ESP_LOGI(TAG, "Socket bound, port %d", listen_addr6.sin6_port);
371
372 memcpy(&listen_addr, &listen_addr6, sizeof(listen_addr6));
373 } else if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
374 listen_addr4.sin_family = AF_INET;
375 listen_addr4.sin_port = htons(s_iperf_ctrl.cfg.sport);
376 listen_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.source_ip4;
377
378 listen_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
379 ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
380 ESP_LOGI(TAG, "Socket created");
381
382 setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
383
384 err = bind(listen_socket, (struct sockaddr *)&listen_addr4, sizeof(struct sockaddr_in));
385 ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d", errno);
386 ESP_LOGI(TAG, "Socket bound, port %d", listen_addr4.sin_port);
387 memcpy(&listen_addr, &listen_addr4, sizeof(listen_addr4));
388 }
389
390 timeout.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
391 setsockopt(listen_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
392
393 socket_recv(listen_socket, listen_addr, IPERF_TRANS_TYPE_UDP);
394 exit:
395 if (listen_socket != -1) {
396 shutdown(listen_socket, 0);
397 close(listen_socket);
398 }
399 ESP_LOGI(TAG, "Udp socket server is closed.");
400 s_iperf_ctrl.finish = true;
401 return ret;
402 }
403
iperf_run_udp_client(void)404 static esp_err_t iperf_run_udp_client(void)
405 {
406 int client_socket = -1;
407 int opt = 1;
408 esp_err_t ret = ESP_OK;
409 struct sockaddr_storage dest_addr = { 0 };
410 struct sockaddr_in6 dest_addr6 = { 0 };
411 struct sockaddr_in dest_addr4 = { 0 };
412
413 ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
414
415 if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
416 inet6_aton(s_iperf_ctrl.cfg.destination_ip6, &dest_addr6.sin6_addr);
417 dest_addr6.sin6_family = AF_INET6;
418 dest_addr6.sin6_port = htons(s_iperf_ctrl.cfg.dport);
419
420 client_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
421 ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
422 ESP_LOGI(TAG, "Socket created, sending to %s:%d", s_iperf_ctrl.cfg.destination_ip6, s_iperf_ctrl.cfg.dport);
423
424 setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
425 memcpy(&dest_addr, &dest_addr6, sizeof(dest_addr6));
426 } else if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
427 dest_addr4.sin_family = AF_INET;
428 dest_addr4.sin_port = htons(s_iperf_ctrl.cfg.dport);
429 dest_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.destination_ip4;
430
431 client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
432 ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
433 ESP_LOGI(TAG, "Socket created, sending to %d:%d", s_iperf_ctrl.cfg.destination_ip4, s_iperf_ctrl.cfg.dport);
434
435 setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
436 memcpy(&dest_addr, &dest_addr4, sizeof(dest_addr4));
437 }
438
439 socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_UDP, s_iperf_ctrl.cfg.bw_lim);
440 exit:
441 if (client_socket != -1) {
442 shutdown(client_socket, 0);
443 close(client_socket);
444 }
445 s_iperf_ctrl.finish = true;
446 ESP_LOGI(TAG, "UDP Socket client is closed");
447 return ret;
448 }
449
iperf_task_traffic(void * arg)450 static void iperf_task_traffic(void *arg)
451 {
452 if (iperf_is_udp_client()) {
453 iperf_run_udp_client();
454 } else if (iperf_is_udp_server()) {
455 iperf_run_udp_server();
456 } else if (iperf_is_tcp_client()) {
457 iperf_run_tcp_client();
458 } else {
459 iperf_run_tcp_server();
460 }
461
462 if (s_iperf_ctrl.buffer) {
463 free(s_iperf_ctrl.buffer);
464 s_iperf_ctrl.buffer = NULL;
465 }
466 ESP_LOGI(TAG, "iperf exit");
467 s_iperf_is_running = false;
468 vTaskDelete(NULL);
469 }
470
iperf_get_buffer_len(void)471 static uint32_t iperf_get_buffer_len(void)
472 {
473 if (iperf_is_udp_client()) {
474 return (s_iperf_ctrl.cfg.len_send_buf == 0 ? IPERF_UDP_TX_LEN : s_iperf_ctrl.cfg.len_send_buf);
475 } else if (iperf_is_udp_server()) {
476 return IPERF_UDP_RX_LEN;
477 } else if (iperf_is_tcp_client()) {
478 return (s_iperf_ctrl.cfg.len_send_buf == 0 ? IPERF_TCP_TX_LEN : s_iperf_ctrl.cfg.len_send_buf);
479 } else {
480 return IPERF_TCP_RX_LEN;
481 }
482 return 0;
483 }
484
iperf_start(iperf_cfg_t * cfg)485 esp_err_t iperf_start(iperf_cfg_t *cfg)
486 {
487 BaseType_t ret;
488
489 if (!cfg) {
490 return ESP_FAIL;
491 }
492
493 if (s_iperf_is_running) {
494 ESP_LOGW(TAG, "iperf is running");
495 return ESP_FAIL;
496 }
497
498 memset(&s_iperf_ctrl, 0, sizeof(s_iperf_ctrl));
499 memcpy(&s_iperf_ctrl.cfg, cfg, sizeof(*cfg));
500 s_iperf_is_running = true;
501 s_iperf_ctrl.finish = false;
502 s_iperf_ctrl.buffer_len = iperf_get_buffer_len();
503 s_iperf_ctrl.buffer = (uint8_t *)malloc(s_iperf_ctrl.buffer_len);
504 if (!s_iperf_ctrl.buffer) {
505 ESP_LOGE(TAG, "create buffer: not enough memory");
506 return ESP_FAIL;
507 }
508 memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
509 ret = xTaskCreatePinnedToCore(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL, portNUM_PROCESSORS - 1);
510 if (ret != pdPASS) {
511 ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
512 free(s_iperf_ctrl.buffer);
513 s_iperf_ctrl.buffer = NULL;
514 return ESP_FAIL;
515 }
516 return ESP_OK;
517 }
518
iperf_stop(void)519 esp_err_t iperf_stop(void)
520 {
521 if (s_iperf_is_running) {
522 s_iperf_ctrl.finish = true;
523 }
524
525 while (s_iperf_is_running) {
526 ESP_LOGI(TAG, "wait current iperf to stop ...");
527 vTaskDelay(300 / portTICK_PERIOD_MS);
528 }
529 return ESP_OK;
530 }
531