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