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