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   (void)ret;
86 }
87 
88 /**
89  * @brief Unlock a mutex
90  *
91  * @param pxMutex pointer of mutex to unlock
92  */
93 void
sys_mutex_unlock(sys_mutex_t * pxMutex)94 sys_mutex_unlock(sys_mutex_t *pxMutex)
95 {
96   BaseType_t ret = xSemaphoreGive(*pxMutex);
97 
98   LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
99   (void)ret;
100 }
101 
102 /**
103  * @brief Delete a mutex
104  *
105  * @param pxMutex pointer of mutex to delete
106  */
107 void
sys_mutex_free(sys_mutex_t * pxMutex)108 sys_mutex_free(sys_mutex_t *pxMutex)
109 {
110   LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex));
111   vSemaphoreDelete(*pxMutex);
112   *pxMutex = NULL;
113 }
114 
115 #endif /* !LWIP_COMPAT_MUTEX */
116 
117 /**
118  * @brief Creates a new semaphore
119  *
120  * @param sem pointer of the semaphore
121  * @param count initial state of the semaphore
122  * @return err_t
123  */
124 err_t
sys_sem_new(sys_sem_t * sem,u8_t count)125 sys_sem_new(sys_sem_t *sem, u8_t count)
126 {
127   LWIP_ASSERT("initial_count invalid (neither 0 nor 1)",
128              (count == 0) || (count == 1));
129 
130   *sem = xSemaphoreCreateBinary();
131   if (*sem == NULL) {
132       LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_sem_new: out of mem\r\n"));
133       return ERR_MEM;
134   }
135 
136   if (count == 1) {
137       BaseType_t ret = xSemaphoreGive(*sem);
138       LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
139       (void)ret;
140   }
141 
142   return ERR_OK;
143 }
144 
145 /**
146  * @brief Signals a semaphore
147  *
148  * @param sem pointer of the semaphore
149  */
150 void
sys_sem_signal(sys_sem_t * sem)151 sys_sem_signal(sys_sem_t *sem)
152 {
153   BaseType_t ret = xSemaphoreGive(*sem);
154   /* queue full is OK, this is a signal only... */
155   LWIP_ASSERT("sys_sem_signal: sane return value",
156              (ret == pdTRUE) || (ret == errQUEUE_FULL));
157   (void)ret;
158 }
159 
160 /*-----------------------------------------------------------------------------------*/
161 // Signals a semaphore (from ISR)
162 int
sys_sem_signal_isr(sys_sem_t * sem)163 sys_sem_signal_isr(sys_sem_t *sem)
164 {
165     BaseType_t woken = pdFALSE;
166     xSemaphoreGiveFromISR(*sem, &woken);
167     return woken == pdTRUE;
168 }
169 
170 /**
171  * @brief Wait for a semaphore to be signaled
172  *
173  * @param sem pointer of the semaphore
174  * @param timeout if zero, will wait infinitely, or will wait for milliseconds specify by this argument
175  * @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
176  */
177 u32_t
sys_arch_sem_wait(sys_sem_t * sem,u32_t timeout)178 sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
179 {
180   BaseType_t ret;
181 
182   if (!timeout) {
183     /* wait infinite */
184     ret = xSemaphoreTake(*sem, portMAX_DELAY);
185     LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
186   } else {
187     TickType_t timeout_ticks = timeout / portTICK_RATE_MS;
188     ret = xSemaphoreTake(*sem, timeout_ticks);
189     if (ret == errQUEUE_EMPTY) {
190       /* timed out */
191       return SYS_ARCH_TIMEOUT;
192     }
193     LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
194   }
195 
196   return 0;
197 }
198 
199 /**
200  * @brief Delete a semaphore
201  *
202  * @param sem pointer of the semaphore to delete
203  */
204 void
sys_sem_free(sys_sem_t * sem)205 sys_sem_free(sys_sem_t *sem)
206 {
207   vSemaphoreDelete(*sem);
208   *sem = NULL;
209 }
210 
211 /**
212  * @brief Create an empty mailbox.
213  *
214  * @param mbox pointer of the mailbox
215  * @param size size of the mailbox
216  * @return ERR_OK on success, ERR_MEM when out of memory
217  */
218 err_t
sys_mbox_new(sys_mbox_t * mbox,int size)219 sys_mbox_new(sys_mbox_t *mbox, int size)
220 {
221   *mbox = mem_malloc(sizeof(struct sys_mbox_s));
222   if (*mbox == NULL){
223     LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox\n"));
224     return ERR_MEM;
225   }
226 
227   (*mbox)->os_mbox = xQueueCreate(size, sizeof(void *));
228 
229   if ((*mbox)->os_mbox == NULL) {
230     LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new (*mbox)->os_mbox\n"));
231     free(*mbox);
232     return ERR_MEM;
233   }
234 
235 #if ESP_THREAD_SAFE
236   (*mbox)->owner = NULL;
237 #endif
238 
239   LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p\n", *mbox, (*mbox)->os_mbox));
240   return ERR_OK;
241 }
242 
243 /**
244  * @brief Send message to mailbox
245  *
246  * @param mbox pointer of the mailbox
247  * @param msg pointer of the message to send
248  */
249 void
sys_mbox_post(sys_mbox_t * mbox,void * msg)250 sys_mbox_post(sys_mbox_t *mbox, void *msg)
251 {
252   BaseType_t ret = xQueueSendToBack((*mbox)->os_mbox, &msg, portMAX_DELAY);
253   LWIP_ASSERT("mbox post failed", ret == pdTRUE);
254   (void)ret;
255 }
256 
257 /**
258  * @brief Try to post a message to mailbox
259  *
260  * @param mbox pointer of the mailbox
261  * @param msg pointer of the message to send
262  * @return ERR_OK on success, ERR_MEM when mailbox is full
263  */
264 err_t
sys_mbox_trypost(sys_mbox_t * mbox,void * msg)265 sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
266 {
267   err_t xReturn;
268 
269   if (xQueueSend((*mbox)->os_mbox, &msg, 0) == pdTRUE) {
270     xReturn = ERR_OK;
271   } else {
272     LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox));
273     xReturn = ERR_MEM;
274   }
275 
276   return xReturn;
277 }
278 
279 /**
280  * @brief Try to post a message to mailbox from ISR
281  *
282  * @param mbox pointer of the mailbox
283  * @param msg pointer of the message to send
284  * @return  ERR_OK on success
285  *          ERR_MEM when mailbox is full
286  *          ERR_NEED_SCHED when high priority task wakes up
287  */
288 err_t
sys_mbox_trypost_fromisr(sys_mbox_t * mbox,void * msg)289 sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
290 {
291   BaseType_t ret;
292   BaseType_t xHigherPriorityTaskWoken = pdFALSE;
293 
294   ret = xQueueSendFromISR((*mbox)->os_mbox, &msg, &xHigherPriorityTaskWoken);
295   if (ret == pdTRUE) {
296     if (xHigherPriorityTaskWoken == pdTRUE) {
297       return ERR_NEED_SCHED;
298     }
299     return ERR_OK;
300   } else {
301     LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
302     return ERR_MEM;
303   }
304 }
305 
306 /**
307  * @brief Fetch message from mailbox
308  *
309  * @param mbox pointer of mailbox
310  * @param msg pointer of the received message, could be NULL to indicate the message should be dropped
311  * @param timeout if zero, will wait infinitely; or will wait milliseconds specify by this argument
312  * @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
313  */
314 u32_t
sys_arch_mbox_fetch(sys_mbox_t * mbox,void ** msg,u32_t timeout)315 sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
316 {
317   BaseType_t ret;
318   void *msg_dummy;
319 
320   if (msg == NULL) {
321     msg = &msg_dummy;
322   }
323 
324   if (timeout == 0) {
325     /* wait infinite */
326     ret = xQueueReceive((*mbox)->os_mbox, &(*msg), portMAX_DELAY);
327     LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
328   } else {
329     TickType_t timeout_ticks = timeout / portTICK_RATE_MS;
330     ret = xQueueReceive((*mbox)->os_mbox, &(*msg), timeout_ticks);
331     if (ret == errQUEUE_EMPTY) {
332       /* timed out */
333       *msg = NULL;
334       return SYS_ARCH_TIMEOUT;
335     }
336     LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
337   }
338 
339   return 0;
340 }
341 
342 /**
343  * @brief try to fetch message from mailbox
344  *
345  * @param mbox pointer of mailbox
346  * @param msg pointer of the received message
347  * @return SYS_MBOX_EMPTY if mailbox is empty, 1 otherwise
348  */
349 u32_t
sys_arch_mbox_tryfetch(sys_mbox_t * mbox,void ** msg)350 sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
351 {
352   BaseType_t ret;
353   void *msg_dummy;
354 
355   if (msg == NULL) {
356     msg = &msg_dummy;
357   }
358   ret = xQueueReceive((*mbox)->os_mbox, &(*msg), 0);
359   if (ret == errQUEUE_EMPTY) {
360     *msg = NULL;
361     return SYS_MBOX_EMPTY;
362   }
363   LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
364 
365   return 0;
366 }
367 
368 void
sys_mbox_set_owner(sys_mbox_t * mbox,void * owner)369 sys_mbox_set_owner(sys_mbox_t *mbox, void* owner)
370 {
371   if (mbox && *mbox) {
372     (*mbox)->owner = owner;
373     LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("set mbox=%p owner=%p", *mbox, owner));
374   }
375 }
376 
377 /**
378  * @brief Delete a mailbox
379  *
380  * @param mbox pointer of the mailbox to delete
381  */
382 void
sys_mbox_free(sys_mbox_t * mbox)383 sys_mbox_free(sys_mbox_t *mbox)
384 {
385   if ((NULL == mbox) || (NULL == *mbox)) {
386     return;
387   }
388   UBaseType_t msgs_waiting = uxQueueMessagesWaiting((*mbox)->os_mbox);
389   LWIP_ASSERT("mbox quence not empty", msgs_waiting == 0);
390 
391   vQueueDelete((*mbox)->os_mbox);
392   free(*mbox);
393   *mbox = NULL;
394 
395   (void)msgs_waiting;
396 }
397 
398 /**
399  * @brief Create a new thread
400  *
401  * @param name thread name
402  * @param thread thread function
403  * @param arg thread arguments
404  * @param stacksize stacksize of the thread
405  * @param prio priority of the thread
406  * @return thread ID
407  */
408 sys_thread_t
sys_thread_new(const char * name,lwip_thread_fn thread,void * arg,int stacksize,int prio)409 sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
410 {
411   TaskHandle_t rtos_task;
412   BaseType_t ret;
413 
414   /* LwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
415      thread function without adaption here. */
416   ret = xTaskCreatePinnedToCore(thread, name, stacksize, arg, prio, &rtos_task,
417           CONFIG_LWIP_TCPIP_TASK_AFFINITY);
418 
419   if (ret != pdTRUE) {
420     return NULL;
421   }
422 
423   return (sys_thread_t)rtos_task;
424 }
425 
426 /**
427  * @brief Initialize the sys_arch layer
428  *
429  */
430 void
sys_init(void)431 sys_init(void)
432 {
433   if (!g_lwip_protect_mutex) {
434     if (ERR_OK != sys_mutex_new(&g_lwip_protect_mutex)) {
435       ESP_LOGE(TAG, "sys_init: failed to init lwip protect mutex\n");
436     }
437   }
438 
439   // Create the pthreads key for the per-thread semaphore storage
440   pthread_key_create(&sys_thread_sem_key, sys_thread_sem_free);
441 
442   esp_vfs_lwip_sockets_register();
443 }
444 
445 /**
446  * @brief Get system ticks
447  *
448  * @return system tick counts
449  */
450 u32_t
sys_jiffies(void)451 sys_jiffies(void)
452 {
453   return xTaskGetTickCount();
454 }
455 
456 /**
457  * @brief Get current time, in miliseconds
458  *
459  * @return current time
460  */
461 u32_t
sys_now(void)462 sys_now(void)
463 {
464   return xTaskGetTickCount() * portTICK_PERIOD_MS;
465 }
466 
467 /**
468  * @brief Protect critical region
469  *
470  * @note This function is only called during very short critical regions.
471  *
472  * @return previous protection level
473  */
474 sys_prot_t
sys_arch_protect(void)475 sys_arch_protect(void)
476 {
477   if (unlikely(!g_lwip_protect_mutex)) {
478     sys_mutex_new(&g_lwip_protect_mutex);
479   }
480   sys_mutex_lock(&g_lwip_protect_mutex);
481   return (sys_prot_t) 1;
482 }
483 
484 /**
485  * @brief Unprotect critical region
486  *
487  * @param pval protection level
488  */
489 void
sys_arch_unprotect(sys_prot_t pval)490 sys_arch_unprotect(sys_prot_t pval)
491 {
492   LWIP_UNUSED_ARG(pval);
493   sys_mutex_unlock(&g_lwip_protect_mutex);
494 }
495 
496 /*
497  * get per thread semaphore
498  */
499 sys_sem_t*
sys_thread_sem_get(void)500 sys_thread_sem_get(void)
501 {
502   sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
503 
504   if (!sem) {
505       sem = sys_thread_sem_init();
506   }
507   LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem));
508   return sem;
509 }
510 
511 static void
sys_thread_sem_free(void * data)512 sys_thread_sem_free(void* data) // destructor for TLS semaphore
513 {
514   sys_sem_t *sem = (sys_sem_t*)(data);
515 
516   if (sem && *sem){
517     LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem del, sem=%p\n", *sem));
518     vSemaphoreDelete(*sem);
519   }
520 
521   if (sem) {
522     LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem pointer del, sem_p=%p\n", sem));
523     free(sem);
524   }
525 }
526 
527 sys_sem_t*
sys_thread_sem_init(void)528 sys_thread_sem_init(void)
529 {
530   sys_sem_t *sem = (sys_sem_t*)mem_malloc(sizeof(sys_sem_t*));
531 
532   if (!sem){
533     ESP_LOGE(TAG, "thread_sem_init: out of memory");
534     return 0;
535   }
536 
537   *sem = xSemaphoreCreateBinary();
538   if (!(*sem)){
539     free(sem);
540     ESP_LOGE(TAG, "thread_sem_init: out of memory");
541     return 0;
542   }
543 
544   pthread_setspecific(sys_thread_sem_key, sem);
545   return sem;
546 }
547 
548 void
sys_thread_sem_deinit(void)549 sys_thread_sem_deinit(void)
550 {
551   sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
552   if (sem != NULL) {
553     sys_thread_sem_free(sem);
554     pthread_setspecific(sys_thread_sem_key, NULL);
555   }
556 }
557 
558 void
sys_delay_ms(uint32_t ms)559 sys_delay_ms(uint32_t ms)
560 {
561   vTaskDelay(ms / portTICK_PERIOD_MS);
562 }
563