1 /*\r
2 * Copyright (c) 2014, Mentor Graphics Corporation\r
3 * All rights reserved.\r
4 *\r
5 * Redistribution and use in source and binary forms, with or without\r
6 * modification, are permitted provided that the following conditions are met:\r
7 *\r
8 * 1. Redistributions of source code must retain the above copyright notice,\r
9 * this list of conditions and the following disclaimer.\r
10 * 2. Redistributions in binary form must reproduce the above copyright notice,\r
11 * this list of conditions and the following disclaimer in the documentation\r
12 * and/or other materials provided with the distribution.\r
13 * 3. Neither the name of Mentor Graphics Corporation nor the names of its\r
14 * contributors may be used to endorse or promote products derived from this\r
15 * software without specific prior written permission.\r
16 *\r
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
27 * POSSIBILITY OF SUCH DAMAGE.\r
28 */\r
29 \r
30 /**************************************************************************\r
31 * FILE NAME\r
32 *\r
33 * nu_env.c\r
34 *\r
35 * COMPONENT\r
36 *\r
37 * OpenAMP stack.\r
38 *\r
39 * DESCRIPTION\r
40 *\r
41 * This file is Nucleus Implementation of environment layer.\r
42 *\r
43 *\r
44 **************************************************************************/\r
45 \r
46 #include "env.h"\r
47 #include "../config/config.h"\r
48 \r
49 #if (ENV == NU_ENV)\r
50 \r
51 #include <stdlib.h>\r
52 #include <string.h>\r
53 #include "nucleus.h"\r
54 #include "kernel/nu_kernel.h"\r
55 #include "os/kernel/plus/core/inc/thread_control.h"\r
56 \r
57 /* Configurable parameters */\r
58 \r
59 /* Main task's stack size */\r
60 #define NU_ENV_STACK_SIZE (NU_MIN_STACK_SIZE * 64)\r
61 \r
62 /* Main task's priority */\r
63 #define NU_ENV_TASK_PRIOIRTY 26\r
64 \r
65 /* Main task's time slice */\r
66 #define NU_ENV_TASK_SLICE 20\r
67 \r
68 \r
69 /* Queue message size */\r
70 #define MSG_SIZE sizeof(struct isr_info)/sizeof(unsigned)\r
71 \r
72 /* Internal functions */\r
73 static VOID NU_Env_Task_Entry(UNSIGNED argc, VOID *argv);\r
74 static void NU_Env_HISR_Entry();\r
75 static VOID NU_Env_LISR(int vector);\r
76 \r
77 /* Globals */\r
78 static NU_HISR NU_Env_HISR;\r
79 static NU_TASK NU_Env_Task;\r
80 static NU_QUEUE NU_Env_Queue;\r
81 static NU_SEMAPHORE NU_Env_Sem;\r
82 static VOID *Queue_Mem = NU_NULL;\r
83 static VOID *Task_Mem = NU_NULL;\r
84 static VOID *Hisr_Mem = NU_NULL;\r
85 static INT Intr_Count = 0;\r
86 static INT ENV_INIT_COUNT = 0;\r
87 static struct isr_info isr_table[ISR_COUNT];\r
88 static INT Old_Level = 0;\r
89 \r
90 /**\r
91 * env_init\r
92 *\r
93 * Initializes OS/BM environment.\r
94 *\r
95 */\r
96 int env_init(){\r
97 STATUS status = NU_SUCCESS;\r
98 NU_MEMORY_POOL *pool_ptr = NU_NULL;\r
99 \r
100 if(!ENV_INIT_COUNT)\r
101 {\r
102 status = NU_System_Memory_Get(&pool_ptr, NU_NULL);\r
103 if(status == NU_SUCCESS)\r
104 {\r
105 status = NU_Allocate_Memory(pool_ptr, &Hisr_Mem, NU_ENV_STACK_SIZE,\r
106 NU_NO_SUSPEND);\r
107 if (status == NU_SUCCESS)\r
108 {\r
109 memset(Hisr_Mem , 0x00 , NU_ENV_STACK_SIZE);\r
110 status = NU_Create_HISR(&NU_Env_HISR, "NU_Env_Q", NU_Env_HISR_Entry, 2, Hisr_Mem,\r
111 NU_ENV_STACK_SIZE);\r
112 if (status == NU_SUCCESS)\r
113 {\r
114 status = NU_Create_HISR_Queue(&NU_Env_HISR,8,NU_TRUE);\r
115 if(status == NU_SUCCESS)\r
116 {\r
117 status = NU_Allocate_Memory(pool_ptr, &Queue_Mem,\r
118 8*sizeof(struct isr_info), NU_NO_SUSPEND);\r
119 memset(Queue_Mem , 0x00 , 8*sizeof(struct isr_info));\r
120 if(status == NU_SUCCESS)\r
121 {\r
122 status = NU_Create_Queue(&NU_Env_Queue, "NU_Env_Q", Queue_Mem,\r
123 (8*MSG_SIZE), NU_FIXED_SIZE,\r
124 MSG_SIZE, NU_FIFO);\r
125 if (status == NU_SUCCESS)\r
126 {\r
127 status = NU_Create_Semaphore(&NU_Env_Sem, "ENV_MSEM", 1 , NU_FIFO);\r
128 if(status == NU_SUCCESS)\r
129 {\r
130 status = NU_Allocate_Memory(pool_ptr, &Task_Mem,\r
131 NU_ENV_STACK_SIZE, NU_NO_SUSPEND);\r
132 memset(Task_Mem , 0x00 , NU_ENV_STACK_SIZE);\r
133 if (status == NU_SUCCESS)\r
134 {\r
135 /* Create task 0. */\r
136 status = NU_Create_Task(&NU_Env_Task, "NU_Env_T", NU_Env_Task_Entry, 0,\r
137 pool_ptr, Task_Mem, NU_ENV_STACK_SIZE, NU_ENV_TASK_PRIOIRTY,\r
138 NU_ENV_TASK_SLICE, NU_PREEMPT, NU_START);\r
139 }\r
140 }\r
141 }\r
142 }\r
143 }\r
144 }\r
145 }\r
146 }\r
147 }\r
148 \r
149 if(status == NU_SUCCESS){\r
150 NU_Obtain_Semaphore(&NU_Env_Sem, NU_SUSPEND);\r
151 ENV_INIT_COUNT++;\r
152 NU_Release_Semaphore(&NU_Env_Sem);\r
153 }\r
154 \r
155 return status;\r
156 }\r
157 \r
158 /**\r
159 * env_deinit\r
160 *\r
161 * Uninitializes OS/BM environment.\r
162 *\r
163 * @returns - execution status\r
164 */\r
165 int env_deinit() {\r
166 int status = NU_SUCCESS;\r
167 \r
168 NU_Obtain_Semaphore(&NU_Env_Sem, NU_SUSPEND);\r
169 ENV_INIT_COUNT--;\r
170 NU_Release_Semaphore(&NU_Env_Sem);\r
171 \r
172 if (!ENV_INIT_COUNT) {\r
173 if (Task_Mem) {\r
174 status |= NU_Terminate_Task(&NU_Env_Task);\r
175 status |= NU_Delete_Task(&NU_Env_Task);\r
176 status |= NU_Deallocate_Memory(Task_Mem);\r
177 }\r
178 \r
179 if (Queue_Mem) {\r
180 NU_Delete_Queue(&NU_Env_Queue);\r
181 status |= NU_Deallocate_Memory(Queue_Mem);\r
182 }\r
183 \r
184 if (Hisr_Mem) {\r
185 status |= NU_Delete_HISR(&NU_Env_HISR);\r
186 status |= NU_Deallocate_Memory(Hisr_Mem);\r
187 }\r
188 \r
189 status |= NU_Delete_Semaphore(&NU_Env_Sem);\r
190 Intr_Count = 0;\r
191 \r
192 }\r
193 return status;\r
194 }\r
195 \r
196 /**\r
197 * env_allocate_memory - implementation\r
198 *\r
199 * @param size\r
200 */\r
201 void *env_allocate_memory(unsigned int size)\r
202 {\r
203 STATUS status = NU_SUCCESS;\r
204 VOID *mem_ptr = NU_NULL;\r
205 NU_MEMORY_POOL *pool_ptr = NU_NULL;\r
206 \r
207 /* Ensure valid allocation size. */\r
208 if (size != 0)\r
209 {\r
210 /* Get system cached memory pools pointer */\r
211 status = NU_System_Memory_Get(&pool_ptr, NU_NULL);\r
212 if (status == NU_SUCCESS)\r
213 {\r
214 /* Allocate requested memory from Nucleus memory system. */\r
215 status = NU_Allocate_Memory(pool_ptr, &mem_ptr,\r
216 (UNSIGNED)size, NU_NO_SUSPEND);\r
217 if(status == NU_SUCCESS)\r
218 {\r
219 memset(mem_ptr, 0x00 ,size);\r
220 }\r
221 }\r
222 }\r
223 \r
224 return (mem_ptr);\r
225 }\r
226 \r
227 /**\r
228 * env_free_memory - implementation\r
229 *\r
230 * @param ptr\r
231 */\r
232 void env_free_memory(void *ptr)\r
233 {\r
234 if (ptr != NU_NULL)\r
235 {\r
236 (VOID) NU_Deallocate_Memory(ptr);\r
237 }\r
238 }\r
239 \r
240 \r
241 /**\r
242 *\r
243 * env_memset - implementation\r
244 *\r
245 * @param ptr\r
246 * @param value\r
247 * @param size\r
248 */\r
249 void env_memset(void *ptr, int value, unsigned long size)\r
250 {\r
251 memset(ptr, value, size);\r
252 }\r
253 \r
254 void env_memcpy(void *dst, void const * src, unsigned long len){\r
255 memcpy(dst,src,len);\r
256 }\r
257 /**\r
258 *\r
259 * env_strncpy - implementation\r
260 *\r
261 * @param dest\r
262 * @param src\r
263 * @param len\r
264 */\r
265 void env_strncpy(char * dst, const char *src, unsigned long len)\r
266 {\r
267 strncpy(dst, src, len);\r
268 }\r
269 \r
270 int env_strncmp(char * dst, const char *src, unsigned long len)\r
271 {\r
272 return (strncmp(dst, src, len));\r
273 }\r
274 \r
275 /**\r
276 *\r
277 * env_strcmp - implementation\r
278 *\r
279 * @param dst\r
280 * @param src\r
281 */\r
282 \r
283 int env_strcmp(const char * dst, const char *src) {\r
284 return (strcmp(dst, src));\r
285 }\r
286 /**\r
287 *\r
288 * env_mb - implementation\r
289 *\r
290 */\r
291 void env_mb()\r
292 {\r
293 ESAL_TS_RTE_COMPILE_MEM_BARRIER();\r
294 }\r
295 \r
296 /**\r
297 * osalr_mb - implementation\r
298 */\r
299 void env_rmb()\r
300 {\r
301 ESAL_TS_RTE_COMPILE_MEM_BARRIER();\r
302 }\r
303 \r
304 /**\r
305 * env_wmb - implementation\r
306 */\r
307 void env_wmb()\r
308 {\r
309 ESAL_TS_RTE_COMPILE_MEM_BARRIER();\r
310 }\r
311 \r
312 /**\r
313 * env_map_vatopa - implementation\r
314 *\r
315 * @param address\r
316 */\r
317 unsigned long env_map_vatopa(void *address)\r
318 {\r
319 return ((((unsigned long)address & (~( 0x0fff << 20))) | (0x08 << 24)));\r
320 }\r
321 \r
322 /**\r
323 * env_map_patova - implementation\r
324 *\r
325 * @param address\r
326 */\r
327 void *env_map_patova(unsigned long address)\r
328 {\r
329 return ((void *)address);\r
330 }\r
331 \r
332 /**\r
333 * env_create_mutex\r
334 *\r
335 * Creates a mutex with initial state being unlocked.\r
336 *\r
337 */\r
338 int env_create_mutex(void **lock, int count)\r
339 {\r
340 \r
341 NU_SEMAPHORE *sem = NU_NULL;\r
342 NU_MEMORY_POOL *pool_ptr = NU_NULL;\r
343 STATUS status;\r
344 \r
345 /* Get system cached memory pools pointer */\r
346 status = NU_System_Memory_Get(&pool_ptr, NU_NULL);\r
347 if (status == NU_SUCCESS)\r
348 {\r
349 /* Allocate requested memory from Nucleus semaphore object. */\r
350 status = NU_Allocate_Memory(pool_ptr, (VOID**)&sem,\r
351 sizeof(NU_SEMAPHORE), NU_NO_SUSPEND);\r
352 if (status == NU_SUCCESS){\r
353 memset(sem , 0x00 , sizeof(NU_SEMAPHORE));\r
354 status = NU_Create_Semaphore(sem, "Env_Sem", count, NU_PRIORITY);\r
355 if ( status == NU_SUCCESS){\r
356 *lock = sem;\r
357 }\r
358 }\r
359 }\r
360 \r
361 return status;\r
362 }\r
363 \r
364 /**\r
365 * env_delete_mutex\r
366 *\r
367 * Deletes the given lock\r
368 *\r
369 */\r
370 void env_delete_mutex(void *lock)\r
371 {\r
372 if (lock)\r
373 {\r
374 NU_Delete_Semaphore((NU_SEMAPHORE *)lock);\r
375 NU_Deallocate_Memory(lock);\r
376 }\r
377 }\r
378 \r
379 /**\r
380 * env_lock_mutex\r
381 *\r
382 * Tries to acquire the lock, if lock is not available then call to\r
383 * this function will suspend.\r
384 */\r
385 void env_lock_mutex(void *lock)\r
386 {\r
387 NU_Obtain_Semaphore((NU_SEMAPHORE *)lock, NU_SUSPEND);\r
388 }\r
389 \r
390 /**\r
391 * env_unlock_mutex\r
392 *\r
393 * Releases the given lock.\r
394 */\r
395 \r
396 void env_unlock_mutex(void *lock)\r
397 {\r
398 NU_Release_Semaphore((NU_SEMAPHORE*)lock);\r
399 }\r
400 \r
401 /**\r
402 * env_create_sync_lock\r
403 *\r
404 * Creates a synchronization lock primitive. It is required\r
405 * when signal is has to be sent from the interrupt context\r
406 * to main thread context.\r
407 *\r
408 */\r
409 int env_create_sync_lock(void **lock , int state){\r
410 NU_SEMAPHORE *sem = NU_NULL;\r
411 NU_MEMORY_POOL *pool_ptr = NU_NULL;\r
412 STATUS status;\r
413 \r
414 /* Get system cached memory pools pointer */\r
415 status = NU_System_Memory_Get(&pool_ptr, NU_NULL);\r
416 if (status == NU_SUCCESS)\r
417 {\r
418 /* Allocate requested memory from Nucleus semaphore object. */\r
419 status = NU_Allocate_Memory(pool_ptr, (VOID**)&sem,\r
420 sizeof(NU_SEMAPHORE), NU_NO_SUSPEND);\r
421 if (status == NU_SUCCESS){\r
422 memset(sem , 0x00 , sizeof(NU_SEMAPHORE));\r
423 status = NU_Create_Semaphore(sem, "Env_Sem", state, NU_PRIORITY);\r
424 if ( status == NU_SUCCESS){\r
425 *lock = sem;\r
426 }\r
427 }\r
428 }\r
429 \r
430 return status;\r
431 }\r
432 \r
433 /**\r
434 * env_delete_sync_lock\r
435 *\r
436 * Deletes the given lock\r
437 *\r
438 */\r
439 void env_delete_sync_lock(void *lock)\r
440 {\r
441 if (lock)\r
442 {\r
443 NU_Delete_Semaphore((NU_SEMAPHORE *)lock);\r
444 NU_Deallocate_Memory(lock);\r
445 }\r
446 }\r
447 \r
448 /**\r
449 * env_acquire_sync_lock\r
450 *\r
451 * Tries to acquire the lock, if lock is not available then call to\r
452 * this function will suspend.\r
453 */\r
454 void env_acquire_sync_lock(void *lock)\r
455 {\r
456 NU_Obtain_Semaphore((NU_SEMAPHORE *)lock, NU_SUSPEND);\r
457 }\r
458 \r
459 /**\r
460 * env_release_sync_lock\r
461 *\r
462 * Releases the given lock.\r
463 */\r
464 void env_release_sync_lock(void *lock)\r
465 {\r
466 NU_Release_Semaphore((NU_SEMAPHORE*)lock);\r
467 }\r
468 /**\r
469 * env_sleep_msec\r
470 *\r
471 * Suspends the calling thread for given time , in msecs.\r
472 */\r
473 \r
474 void env_sleep_msec(int num_msec)\r
475 {\r
476 NU_Sleep(num_msec/10);\r
477 }\r
478 \r
479 /**\r
480 * env_disable_interrupts\r
481 *\r
482 * Disables system interrupts\r
483 *\r
484 */\r
485 void env_disable_interrupts()\r
486 {\r
487 Old_Level = NU_Local_Control_Interrupts(NU_DISABLE_INTERRUPTS);\r
488 }\r
489 \r
490 /**\r
491 * env_restore_interrupts\r
492 *\r
493 * Enables system interrupts\r
494 *\r
495 */\r
496 void env_restore_interrupts()\r
497 {\r
498 NU_Local_Control_Interrupts(Old_Level);\r
499 }\r
500 \r
501 /**\r
502 * env_register_isr\r
503 *\r
504 * Registers interrupt handler for the given interrupt vector.\r
505 *\r
506 */\r
507 void env_register_isr(int vector , void *data ,\r
508 void (*isr)(int vector , void *data))\r
509 {\r
510 VOID (*old_lisr)(INT);\r
511 \r
512 env_disable_interrupts();\r
513 \r
514 if(Intr_Count < ISR_COUNT)\r
515 {\r
516 /* Save interrupt data */\r
517 isr_table[Intr_Count].vector = vector + ESAL_AR_INT_VECTOR_ID_DELIMITER;\r
518 isr_table[Intr_Count].data = data;\r
519 isr_table[Intr_Count++].isr = isr;\r
520 \r
521 /* Register LISR */\r
522 NU_Register_LISR(vector + ESAL_AR_INT_VECTOR_ID_DELIMITER, NU_Env_LISR, &old_lisr);\r
523 }\r
524 \r
525 env_restore_interrupts();\r
526 \r
527 }\r
528 \r
529 /**\r
530 * env_enable_interrupt\r
531 *\r
532 * Enables the given interrupt\r
533 *\r
534 * @param vector - interrupt vector number\r
535 * @param priority - interrupt priority\r
536 * @param polarity - interrupt polarity\r
537 */\r
538 \r
539 void env_enable_interrupt(unsigned int vector , unsigned int priority ,\r
540 unsigned int polarity)\r
541 {\r
542 int idx;\r
543 \r
544 env_disable_interrupts();\r
545 \r
546 for(idx = 0 ; idx < ISR_COUNT ; idx++)\r
547 {\r
548 if(isr_table[idx].vector == vector + ESAL_AR_INT_VECTOR_ID_DELIMITER)\r
549 {\r
550 isr_table[idx].priority = priority;\r
551 isr_table[idx].type = polarity;\r
552 (VOID) ESAL_GE_INT_Enable(vector + ESAL_AR_INT_VECTOR_ID_DELIMITER, polarity, priority );\r
553 break;\r
554 }\r
555 }\r
556 \r
557 env_restore_interrupts();\r
558 }\r
559 \r
560 /**\r
561 * env_disable_interrupt\r
562 *\r
563 * Disables the given interrupt\r
564 *\r
565 * @param vector - interrupt vector number\r
566 */\r
567 \r
568 void env_disable_interrupt(unsigned int vector)\r
569 {\r
570 ESAL_GE_INT_Disable(vector);\r
571 }\r
572 \r
573 /**\r
574 * env_map_memory\r
575 *\r
576 * Enables memory mapping for given memory region.\r
577 *\r
578 * @param pa - physical address of memory\r
579 * @param va - logical address of memory\r
580 * @param size - memory size\r
581 * param flags - flags for cache/uncached and access type\r
582 */\r
583 \r
584 void env_map_memory(unsigned int pa, unsigned int va, unsigned int size,\r
585 unsigned int flags)\r
586 {\r
587 int mem_type = ESAL_RAM;\r
588 int cache_type = ESAL_NOCACHE;\r
589 \r
590 if ((flags & (0x0f << 4 )) == MEM_MAPPED)\r
591 {\r
592 mem_type = ESAL_MEM_MAPPED;\r
593 }\r
594 \r
595 if ((flags & 0x0f) == WB_CACHE) {\r
596 cache_type = ESAL_WRITEBACK;\r
597 } else if ((flags & 0x0f) == WT_CACHE) {\r
598 cache_type = ESAL_WRITETHROUGH;\r
599 } else {\r
600 cache_type = ESAL_NOCACHE;\r
601 }\r
602 \r
603 ESAL_CO_MEM_Region_Setup(0, va, pa, size, mem_type, cache_type , 0);\r
604 }\r
605 \r
606 /**\r
607 * \r
608 * env_get_timestamp\r
609 *\r
610 * Returns a 64 bit time stamp.\r
611 *\r
612 *\r
613 */\r
614 unsigned long long env_get_timestamp(void) {\r
615 \r
616 return NU_Get_Time_Stamp();\r
617 }\r
618 /**\r
619 * env_disable_cache\r
620 * \r
621 * Disables system caches.\r
622 *\r
623 */\r
624 \r
625 void env_disable_cache() {\r
626 ESAL_CO_MEM_DCACHE_ALL_FLUSH_INVAL();\r
627 ESAL_CO_MEM_ICACHE_ALL_INVALIDATE();\r
628 ESAL_CO_MEM_CACHE_DISABLE();\r
629 }\r
630 \r
631 /***********************************************************************\r
632 * *\r
633 * * FUNCTION\r
634 * *\r
635 * * Env_Task_Entry\r
636 * *\r
637 * * DESCRIPTION\r
638 * *\r
639 * * Entry function for the Nucleus Environment task.\r
640 *\r
641 * ***********************************************************************/\r
642 static VOID NU_Env_Task_Entry(UNSIGNED argc, VOID *argv)\r
643 {\r
644 STATUS status;\r
645 UNSIGNED size;\r
646 struct isr_info intr;\r
647 \r
648 while (1)\r
649 {\r
650 /* Wait for the interrupt */\r
651 status = NU_Receive_From_Queue(&NU_Env_Queue, &intr,\r
652 MSG_SIZE, &size,NU_SUSPEND);\r
653 if(status == NU_SUCCESS)\r
654 {\r
655 intr.isr(intr.vector - ESAL_AR_INT_VECTOR_ID_DELIMITER, intr.data);\r
656 }\r
657 }\r
658 }\r
659 \r
660 static void NU_Env_HISR_Entry()\r
661 {\r
662 struct isr_info *info;\r
663 NU_Receive_From_HISR_Queue(&NU_Env_HISR, (VOID **)&info);\r
664 NU_Send_To_Queue(&NU_Env_Queue,info,MSG_SIZE,NU_NO_SUSPEND);\r
665 env_enable_interrupt(info->vector , info->priority, info->type);\r
666 }\r
667 \r
668 static VOID NU_Env_LISR(int vector)\r
669 {\r
670 int idx;\r
671 env_disable_interrupt(vector);\r
672 \r
673 for(idx = 0 ; idx < ISR_COUNT ; idx++)\r
674 {\r
675 if(isr_table[idx].vector == vector)\r
676 {\r
677 NU_Send_To_HISR_Queue(&NU_Env_HISR , &isr_table[idx], NU_ACTIVATE_HISR);\r
678 break;\r
679 }\r
680 }\r
681 }\r
682 \r
683 #endif /* (ENV == NU_ENV) */\r