1 /*
2 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25 * OF SUCH DAMAGE.
26 *
27 * This file is part of the lwIP TCP/IP stack.
28 *
29 * Author: Adam Dunkels <adam@sics.se>
30 *
31 */
32
33 /* lwIP includes. */
34
35 #include <pthread.h>
36 #include "lwip/debug.h"
37 #include "lwip/def.h"
38 #include "lwip/sys.h"
39 #include "lwip/mem.h"
40 #include "arch/sys_arch.h"
41 #include "lwip/stats.h"
42 #include "esp_log.h"
43 #include "esp_compiler.h"
44
45 static const char* TAG = "lwip_arch";
46
47 static sys_mutex_t g_lwip_protect_mutex = NULL;
48
49 static pthread_key_t sys_thread_sem_key;
50 static void sys_thread_sem_free(void* data);
51
52 #if !LWIP_COMPAT_MUTEX
53
54 /**
55 * @brief Create a new mutex
56 *
57 * @param pxMutex pointer of the mutex to create
58 * @return ERR_OK on success, ERR_MEM when out of memory
59 */
60 err_t
sys_mutex_new(sys_mutex_t * pxMutex)61 sys_mutex_new(sys_mutex_t *pxMutex)
62 {
63 *pxMutex = xSemaphoreCreateMutex();
64 if (*pxMutex == NULL) {
65 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: out of mem\r\n"));
66 return ERR_MEM;
67 }
68
69 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex));
70
71 return ERR_OK;
72 }
73
74 /**
75 * @brief Lock a mutex
76 *
77 * @param pxMutex pointer of mutex to lock
78 */
79 void
sys_mutex_lock(sys_mutex_t * pxMutex)80 sys_mutex_lock(sys_mutex_t *pxMutex)
81 {
82 BaseType_t ret = xSemaphoreTake(*pxMutex, portMAX_DELAY);
83
84 LWIP_ASSERT("failed to take the mutex", ret == pdTRUE);
85 }
86
87 /**
88 * @brief Unlock a mutex
89 *
90 * @param pxMutex pointer of mutex to unlock
91 */
92 void
sys_mutex_unlock(sys_mutex_t * pxMutex)93 sys_mutex_unlock(sys_mutex_t *pxMutex)
94 {
95 BaseType_t ret = xSemaphoreGive(*pxMutex);
96
97 LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
98 }
99
100 /**
101 * @brief Delete a mutex
102 *
103 * @param pxMutex pointer of mutex to delete
104 */
105 void
sys_mutex_free(sys_mutex_t * pxMutex)106 sys_mutex_free(sys_mutex_t *pxMutex)
107 {
108 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex));
109 vSemaphoreDelete(*pxMutex);
110 *pxMutex = NULL;
111 }
112
113 #endif /* !LWIP_COMPAT_MUTEX */
114
115 /**
116 * @brief Creates a new semaphore
117 *
118 * @param sem pointer of the semaphore
119 * @param count initial state of the semaphore
120 * @return err_t
121 */
122 err_t
sys_sem_new(sys_sem_t * sem,u8_t count)123 sys_sem_new(sys_sem_t *sem, u8_t count)
124 {
125 LWIP_ASSERT("initial_count invalid (neither 0 nor 1)",
126 (count == 0) || (count == 1));
127
128 *sem = xSemaphoreCreateBinary();
129 if (*sem == NULL) {
130 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_sem_new: out of mem\r\n"));
131 return ERR_MEM;
132 }
133
134 if (count == 1) {
135 BaseType_t ret = xSemaphoreGive(*sem);
136 LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
137 }
138
139 return ERR_OK;
140 }
141
142 /**
143 * @brief Signals a semaphore
144 *
145 * @param sem pointer of the semaphore
146 */
147 void
sys_sem_signal(sys_sem_t * sem)148 sys_sem_signal(sys_sem_t *sem)
149 {
150 BaseType_t ret = xSemaphoreGive(*sem);
151 /* queue full is OK, this is a signal only... */
152 LWIP_ASSERT("sys_sem_signal: sane return value",
153 (ret == pdTRUE) || (ret == errQUEUE_FULL));
154 }
155
156 /*-----------------------------------------------------------------------------------*/
157 // Signals a semaphore (from ISR)
158 int
sys_sem_signal_isr(sys_sem_t * sem)159 sys_sem_signal_isr(sys_sem_t *sem)
160 {
161 BaseType_t woken = pdFALSE;
162 xSemaphoreGiveFromISR(*sem, &woken);
163 return woken == pdTRUE;
164 }
165
166 /**
167 * @brief Wait for a semaphore to be signaled
168 *
169 * @param sem pointer of the semaphore
170 * @param timeout if zero, will wait infinitely, or will wait for milliseconds specify by this argument
171 * @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
172 */
173 u32_t
sys_arch_sem_wait(sys_sem_t * sem,u32_t timeout)174 sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
175 {
176 BaseType_t ret;
177
178 if (!timeout) {
179 /* wait infinite */
180 ret = xSemaphoreTake(*sem, portMAX_DELAY);
181 LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
182 } else {
183 TickType_t timeout_ticks = timeout / portTICK_RATE_MS;
184 ret = xSemaphoreTake(*sem, timeout_ticks);
185 if (ret == errQUEUE_EMPTY) {
186 /* timed out */
187 return SYS_ARCH_TIMEOUT;
188 }
189 LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
190 }
191
192 return 0;
193 }
194
195 /**
196 * @brief Delete a semaphore
197 *
198 * @param sem pointer of the semaphore to delete
199 */
200 void
sys_sem_free(sys_sem_t * sem)201 sys_sem_free(sys_sem_t *sem)
202 {
203 vSemaphoreDelete(*sem);
204 *sem = NULL;
205 }
206
207 /**
208 * @brief Create an empty mailbox.
209 *
210 * @param mbox pointer of the mailbox
211 * @param size size of the mailbox
212 * @return ERR_OK on success, ERR_MEM when out of memory
213 */
214 err_t
sys_mbox_new(sys_mbox_t * mbox,int size)215 sys_mbox_new(sys_mbox_t *mbox, int size)
216 {
217 *mbox = mem_malloc(sizeof(struct sys_mbox_s));
218 if (*mbox == NULL){
219 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox\n"));
220 return ERR_MEM;
221 }
222
223 (*mbox)->os_mbox = xQueueCreate(size, sizeof(void *));
224
225 if ((*mbox)->os_mbox == NULL) {
226 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new (*mbox)->os_mbox\n"));
227 free(*mbox);
228 return ERR_MEM;
229 }
230
231 #if ESP_THREAD_SAFE
232 (*mbox)->owner = NULL;
233 #endif
234
235 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p\n", *mbox, (*mbox)->os_mbox));
236 return ERR_OK;
237 }
238
239 /**
240 * @brief Send message to mailbox
241 *
242 * @param mbox pointer of the mailbox
243 * @param msg pointer of the message to send
244 */
245 void
sys_mbox_post(sys_mbox_t * mbox,void * msg)246 sys_mbox_post(sys_mbox_t *mbox, void *msg)
247 {
248 BaseType_t ret = xQueueSendToBack((*mbox)->os_mbox, &msg, portMAX_DELAY);
249 LWIP_ASSERT("mbox post failed", ret == pdTRUE);
250 }
251
252 /**
253 * @brief Try to post a message to mailbox
254 *
255 * @param mbox pointer of the mailbox
256 * @param msg pointer of the message to send
257 * @return ERR_OK on success, ERR_MEM when mailbox is full
258 */
259 err_t
sys_mbox_trypost(sys_mbox_t * mbox,void * msg)260 sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
261 {
262 err_t xReturn;
263
264 if (xQueueSend((*mbox)->os_mbox, &msg, 0) == pdTRUE) {
265 xReturn = ERR_OK;
266 } else {
267 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox));
268 xReturn = ERR_MEM;
269 }
270
271 return xReturn;
272 }
273
274 /**
275 * @brief Try to post a message to mailbox from ISR
276 *
277 * @param mbox pointer of the mailbox
278 * @param msg pointer of the message to send
279 * @return ERR_OK on success
280 * ERR_MEM when mailbox is full
281 * ERR_NEED_SCHED when high priority task wakes up
282 */
283 err_t
sys_mbox_trypost_fromisr(sys_mbox_t * mbox,void * msg)284 sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
285 {
286 BaseType_t ret;
287 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
288
289 ret = xQueueSendFromISR((*mbox)->os_mbox, &msg, &xHigherPriorityTaskWoken);
290 if (ret == pdTRUE) {
291 if (xHigherPriorityTaskWoken == pdTRUE) {
292 return ERR_NEED_SCHED;
293 }
294 return ERR_OK;
295 } else {
296 LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
297 return ERR_MEM;
298 }
299 }
300
301 /**
302 * @brief Fetch message from mailbox
303 *
304 * @param mbox pointer of mailbox
305 * @param msg pointer of the received message, could be NULL to indicate the message should be dropped
306 * @param timeout if zero, will wait infinitely; or will wait milliseconds specify by this argument
307 * @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
308 */
309 u32_t
sys_arch_mbox_fetch(sys_mbox_t * mbox,void ** msg,u32_t timeout)310 sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
311 {
312 BaseType_t ret;
313 void *msg_dummy;
314
315 if (msg == NULL) {
316 msg = &msg_dummy;
317 }
318
319 if (timeout == 0) {
320 /* wait infinite */
321 ret = xQueueReceive((*mbox)->os_mbox, &(*msg), portMAX_DELAY);
322 LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
323 } else {
324 TickType_t timeout_ticks = timeout / portTICK_RATE_MS;
325 ret = xQueueReceive((*mbox)->os_mbox, &(*msg), timeout_ticks);
326 if (ret == errQUEUE_EMPTY) {
327 /* timed out */
328 *msg = NULL;
329 return SYS_ARCH_TIMEOUT;
330 }
331 LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
332 }
333
334 return 0;
335 }
336
337 /**
338 * @brief try to fetch message from mailbox
339 *
340 * @param mbox pointer of mailbox
341 * @param msg pointer of the received message
342 * @return SYS_MBOX_EMPTY if mailbox is empty, 1 otherwise
343 */
344 u32_t
sys_arch_mbox_tryfetch(sys_mbox_t * mbox,void ** msg)345 sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
346 {
347 BaseType_t ret;
348 void *msg_dummy;
349
350 if (msg == NULL) {
351 msg = &msg_dummy;
352 }
353 ret = xQueueReceive((*mbox)->os_mbox, &(*msg), 0);
354 if (ret == errQUEUE_EMPTY) {
355 *msg = NULL;
356 return SYS_MBOX_EMPTY;
357 }
358 LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
359
360 return 0;
361 }
362
363 void
sys_mbox_set_owner(sys_mbox_t * mbox,void * owner)364 sys_mbox_set_owner(sys_mbox_t *mbox, void* owner)
365 {
366 if (mbox && *mbox) {
367 (*mbox)->owner = owner;
368 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("set mbox=%p owner=%p", *mbox, owner));
369 }
370 }
371
372 /**
373 * @brief Delete a mailbox
374 *
375 * @param mbox pointer of the mailbox to delete
376 */
377 void
sys_mbox_free(sys_mbox_t * mbox)378 sys_mbox_free(sys_mbox_t *mbox)
379 {
380 if ((NULL == mbox) || (NULL == *mbox)) {
381 return;
382 }
383 UBaseType_t msgs_waiting = uxQueueMessagesWaiting((*mbox)->os_mbox);
384 LWIP_ASSERT("mbox quence not empty", msgs_waiting == 0);
385
386 vQueueDelete((*mbox)->os_mbox);
387 free(*mbox);
388 *mbox = NULL;
389 }
390
391 /**
392 * @brief Create a new thread
393 *
394 * @param name thread name
395 * @param thread thread function
396 * @param arg thread arguments
397 * @param stacksize stacksize of the thread
398 * @param prio priority of the thread
399 * @return thread ID
400 */
401 sys_thread_t
sys_thread_new(const char * name,lwip_thread_fn thread,void * arg,int stacksize,int prio)402 sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
403 {
404 TaskHandle_t rtos_task;
405 BaseType_t ret;
406
407 /* LwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
408 thread function without adaption here. */
409 ret = xTaskCreatePinnedToCore(thread, name, stacksize, arg, prio, &rtos_task,
410 CONFIG_LWIP_TCPIP_TASK_AFFINITY);
411
412 if (ret != pdTRUE) {
413 return NULL;
414 }
415
416 return (sys_thread_t)rtos_task;
417 }
418
419 /**
420 * @brief Initialize the sys_arch layer
421 *
422 */
423 void
sys_init(void)424 sys_init(void)
425 {
426 if (!g_lwip_protect_mutex) {
427 if (ERR_OK != sys_mutex_new(&g_lwip_protect_mutex)) {
428 ESP_LOGE(TAG, "sys_init: failed to init lwip protect mutex\n");
429 }
430 }
431
432 // Create the pthreads key for the per-thread semaphore storage
433 pthread_key_create(&sys_thread_sem_key, sys_thread_sem_free);
434
435 esp_vfs_lwip_sockets_register();
436 }
437
438 /**
439 * @brief Get system ticks
440 *
441 * @return system tick counts
442 */
443 u32_t
sys_jiffies(void)444 sys_jiffies(void)
445 {
446 return xTaskGetTickCount();
447 }
448
449 /**
450 * @brief Get current time, in miliseconds
451 *
452 * @return current time
453 */
454 u32_t
sys_now(void)455 sys_now(void)
456 {
457 return xTaskGetTickCount() * portTICK_PERIOD_MS;
458 }
459
460 /**
461 * @brief Protect critical region
462 *
463 * @note This function is only called during very short critical regions.
464 *
465 * @return previous protection level
466 */
467 sys_prot_t
sys_arch_protect(void)468 sys_arch_protect(void)
469 {
470 if (unlikely(!g_lwip_protect_mutex)) {
471 sys_mutex_new(&g_lwip_protect_mutex);
472 }
473 sys_mutex_lock(&g_lwip_protect_mutex);
474 return (sys_prot_t) 1;
475 }
476
477 /**
478 * @brief Unprotect critical region
479 *
480 * @param pval protection level
481 */
482 void
sys_arch_unprotect(sys_prot_t pval)483 sys_arch_unprotect(sys_prot_t pval)
484 {
485 LWIP_UNUSED_ARG(pval);
486 sys_mutex_unlock(&g_lwip_protect_mutex);
487 }
488
489 /*
490 * get per thread semaphore
491 */
492 sys_sem_t*
sys_thread_sem_get(void)493 sys_thread_sem_get(void)
494 {
495 sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
496
497 if (!sem) {
498 sem = sys_thread_sem_init();
499 }
500 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem));
501 return sem;
502 }
503
504 static void
sys_thread_sem_free(void * data)505 sys_thread_sem_free(void* data) // destructor for TLS semaphore
506 {
507 sys_sem_t *sem = (sys_sem_t*)(data);
508
509 if (sem && *sem){
510 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem del, sem=%p\n", *sem));
511 vSemaphoreDelete(*sem);
512 }
513
514 if (sem) {
515 LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem pointer del, sem_p=%p\n", sem));
516 free(sem);
517 }
518 }
519
520 sys_sem_t*
sys_thread_sem_init(void)521 sys_thread_sem_init(void)
522 {
523 sys_sem_t *sem = (sys_sem_t*)mem_malloc(sizeof(sys_sem_t*));
524
525 if (!sem){
526 ESP_LOGE(TAG, "thread_sem_init: out of memory");
527 return 0;
528 }
529
530 *sem = xSemaphoreCreateBinary();
531 if (!(*sem)){
532 free(sem);
533 ESP_LOGE(TAG, "thread_sem_init: out of memory");
534 return 0;
535 }
536
537 pthread_setspecific(sys_thread_sem_key, sem);
538 return sem;
539 }
540
541 void
sys_thread_sem_deinit(void)542 sys_thread_sem_deinit(void)
543 {
544 sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
545 if (sem != NULL) {
546 sys_thread_sem_free(sem);
547 pthread_setspecific(sys_thread_sem_key, NULL);
548 }
549 }
550
551 void
sys_delay_ms(uint32_t ms)552 sys_delay_ms(uint32_t ms)
553 {
554 vTaskDelay(ms / portTICK_PERIOD_MS);
555 }
556