comparison riviera/rvf/rvf_task.c @ 0:75a11d740a02

initial import of gsm-fw from freecalypso-sw rev 1033:5ab737ac3ad7
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 09 Jun 2016 00:02:41 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:75a11d740a02
1 /****************************************************************************/
2 /* */
3 /* Name rvf_task.c */
4 /* */
5 /* Function this file contains \rvf task related functions */
6 /* */
7 /* Version 0.1 */
8 /* */
9 /* Date Modification */
10 /* ------------------------------------ */
11 /* 3/12/99 Create */
12 /* 10/27/99 remove all non-nucleus sections (#ifdef) */
13 /* change tasks priority and time_slicing */
14 /* 11/17/1999 change RVF_create_task and RVF_get_taskid functions */
15 /* 30/11/99 compliant to RV coding guidelines */
16 /* 28/08/2000 add mutex related functions. */
17 /* */
18 /* Author David Lamy-Charrier (dlamy@tif.ti.com) */
19 /* */
20 /* (C) Copyright 1999 by Texas Instruments Incorporated, All Rights Reserved*/
21 /****************************************************************************/
22
23 #include "../../include/config.h"
24
25 #include "../rv/rv_general.h"
26 #include "rvf_api.h"
27 #include "rvf_i.h"
28 #include "../rvm/rvm_i.h" /* ONLY for Task Codes */
29 #include "../rvm/rvm_use_id_list.h"
30
31 #include "../../nucleus/nucleus.h"
32
33 #include <stdio.h>
34 #include <string.h>
35
36 /* include the rvtool_trace.h file only in the RivieraTool */
37 #ifdef _WINDOWS
38 #ifndef _CONSOLE
39 // #include "rvtool_trace.h"
40 #endif
41 #include <windows.h>
42 #endif
43
44 #define RVF_STATIC_ALLOC_NB 1
45
46 /**********************************************************************
47 ** Nucleus specific definitions
48 */
49
50 typedef void (*NU_TASK_ENTRY)(UNSIGNED, VOID *); //
51
52 /* array of tasks */
53 /**********************************************************************
54 * Note: if dynamic mem alloc of "pOSTCB" creates too much fragentation
55 * The origianl way me be utilised and pointed to by a addr_id table
56 * structure, which is proposed for type 2 support.
57 ***********************************************************************/
58
59 #define _RALLOC_TASK_CNTRL_BLK(tb) rvf_get_buf(rvm_sys_mem_bank, sizeof(NU_TASK), tb)
60 #define _RALLOC_TASK_EVT_GRP(tb) rvf_get_buf(rvm_sys_mem_bank, sizeof(NU_EVENT_GROUP), tb)
61 #define _RALLOC_TASK_RT_ADDR_DATA(tb) rvf_get_buf(rvm_sys_mem_bank, sizeof(T_RVF_RT_ADDR_ID_DATA), tb)
62 //#define _RALLOC_TASK_RT_TM(tb) rvf_get_buf(rvm_sys_mem_bank, sizeof(T_RVF_TIMER_LIST_Q), tb)
63
64 /*static NU_TASK bOSTCB[RVF_STATIC_ALLOC_NB];
65 static NU_EVENT_GROUP bOSEvtGrp[RVF_STATIC_ALLOC_NB];
66 static UINT8 *bOSStack;
67 static UINT16 bOSStackSize;
68 static char bOSTName[RVF_STATIC_ALLOC_NB][RVF_STATIC_ALLOC_NB];
69 */
70
71 static UINT8 task_counter = 0;
72 static INT16 OSDisableNesting = 0;
73 static BOOL OSInterruptAlreadyMasked = FALSE;
74 static INT32 OSLastIntLevel;
75 static T_RVF_G_ADDR_ID _RDV=RVF_INVALID_ADDR_ID;
76
77 T_RVF_RET _initSysRtData(void);
78
79
80 /*******************************************************************************
81 **
82 ** Function rvf_AllocTaskXyz()
83 **
84 ** Description
85 **
86 **
87 ** Returns T_RVF_MB_STATUS
88 **
89 *******************************************************************************/
90 T_RVF_RET _initSysRtData(void) { /*A-M-E-N-D-E-D!*/
91 int i=0;
92
93 for (i=0; i < MAX_RVF_G_ADDR_ID; i++) pRtAddrIdTable[i]=NULL;
94
95 // pOSTCB[0]=&OSTCB[0];
96 return RVF_OK;
97 }
98
99 /*******************************************************************************
100 **
101 ** Function rvf_init
102 **
103 ** Description This function is called once at startup to initialize
104 ** all the rvf (timer, buffer...).
105 **
106 ** Returns void
107 **
108 *******************************************************************************/
109 void rvf_init(void) {
110 /* UINT8 i; */
111
112 /* Initialize RVF variables */
113 /*for (i = 0; i < MAX_RVF_TASKS; i++) {
114 OSStack[i] = 0;
115 OSStackSize[i] = 0;
116 OSTName[i][0] = 0;
117 memset( &OSTCB[i], 0, sizeof(NU_TASK) );
118 memset( &OSEvtGrp[i], 0, sizeof(NU_EVENT_GROUP) );
119 }
120 // for (i = 0; i < 1; i++) memset( &OSTCB[i], 0, sizeof(NU_TASK) );
121 */
122 _initSysRtData();
123 _rvf_buffer_init();
124 _rvf_timers_init();
125 }
126
127 /*******************************************************************************
128 **
129 ** Function _rvf_name_cpy
130 **
131 ** Description Internal function which copy a string in a buffer.
132 ** The string may be null-terminated or length bytes long.
133 **
134 ** Returns void
135 **
136 *******************************************************************************/
137 void _rvf_name_cpy(char * dest, char * source, UINT8 length) {
138 UINT8 cpt;
139 for( cpt = 0; cpt < length ; cpt++) {
140 dest[cpt] = source[cpt];
141 if (source[cpt] == 0) { return;}
142 }
143 }
144
145 T_RVF_G_ADDR_ID rvf_get_context() {
146 T_RVF_G_ADDR_ID gid=rvf_get_taskid();
147 if(pRtAddrIdTable[gid]->type_code==ET2_HOST_TASK) {
148 return pRtAddrIdTable[gid]->virtualContext;
149 } else {
150 return pRtAddrIdTable[gid]->host_addr_id;
151 }
152 }
153
154 void rvf_setRDV(T_RVF_G_ADDR_ID tid,T_RVF_G_ADDR_ID vid) {
155 if(pRtAddrIdTable[tid]) pRtAddrIdTable[tid]->virtualContext=vid;
156 }
157
158
159 /**************************************************************
160 * Function _rvf_name_cpy
161 *
162 ***************************************************************/
163 T_RVF_G_ADDR_ID rvf_allocate_task_id(UINT8 isRealTask) {
164 T_RVF_G_ADDR_ID i=0;
165 UINT8 isTask=1;
166
167 /* Note: differentiation is made between REAL and VIRTUAL IDs
168 * to provide for backwards compatabible LEGACY timer implementations
169 * The Real task ids should be in sync with:
170 * "static rvf_Timer[MAX_RVF_TASKS][RVF_NUM_TASK_TIMERS]",
171 * defined in "rvf_time.c". In the future, one NU_Timer blk will
172 * be pointed to in the rt global addr table. Hence, eliminating
173 * the large resource of "rvf_Timer[][]"
174 */
175 if(!pRtAddrIdTable[i]) return RVF_INVALID_ADDR_ID;
176
177 if(isRealTask) {
178 /* REAL TASK */
179 for(i=0; pRtAddrIdTable[i]!=NULL && i<MAX_RVF_TASKS; i++);
180
181 if (i < MAX_RVF_TASKS) { /* alloc. and init. */
182 if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[i])) return RVF_INVALID_TASK;
183 memset( pRtAddrIdTable[i], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );
184 rvf_mbox_buffer_init(pRtAddrIdTable[i]);
185
186 } else return RVF_INVALID_ADDR_ID;
187 } else {
188 /* VIRTUAL TASK */
189 for(i=MAX_RVF_TASKS; pRtAddrIdTable[i]!=NULL && i<MAX_RVF_G_ADDR_ID; i++);
190
191 if (i < MAX_RVF_G_ADDR_ID) { /* alloc. and init. */
192 if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[i])) return RVF_INVALID_TASK;
193 memset( pRtAddrIdTable[i], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );
194 rvf_mbox_buffer_init(pRtAddrIdTable[i]);
195
196 } else return RVF_INVALID_ADDR_ID;
197 }
198
199 return i;
200 }
201
202 /* MUST RESOLVE ERROR CODES '1' is just for P of C */
203 T_RVF_RET rvf_setRtAddrSweIndex(T_RVF_G_ADDR_ID id, UINT8 sweIndex) {
204 if(id>=MAX_RVF_G_ADDR_ID) return 1;
205 pRtAddrIdTable[id]->swe_db_index=sweIndex;
206 // pRtAddrIdTable[id]->type_code=tcode; /* allows type to be set if not in swe */
207 return RVF_OK;
208 }
209
210 T_RVF_RET rvf_setHostTaskStackPtr(T_RVF_G_ADDR_ID id, UINT8* pStack) { /* deprecated ! */
211 if(id>=MAX_RVF_TASKS) return 1;
212 pRtAddrIdTable[id]->p_os_stack=pStack;
213
214 return RVF_OK;
215 }
216
217 T_RVF_RET rvf_isHostingTaskIdle(T_RVF_G_ADDR_ID id, UINT8* status) { /* deprecated ! */
218 if(id>=MAX_RVF_TASKS) return RVF_INVALID_PARAMETER;
219 if(!pRtAddrIdTable[id]) return RVF_INVALID_PARAMETER;
220 if(pRtAddrIdTable[id]->type_code!=ET2_HOST_TASK) return RVF_INVALID_PARAMETER;
221
222 if(pRtAddrIdTable[id]->hosting_count==0) *status=1;
223 return RVF_OK;
224 }
225
226 /* convenience/helper fnc. */
227 T_RVF_G_ADDR_ID resolveHostAddrId(T_RVF_G_ADDR_ID id) {
228 if(!pRtAddrIdTable[id]) return RVF_INVALID_ADDR_ID;
229 return pRtAddrIdTable[id]->host_addr_id;
230 }
231
232 /* HostingCounter enables one to deduce if task can be terminated */
233 /* ERROR return val must be revised */
234 T_RVF_RET rvf_registerToHost(T_RVF_G_ADDR_ID host_id, T_RVF_G_ADDR_ID eid) {
235 UINT8 i;
236
237 if (host_id >= MAX_RVF_TASKS || eid >= MAX_RVF_G_ADDR_ID ) return RV_INVALID_PARAMETER;;
238
239 for(i=0;pRtAddrIdTable[host_id]->parasites[i]!=0 && i < MAX_PARASITES; i++);
240
241 pRtAddrIdTable[host_id]->parasites[i]=eid;
242 pRtAddrIdTable[host_id]->hosting_count++;
243
244 return RVF_OK;
245 }
246
247 T_RVF_RET rvf_unregisterFromHost(T_RVF_G_ADDR_ID host_id, T_RVF_G_ADDR_ID pid) {
248 UINT8 i=0;
249 if (pRtAddrIdTable[host_id]->hosting_count !=0) {
250 for(i=0; i<MAX_PARASITES || pRtAddrIdTable[host_id]->parasites[i]==pid; i++);
251 pRtAddrIdTable[host_id]->parasites[i]=0;
252
253 pRtAddrIdTable[host_id]->hosting_count--;
254 } else return RV_INVALID_PARAMETER;
255
256 return RVF_OK;
257 }
258
259 T_RVF_RET rvf_associateGrpToHost(T_RVF_G_ADDR_ID host_id, T_RVF_GD_ID gd_id) {
260
261 if (host_id >= MAX_RVF_TASKS ) return RV_INVALID_PARAMETER;
262
263 pRtAddrIdTable[host_id]->gdHost=gd_id;
264
265 return RVF_OK;
266 }
267
268 /*T_RVF_RET rvf_unregisterGrpFromHost(T_RVF_G_ADDR_ID host_id, T_RVF_G_ADDR_ID pid) {
269 if (pRtAddrIdTable[host_id]->hosting_count !=0) {
270 pRtAddrIdTable[host_id]->parasites[pid]=0;
271 pRtAddrIdTable[host_id]->hosting_count--;
272 } else return RV_INVALID_PARAMETER;
273
274 return RVF_OK;
275 }*/
276
277 T_RVF_G_ADDR_ID rvf_resolveHostingAddrId(T_RVM_GROUP_DIRECTIVE gd) {
278 int i=0;
279
280 for(i=1; i<MAX_RVF_TASKS; i++) { // i=1 by-pass RVM task
281 if(pRtAddrIdTable[i]!=NULL) {
282 if( pRtAddrIdTable[i]->type_code==ET2_HOST_TASK &&
283 // pRtAddrIdTable[i]->priority==priority && // to do ...
284 // pRtAddrIdTable[i]->os_stack_size>=stack_size
285 pRtAddrIdTable[i]->gdHost==gd.group_directive &&
286 pRtAddrIdTable[i]->hosting_count<MAX_PARASITES ) return i; // def. 10 A+ .
287 }
288 }
289
290 return RVF_INVALID_ADDR_ID; /* nothing found ret. param must be invalid*/
291 }
292
293 /*********************************************************************
294 * start() and stop() should be added to params, due to defered calling
295 * Allow for parasites to be added at RT and their start to be called.
296 *********************************************************************/
297 T_RVF_RET rvf_create_virtual_task (T_RV_RET (* handle_message)(T_RV_HDR * msg),
298 T_RV_RET (* handle_timer)(T_RV_HDR * msg),
299 T_RVF_G_ADDR_ID task_id, T_RVF_G_ADDR_ID host_task_id, char *taskname,
300 UINT8 priority, UINT8 tcode) {
301
302 if(!pRtAddrIdTable[task_id]) { /* allow for static init. or previous dyn init, eg. idle*/
303 if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[task_id])) return RVF_INTERNAL_ERR;
304 memset( pRtAddrIdTable[task_id], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );
305
306 rvf_mbox_buffer_init(pRtAddrIdTable[task_id]);
307 } /*else printf("RVF: task rt addr %d already alloc'ed\n", task_id); */
308
309 pRtAddrIdTable[task_id]->host_addr_id=host_task_id;
310 pRtAddrIdTable[task_id]->symbolic_name=taskname;
311 pRtAddrIdTable[task_id]->handle_message=handle_message;
312 pRtAddrIdTable[task_id]->handle_timer=handle_timer;
313 pRtAddrIdTable[task_id]->type_code=tcode;
314
315 pRtAddrIdTable[task_id]->pOSTCB=NULL; /* init to NULL for res.'free' */
316 pRtAddrIdTable[task_id]->pOSEvtGrp=NULL;
317 pRtAddrIdTable[task_id]->p_os_stack=NULL;
318 // pRtAddrIdTable[task_id]->p_tm_q=NULL;
319
320 // rvf_registerToHost(host_task_id, task_id);
321
322 return RVF_OK;
323 }
324
325 T_RVF_RET rvf_register_t3_handlers (T_RVF_G_ADDR_ID task_id,
326 T_RV_RET (* handle_message)(T_RV_HDR * msg),
327 T_RV_RET (* handle_timer)(T_RV_HDR * msg) ) {
328
329 pRtAddrIdTable[task_id]->handle_message=handle_message;
330 pRtAddrIdTable[task_id]->handle_timer=handle_timer;
331
332 return RVF_OK;
333 }
334
335 /* RVM must create a stack with its MB and pass as params to task create */
336 /*T_RVF_RET rvf_create_host_task (T_RV_RET (* proxy)(void), T_RVF_G_ADDR_ID task_id,
337 char *taskname, UINT8 *stack, UINT16 stacksize,
338 UINT8 priority, UINT8 tcode, UINT8 time_slicing, T_RVF_TASK_STATE suspend) {
339
340 return rvf_create_task( (TASKPTR)proxy, task_id, taskname, stack, stacksize,\
341 priority, tcode, time_slicing, suspend ) ;
342
343 }*/
344
345 /*******************************************************************************
346 **
347 ** Function rvf_create_task
348 **
349 ** Description This function is called to create a new rvf task.
350 ** time_slice represents the number of Nucleus ticks before a task is interrupted.
351 ** 0 for no time-slicing.
352 **
353 ** Returns RVF_OK if successful, else an error code
354 **
355 *******************************************************************************/
356 T_RVF_RET rvf_create_legacy_task (TASKPTR task_entry, UINT8 task_id, char *taskname, UINT8 *stack, UINT16 stacksize, UINT8 priority, UINT8 time_slicing, T_RVF_TASK_STATE is_suspend) {
357
358 return rvf_create_task(task_entry,task_id,taskname,stack,stacksize,priority,ET4_TASK,time_slicing,is_suspend);
359 }
360
361 T_RVF_RET rvf_create_task (TASKPTR task_entry, T_RVF_G_ADDR_ID task_id, char *taskname, UINT8 *stack, UINT16 stacksize,
362 UINT8 priority, UINT8 tcode, UINT8 time_slicing, T_RVF_TASK_STATE suspend) {
363
364 /*if (task_id >= MAX_RVF_TASKS) { return 1; } */
365 if (task_counter >= MAX_RVF_TASKS) { return 1; }
366
367 /* fill the task stack with a 0xFE pattern to allow use with stack monitoring tool */
368 memset( stack, 0xFE, stacksize );
369
370 /* allow for immediate task creation, eg. no alloc taskId(), but #defined task No. Dangerous!*/
371 if(!pRtAddrIdTable[task_id]) {
372 if(_RALLOC_TASK_RT_ADDR_DATA((T_RVF_BUFFER**)&pRtAddrIdTable[task_id])) return RVF_INTERNAL_ERR;
373 memset( pRtAddrIdTable[task_id], 0, sizeof(T_RVF_RT_ADDR_ID_DATA) );
374
375 rvf_mbox_buffer_init(pRtAddrIdTable[task_id]);
376 } /*else printf("RVF: task rt addr %d already alloc'ed\n", task_id); */
377
378 /*if(_RALLOC_TASK_RT_TM((T_RVF_BUFFER**)&pRtAddrIdTable[task_id]->p_tm_q)) {
379 rvf_free_buf(pRtAddrIdTable[task_id]);
380 pRtAddrIdTable[task_id]=NULL;
381 return RVF_INTERNAL_ERR;
382 } else pRtAddrIdTable[task_id]->p_tm_q->timerCnt=0;*/
383
384 pRtAddrIdTable[task_id]->hosting_count=0;
385 pRtAddrIdTable[task_id]->host_addr_id=task_id;
386 pRtAddrIdTable[task_id]->symbolic_name=taskname;
387 pRtAddrIdTable[task_id]->priority=priority;
388 pRtAddrIdTable[task_id]->type_code=tcode;
389
390 memset(&pRtAddrIdTable[task_id]->parasites, 0, (sizeof(T_RVF_G_ADDR_ID)*MAX_PARASITES));
391
392 if (stack) {
393 pRtAddrIdTable[task_id]->p_os_stack = (UINT8 *)stack;// - stacksize;
394 pRtAddrIdTable[task_id]->os_stack_size = stacksize;
395 } else {
396 // rvf_free_buf(pRtAddrIdTable[task_id]->p_tm_q);
397 rvf_free_buf(pRtAddrIdTable[task_id]);
398 pRtAddrIdTable[task_id]=NULL;
399 return RVF_INTERNAL_ERR;
400 }
401
402 if(_RALLOC_TASK_CNTRL_BLK((T_RVF_BUFFER**)&pRtAddrIdTable[task_id]->pOSTCB)) {
403 rvf_free_buf(stack);
404 // rvf_free_buf(pRtAddrIdTable[task_id]->p_tm_q);
405 rvf_free_buf(pRtAddrIdTable[task_id]);
406 pRtAddrIdTable[task_id]=NULL;
407 return RVF_INTERNAL_ERR;
408 }
409 memset( pRtAddrIdTable[task_id]->pOSTCB, 0, sizeof(NU_TASK) );
410
411 if(_RALLOC_TASK_EVT_GRP((T_RVF_BUFFER**)&pRtAddrIdTable[task_id]->pOSEvtGrp)) {
412 rvf_free_buf(pRtAddrIdTable[task_id]->pOSTCB);
413 rvf_free_buf(stack);
414 // rvf_free_buf(pRtAddrIdTable[task_id]->p_tm_q);
415 rvf_free_buf(pRtAddrIdTable[task_id]);
416 pRtAddrIdTable[task_id]=NULL;
417 return RVF_INTERNAL_ERR;
418 }
419 memset( pRtAddrIdTable[task_id]->pOSEvtGrp, 0, sizeof(NU_EVENT_GROUP) );
420
421 /* Create one Event Group for this task */
422 if( NU_SUCCESS != \
423 NU_Create_Event_Group (pRtAddrIdTable[task_id]->pOSEvtGrp,\
424 taskname) ) {
425 return RVF_INTERNAL_ERR;
426 }
427
428 //}
429 /* Create Task */
430 if ( NU_SUCCESS !=
431 NU_Create_Task (pRtAddrIdTable[task_id]->pOSTCB, /* Task Control Block */
432 taskname, /*taskname,*/ /* Task Name */
433 (NU_TASK_ENTRY )task_entry, /* Task Entry Function */
434 0, /* why prev. task_id ??? */ /* ARGC A-M-E-N-D-E-D! */
435 NULL, /* ARGV */
436 pRtAddrIdTable[task_id]->p_os_stack, /* Begining of Stack */
437 pRtAddrIdTable[task_id]->os_stack_size, /* Stack size */
438 priority, /* Priority */
439 time_slicing, /* Time Slicing Period*/
440 NU_PREEMPT, /* Preemption allowed */
441 (OPTION)(suspend == RUNNING ? NU_START : NU_NO_START) )/* Start the task or suspend it */
442 ) {
443 return RVF_INTERNAL_ERR;
444 }
445 task_counter++; /* MUST 'DEC' ON TASK TERMINATION */
446
447 return RVF_OK;
448 }
449
450 /* to be called from func. 'create_tasks()' of module "create_RVtasks.c"
451 * utilises static alloated system variables */
452 /*T_RVF_RET rvf_create_boot_task (TASKPTR task_entry, UINT8 task_id, char *taskname, UINT8 *stack, UINT16 stacksize, UINT8 priority, UINT8 time_slicing, T_RVF_TASK_STATE suspend) {
453 if (task_id >= MAX_RVF_TASKS) { return 1;}
454
455 if (stack) {
456 bOSStack = (UINT8 *)stack;// - stacksize;
457 bOSStackSize = stacksize;
458 }
459 // copy the task name into an internal buffer
460 _rvf_name_cpy( &(bOSTName[0][0]), taskname, RVF_MAX_TASK_LEN);
461
462 // fill the task stack with a 0xFE pattern to allow use with stack monitoring tool
463 memset( stack, 0xFE, stacksize );
464
465 // Create one Event Group for this task
466 if( NU_SUCCESS != NU_Create_Event_Group (&bOSEvtGrp[0], taskname) ){
467 return RVF_INTERNAL_ERR;
468 }
469
470 // Create Task
471 if ( NU_SUCCESS !=
472 NU_Create_Task (&bOSTCB[0], // (reserved)Task Control Block
473 taskname, // Task Name
474 (NU_TASK_ENTRY )task_entry, // Task Entry Function
475 task_id, // ARGC
476 NULL, // ARGV
477 bOSStack, // Begining of Stack
478 stacksize, // Stack size
479 priority, // Priority
480 time_slicing, // Time Slicing Period
481 NU_PREEMPT, // Preemption allowed
482 (OPTION)(suspend == RUNNING ? NU_START : NU_NO_START) )// Start the task or suspend it
483 )
484 { return RVF_INTERNAL_ERR;
485 }
486 task_counter++;
487 return RVF_OK;
488 }*/
489
490 /* Later timer blk must be added, once made dynamic */
491 T_RVF_RET rvf_free_sys_resources(T_RVF_G_ADDR_ID gid, UINT8 rm) {
492
493 if(!pRtAddrIdTable[gid]) return RVF_INTERNAL_ERR;
494
495 if(rm==1 || rm==2) {
496 if(pRtAddrIdTable[gid]->pOSTCB)rvf_free_buf(pRtAddrIdTable[gid]->pOSTCB);
497 if(pRtAddrIdTable[gid]->pOSEvtGrp)rvf_free_buf(pRtAddrIdTable[gid]->pOSEvtGrp);
498 if(pRtAddrIdTable[gid]->p_os_stack)rvf_free_buf(pRtAddrIdTable[gid]->p_os_stack);
499
500 task_counter--;
501 }
502 if(rm==0 || rm==2) {
503 if(pRtAddrIdTable[gid])rvf_free_buf(pRtAddrIdTable[gid]);
504 pRtAddrIdTable[gid]=NULL;
505 }
506
507 return RVF_OK;
508 }
509
510 /*******************************************************************************
511 **
512 ** Function rvf_exit_task
513 **
514 ** Description This function is called to stop a rvf task.
515 ** A task can kill another task or itself.
516 **
517 ** Returns void
518 **
519 *******************************************************************************/
520 void rvf_exit_task (T_RVF_G_ADDR_ID task_id)
521 {
522 if(!pRtAddrIdTable[task_id]) return;
523 /*
524 ** Empty task's mail box
525 */
526 _rvf_empty_mailboxes(task_id);
527
528 /*
529 ** Terminate task
530 */
531 NU_Terminate_Task(pRtAddrIdTable[task_id]->pOSTCB); /*&OSTCB[task_id]);*/
532 NU_Delete_Task(pRtAddrIdTable[task_id]->pOSTCB);
533
534 /*
535 ** Delete related event group
536 */
537 NU_Delete_Event_Group (pRtAddrIdTable[task_id]->pOSEvtGrp);
538
539 pRtAddrIdTable[task_id]->p_os_stack=0;
540 //OSStack[task_id] = 0;
541 }
542
543
544 /*******************************************************************************
545 **
546 ** Function rvf_suspend_task
547 **
548 ** Description This function is called to suspend a rvf task.
549 ** A task can suspend another task or itself.
550 **
551 ** Returns void
552 **
553 *******************************************************************************/
554 T_RVF_RET rvf_suspend_task (T_RVF_G_ADDR_ID task_id)
555 {
556 if(!pRtAddrIdTable[task_id]) return RVF_INVALID_PARAMETER;
557 NU_Suspend_Task(pRtAddrIdTable[task_id]->pOSTCB); // A-M-E-N-D-E-D!
558
559 return RVF_OK;
560 }
561
562
563 /*******************************************************************************
564 **
565 ** Function rvf_wait
566 **
567 ** Description This function is called by tasks to wait for a specific
568 ** event or set of events. The task may specify the duration
569 ** that it wants to wait for, or 0 if infinite.
570 **
571 ** Returns the event mask of received events or zero if timeout
572 **
573 *******************************************************************************/
574 UINT16 rvf_wait (UINT16 flag, UINT32 timeout) {
575 T_RVF_G_ADDR_ID rtask = rvf_get_taskid();
576
577 if (!timeout) timeout = 0xFFFFFFFFL;
578
579 return rvf_evt_wait(rtask, flag, timeout);
580 }
581
582 UINT16 rvf_evt_wait(T_RVF_G_ADDR_ID rtask, UINT16 flag, UINT32 timeout) {
583
584 #define RVF_RET_TIME_OUT 0
585
586 UINT16 mbxEvt = 0;
587 UNSIGNED evt = 0;
588 UNSIGNED clear = 0;
589 STATUS status_ret;
590
591 /* Check if anything in any of the mailboxes. Possible race condition. */
592
593 if (rtask>=MAX_RVF_TASKS || !pRtAddrIdTable[rtask]) {
594 RVM_TRACE_WARNING_PARAM("RVF: Illegal MBOX or MBOX not ready!", rtask);
595 return (UINT16) RVF_RET_TIME_OUT;
596 }
597
598 if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[0])
599 mbxEvt |= RVF_TASK_MBOX_0_EVT_MASK;
600 if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[1])
601 mbxEvt |= RVF_TASK_MBOX_1_EVT_MASK;
602 if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[2])
603 mbxEvt |= RVF_TASK_MBOX_2_EVT_MASK;
604 if (pRtAddrIdTable[rtask] && pRtAddrIdTable[rtask]->OSTaskQFirst[3])
605 mbxEvt |= RVF_TASK_MBOX_3_EVT_MASK;
606
607 /* If any valid event if pending, return immediately */
608 if (mbxEvt & flag)
609 {
610 /* Return only those bits which user wants... */
611 evt = (UINT16) (mbxEvt & flag);
612
613 /* clear the nucleus event(s) for mailboxes */
614 if ( mbxEvt & 0x000F ) /* a mailbox event is signaled*/
615 { NU_Retrieve_Events (pRtAddrIdTable[rtask]->pOSEvtGrp, (UNSIGNED) mbxEvt & 0x000F, NU_AND_CONSUME,
616 (UNSIGNED *)&clear, NU_NO_SUSPEND);
617 }
618
619
620 return ((UINT16) evt);
621 }
622
623
624 if(pRtAddrIdTable[rtask]) {
625 status_ret = NU_Retrieve_Events (pRtAddrIdTable[rtask]->pOSEvtGrp,
626 (UNSIGNED) flag, NU_OR_CONSUME,
627 (UNSIGNED *)&evt, timeout );
628 if ( status_ret == NU_SUCCESS) {
629 return (UINT16) evt;
630 } else { /* timeout or error case */
631 return (UINT16) RVF_RET_TIME_OUT;
632 }
633 } return (UINT16) RVF_RET_TIME_OUT;
634 }
635
636 /*******************************************************************************
637 **
638 ** Function rvf_wait_for_specific_msg
639 **
640 ** Description This function is called by tasks to wait for a specific
641 ** message in the specified mailbox. The task may specify the duration
642 ** that it wants to wait for, or 0 if infinite.
643 **
644 ** Returns A pointer to the message, NULL in case of time-out.
645 **
646 *******************************************************************************/
647 T_RVF_BUFFER * rvf_wait_for_specific_msg(UINT16 msg_code, UINT8 mbox, UINT32 timeout)
648 {
649 T_RVF_G_ADDR_ID task_id = rvf_get_taskid();
650 T_RVF_BUFFER * p_buf = NULL;
651 T_RVF_INTERNAL_BUF * p_hdr;
652 UNSIGNED clear = 0;
653 STATUS status_ret;
654 UINT32 wait_time;
655 UINT32 init_time = rvf_get_tick_count();
656
657
658 /* check input parameter */
659 if ( mbox >= RVF_NUM_TASK_MBOX) /* NOTE: must be def to 2 max */
660 { rvf_send_trace( "RVF: rvf_wait_for_specific_msg(): invalid mailbox id", 52, NULL_PARAM, RV_TRACE_LEVEL_ERROR, RVM_USE_ID);
661 return p_buf;
662 }
663
664 if(!timeout)
665 timeout = 0xFFFFFFFFL;
666 wait_time = timeout;
667
668 while( (rvf_get_tick_count() - init_time) < timeout )
669 {
670 /* test all messages in the mailbox */
671 if( pRtAddrIdTable[task_id]->OSTaskQFirst[mbox] )
672 {
673 rvf_disable(9);
674 p_hdr = pRtAddrIdTable[task_id]->OSTaskQFirst[mbox];
675 p_buf = MEM2USER(p_hdr);
676 /* test the first one */
677 if ( ((T_RV_HDR *)p_buf)->msg_id == msg_code )
678 { /* message found, return it */
679 pRtAddrIdTable[task_id]->OSTaskQFirst[mbox] = p_hdr->p_next;
680 p_hdr->p_next = NULL;
681
682 #if RVF_ENABLE_BUF_LINKAGE_CHECK
683 RVF_SET_BUF_UNLINKED(p_hdr);
684 #endif
685 /* clear the Nucleus Event for this mailbox */
686 NU_Retrieve_Events( pRtAddrIdTable[task_id]->pOSEvtGrp, EVENT_MASK(mbox), NU_AND_CONSUME,
687 (UNSIGNED *)&clear, NU_NO_SUSPEND);
688 rvf_enable();
689 return p_buf;
690 }
691
692 while(p_hdr->p_next != NULL)
693 {
694 p_buf = MEM2USER(p_hdr->p_next);
695 if ( ((T_RV_HDR *)p_buf)->msg_id == msg_code )
696 { /* remove it from the list */
697 p_hdr->p_next = ( (T_RVF_INTERNAL_BUF *) USER2MEM(p_buf))->p_next;
698 /* check if it the last one */
699 if ( pRtAddrIdTable[task_id]->OSTaskQLast[mbox] == USER2MEM(p_buf) )
700 { pRtAddrIdTable[task_id]->OSTaskQLast[mbox] = p_hdr;
701 }
702 ((T_RVF_INTERNAL_BUF *) USER2MEM(p_buf))->p_next = NULL;
703
704 #if RVF_ENABLE_BUF_LINKAGE_CHECK
705 RVF_SET_BUF_UNLINKED(USER2MEM(p_buf));
706 #endif
707 rvf_enable();
708 return p_buf;
709 }
710 p_hdr = p_hdr->p_next;
711 }
712
713 rvf_enable();
714 }
715
716 /* here, the message has not been found, so wait for a new message */
717
718 if ((wait_time != 0xFFFFFFFFL) &&
719 (timeout > rvf_get_tick_count() - init_time))
720 {
721 /* NU_Retrieve_Events bug: cannot call function with a parameter with F in MSB */
722 wait_time = (timeout - (rvf_get_tick_count() - init_time)) & 0x0FFFFFFFL;
723 }
724
725 status_ret = NU_Retrieve_Events( pRtAddrIdTable[task_id]->pOSEvtGrp, EVENT_MASK(mbox), NU_OR_CONSUME,
726 (UNSIGNED *)&clear, wait_time);
727
728 if( status_ret != NU_SUCCESS) /* time out */
729 { return NULL;
730 }
731
732 }
733 return NULL;
734 }
735
736
737 /*******************************************************************************
738 **
739 ** Function rvf_delay
740 **
741 ** Description This function is called by tasks to sleep unconditionally
742 ** for a specified amount of time.
743 **
744 ** Returns void
745 **
746 *******************************************************************************/
747 void rvf_delay (UINT32 timeout)
748 {
749 if (timeout == 0)
750 { timeout = 1;
751 }
752
753 NU_Sleep(timeout);
754
755 }
756
757
758 /*******************************************************************************
759 **
760 ** Function rvf_send_event
761 **
762 ** Description This function is called by tasks to send events to other
763 ** tasks. Tasks can also send events to themselves.
764 **
765 ** Returns 0 if all OK, else 1
766 **
767 *******************************************************************************/
768 UINT8 rvf_send_event (T_RVF_G_ADDR_ID task_id, UINT16 event) {
769
770 if (task_id >= MAX_RVF_TASKS || !pRtAddrIdTable[task_id] ) return 1;
771
772 NU_Set_Events (pRtAddrIdTable[task_id]->pOSEvtGrp, (UNSIGNED)event, NU_OR);
773
774 return 0;
775 }
776
777 /*******************************************************************************
778 **
779 ** Function rvf_get_taskid
780 **
781 ** Description This function gets the currently running task ID.
782 **
783 ** Returns task ID
784 **
785 *******************************************************************************/
786 T_RVF_G_ADDR_ID rvf_get_taskid(void) { /* Retrieve the taskid using index of the task pointer in the OSTCB array */
787 NU_TASK * currTask=0;
788 T_RVF_G_ADDR_ID taskId=RVF_INVALID_ADDR_ID;
789
790 currTask = NU_Current_Task_Pointer();
791
792 if( currTask != NU_NULL) {
793 /* find the task pointer in the OSTCB array */
794 for ( taskId = 0; taskId < MAX_RVF_TASKS; taskId++) {
795 if( (pRtAddrIdTable[taskId]!= NULL) &&
796 ((pRtAddrIdTable[taskId]->pOSTCB)) == currTask) return taskId;
797 }
798 return RVF_INVALID_ADDR_ID;
799 } else {
800 return RVF_INVALID_ADDR_ID; /* error case, must return an error code */
801 }
802 }
803
804
805 /*******************************************************************************
806 **
807 ** Function rvf_get_taskname
808 **
809 ** Description This function gets the currently running task name.
810 **
811 ** Returns pointer to task name or NULL if error
812 **
813 *******************************************************************************/
814 char* rvf_get_taskname(void)
815 { T_RVF_G_ADDR_ID id = rvf_get_taskid();
816
817 if (id == 0xFF) return NULL; /* return NULL if rvf_get_taskid returns 0xFF */
818
819 return pRtAddrIdTable[id]->symbolic_name; /*(OSTName[ id ]);*/
820 }
821
822 /*******************************************************************************
823 **
824 ** Function rvf_enable
825 **
826 ** Description This function enables interrupts.
827 **
828 ** Returns void
829 **
830 *******************************************************************************/
831 void rvf_enable(void)
832 {
833
834 if( --OSDisableNesting == 0) /* Control nesting interrupt */
835 {
836 if( OSInterruptAlreadyMasked == TRUE) /* check if interrupts have been disabled outside RVF,
837 in that case, do not enable interrupts */
838 { OSInterruptAlreadyMasked = FALSE;
839 }
840 else
841 { NU_Control_Interrupts(OSLastIntLevel);
842 }
843 }
844
845 }
846
847 #ifdef _WINDOWS
848
849 /*******************************************************************************
850 **
851 ** Function INT_Check_IRQ_Mask()
852 **
853 ** Description This function checks if the IRQ are disabled (outside RVF).
854 **
855 ** Returns IRQ mask
856 **
857 *******************************************************************************/
858 UINT32 INT_Check_IRQ_Mask(void)
859 {
860 return 0;
861 }
862 #else
863
864 /*-------------------------------------------------------*/
865 /* INT_Check_IRQ_Mask() */
866 /*-------------------------------------------------------*/
867 /* */
868 /* Description: check in the CPSR register if the IRQ */
869 /* are masked out or not. */
870 /* ------------ */
871 /* */
872 /*-------------------------------------------------------*/
873 /* Declaration of ASM INT_Check_IRQ_Mask function */
874 UINT32 INT_Check_IRQ_Mask(void);
875
876 /*
877 * FreeCalypso: this assembly function will be moved out into
878 * its own source file, or maybe added to nucleus/tct.S.
879 */
880 #if 0
881 asm(" .def $INT_Check_IRQ_Mask");
882 asm("$INT_Check_IRQ_Mask ");
883 asm(" .ref _INT_32_Check_IRQ_Mask");
884 asm(".state16");
885 asm(" ADR r0,_INT_32_Check_IRQ_Mask ");
886 asm(" BX r0 ");
887
888 asm(" .align");
889 asm(" .state32");
890 asm(" .def _INT_32_Check_IRQ_Mask");
891 asm("_INT_32_Check_IRQ_Mask ");
892
893 asm(" MRS r0,CPSR "); // pick up CPSR
894 asm(" BX lr "); // return to caller
895 #endif
896 #endif
897
898
899 /*******************************************************************************
900 **
901 ** Function rvf_disable
902 **
903 ** Description This function disables interrupts.
904 **
905 ** Returns void
906 **
907 *******************************************************************************/
908
909 #define RVF_IRQ_DISABLED_MASK 0x00000080
910
911 void rvf_disable(UINT8 who)
912 {
913
914 /* Control interrupt nesting ourselves */
915 if (OSDisableNesting == 0)
916 {
917 if ( INT_Check_IRQ_Mask() & RVF_IRQ_DISABLED_MASK) /* if IRQ are disabled (outside RVF) */
918 { OSInterruptAlreadyMasked = TRUE;
919 }
920 else
921 { OSLastIntLevel = NU_Control_Interrupts(NU_DISABLE_INTERRUPTS);
922 }
923 }
924 OSDisableNesting++;
925
926 }
927
928
929 /*******************************************************************************
930 **
931 ** Function rvf_used_stack
932 **
933 ** Description This function tries to calculate the amount of
934 ** stack used by looking for a zero.
935 **
936 ** Returns the number of non-zero bytes on the stack
937 **
938 *******************************************************************************/
939 UINT16 rvf_used_stack(T_RVF_G_ADDR_ID task)
940 {
941 UINT16 j, stacksize;
942 UINT8 *p;
943
944 if(!pRtAddrIdTable[task]) return 0;
945
946 stacksize = pRtAddrIdTable[task]->os_stack_size; /*OSStackSize[task];*/
947 p = pRtAddrIdTable[task]->p_os_stack; /*OSStack[task];*/
948 for(j = 0; (j < stacksize) && (*p++ == 0xFE); j++);
949
950 return ((UINT16)(stacksize - j));
951 }
952
953
954 /*******************************************************************************
955 **
956 ** Function rvf_dump_tasks
957 **
958 ** Description This function dump all the rvf tasks.
959 **
960 ** Returns void
961 **
962 *******************************************************************************/
963 void rvf_dump_tasks()
964 { UINT8 num_task;
965 char task_info[100];
966
967 rvf_send_trace("*** START DUMPING TASKS ***", 27, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, RVM_USE_ID);
968
969 /* for each task, display its name, its id, its stack size*/
970 rvf_send_trace("*TASK_NAME Id Stack_size Used_stack", 35, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, RVM_USE_ID);
971
972 for ( num_task = 0; num_task < MAX_RVF_G_ADDR_ID; num_task++ )
973 {
974 /* trace the task if it has been created*/
975 if (pRtAddrIdTable[num_task] != 0 ) {
976 sprintf( task_info, "%10.10s %2d %5d %5d",
977 pRtAddrIdTable[num_task]->symbolic_name /*OSTName[num_task]*/,
978 num_task,
979 pRtAddrIdTable[num_task]->os_stack_size,
980 rvf_used_stack( num_task) );
981 rvf_send_trace( task_info, 35, NULL_PARAM, RV_TRACE_LEVEL_DEBUG_HIGH, RVM_USE_ID);
982 }
983 }
984 /* find a way to track task stack usage and display it */
985 /* using NU_Check_Stack, NU_Task_Information or finding the first non-zero value in the stack */
986 }
987
988
989 /****************************************************************************/
990 /* For Big Endian Processors swap the bytes */
991 #if defined(__BIG_ENDIAN)
992 UINT16 ntohs(UINT16 n)
993 {
994 register UINT8 tmp;
995 register UINT8 *p=(UINT8 *)&n;
996
997 tmp = p[0];
998 p[0] = p[1];
999 p[1] = tmp;
1000
1001 return n;
1002 }
1003
1004 UINT32 ntohl(UINT32 n)
1005 {
1006 register UINT8 tmp;
1007 register UINT8 *p=(UINT8 *)&n;
1008
1009 tmp = p[0];
1010 p[0] = p[3];
1011 p[3] = tmp;
1012
1013 tmp = p[1];
1014 p[1] = p[2];
1015 p[2] = tmp;
1016
1017 return n;
1018 }
1019
1020 #endif /* __BIG_ENDIAN*/
1021
1022
1023
1024
1025 /******************************************************************************
1026 **
1027 ** Function rvf_send_trace
1028 **
1029 ** Description This function displays a message essentially for debug purposes.
1030 ** It displays the msg_length characters of the string pointed by msg
1031 ** and the value of val.
1032 **
1033 ** Returns void
1034 **
1035 ******************************************************************************/
1036 #ifdef _WINDOWS
1037 #ifndef _CONSOLE
1038 void rvf_send_trace1( INT8 * msg, UINT8 msg_length, UINT32 val, UINT8 TRACE_LEVEL, UINT32 swe_use_id)
1039 {
1040 /* Function to display trace message for Tool */
1041
1042 UINT32 trace_type = swe_use_id;
1043
1044 Trace( msg, msg_length, val, TRACE_LEVEL, trace_type);
1045 }
1046 #endif
1047 #endif
1048
1049 #ifdef _CONSOLE /* CONSOLE */
1050 void rvf_send_trace1( INT8 * msg, UINT8 msg_length, UINT32 val, UINT8 TRACE_LEVEL, UINT32 swe_use_id) {
1051 const int MAX = 1000;
1052 static int l = 0;
1053 char buf[100];
1054 HANDLE out = 0;
1055 int nb;
1056
1057 if(!out)out=GetStdHandle(STD_OUTPUT_HANDLE);
1058
1059 rvf_disable(25);
1060
1061 sprintf(buf,"%s %d\n", msg, val);
1062 WriteConsole(out, buf, strlen(buf), &nb, NULL);
1063
1064 if(l>=MAX) {
1065 system("cls");
1066 l=0;
1067 } else l++;
1068
1069 rvf_enable();
1070 }
1071 #endif /* CONSOLE */
1072
1073 /*******************************************************************************
1074 **
1075 ** Function rvf_resume_task
1076 **
1077 ** Description This function is called to resume a rvf task which is in a suspend state.
1078 **
1079 ** Returns RVF_OK if successful, else an error code
1080 **
1081 *******************************************************************************/
1082 T_RVF_RET rvf_resume_task( T_RVF_G_ADDR_ID taskid)
1083 {
1084 if(!pRtAddrIdTable[taskid]) return RVF_INVALID_PARAMETER;
1085 if(!pRtAddrIdTable[taskid]->pOSTCB) return RVF_INVALID_PARAMETER;
1086 /* resume the task */
1087 if ( NU_INVALID_TASK == NU_Resume_Task( pRtAddrIdTable[taskid]->pOSTCB) ) {// A-M-E-N-D-E-D!
1088 return RVF_INTERNAL_ERR; //RVF_INVALID_PARAMETER;
1089 }
1090 return RVF_OK;
1091 }
1092
1093 /*******************************************************************************
1094 **
1095 ** Function rvf_initialize_mutex
1096 **
1097 ** Description This function initialize a mutex structure, which will be used
1098 ** to protect shared variables against simultaneous access.
1099 **
1100 ** Returns RVF_OK if successful, else an error code
1101 **
1102 *******************************************************************************/
1103 T_RVF_RET rvf_initialize_mutex( T_RVF_MUTEX * mutex)
1104 {
1105
1106 /* initializes the mutex structure */
1107
1108 if( NU_Create_Semaphore( (NU_SEMAPHORE *)mutex, "RVF", 1, NU_PRIORITY ) != NU_SUCCESS)
1109 { return RVF_INTERNAL_ERR;
1110 }
1111
1112 return RVF_OK;
1113 }
1114
1115 /*******************************************************************************
1116 **
1117 ** Function rvf_lock_mutex
1118 **
1119 ** Description This function locks a mutex to avoid simultaneous access.
1120 ** If the mutex is already locked, the task is suspended
1121 ** until the mutex is unlocked.
1122 **
1123 ** Returns RVF_OK if successful, else an error code
1124 **
1125 *******************************************************************************/
1126 T_RVF_RET rvf_lock_mutex( T_RVF_MUTEX * mutex)
1127 {
1128 if( NU_Obtain_Semaphore( (NU_SEMAPHORE *)mutex, NU_SUSPEND ) != NU_SUCCESS)
1129 { return RVF_INTERNAL_ERR;
1130 }
1131
1132 return RVF_OK;
1133 }
1134
1135 /*******************************************************************************
1136 **
1137 ** Function rvf_unlock_mutex
1138 **
1139 ** Description This function unlocks a mutex to avoid simultaneous access.
1140 **
1141 ** Returns RVF_OK if successful, else an error code
1142 **
1143 *******************************************************************************/
1144 T_RVF_RET rvf_unlock_mutex( T_RVF_MUTEX * mutex)
1145 {
1146 if( NU_Release_Semaphore( (NU_SEMAPHORE *)mutex ) != NU_SUCCESS)
1147 { return RVF_INTERNAL_ERR;
1148 }
1149
1150 return RVF_OK;
1151 }
1152
1153 /*******************************************************************************
1154 **
1155 ** Function rvf_delete_mutex
1156 **
1157 ** Description This function deletes a previously created mutex.
1158 **
1159 ** Returns RVF_OK if successful, else an error code
1160 **
1161 *******************************************************************************/
1162 T_RVF_RET rvf_delete_mutex( T_RVF_MUTEX * mutex)
1163 {
1164 if( NU_Delete_Semaphore( (NU_SEMAPHORE *)mutex ) != NU_SUCCESS)
1165 { return RVF_INTERNAL_ERR;
1166 }
1167
1168 return RVF_OK;
1169 }
1170
1171 void rvf_yield() { NU_Relinquish(); }
1172
1173 /* convenience function */
1174 UINT8 rvf_isType2() {
1175 if(pRtAddrIdTable[rvf_get_taskid()]->type_code==ET2_HOST_TASK) return 1;
1176 else return 0;
1177 }