/************************************************************************/ /* SISCO SOFTWARE MODULE HEADER *****************************************/ /************************************************************************/ /* (c) Copyright Systems Integration Specialists Company, Inc., */ /* 1998-2005, All Rights Reserved. */ /* */ /* PROPRIETARY AND CONFIDENTIAL */ /* */ /* MODULE NAME : mvl_uca.c */ /* PRODUCT(S) : MMSEASE */ /* */ /* MODULE DESCRIPTION : */ /* Special read/write processing functions for UCA and */ /* IEC 61850 objects. */ /* */ /* NOTE: define MVL61850_CTL_DISABLE to avoid calling user functions */ /* (u_mvl61850_ctl_oper_*) if 61850 Controls not needed. */ /* */ /* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */ /* */ /* MODIFICATION LOG : */ /* Date Who Rev Comments */ /* -------- --- ------ ------------------------------------------- */ /* 01/27/09 JRB 70 Don't send write resp until ind is done. */ /* Don't allow write if any leaf unwritable. */ /* Call user func at start/end of complex write.*/ /* 01/07/09 JRB 69 Del S_LOCK for OPC_CLIENT in u_mvl_read_ind */ /* (already locked by mvl_comm_serve). */ /* 06/30/08 JRB 68 Repl cmd_executing w/ ctlState (more flexible)*/ /* Do SBO Ctrl checks only if Oper/Cancel is */ /* ONLY structure being written. */ /* Fix sboName buffer size. */ /* 05/06/08 JRB 67 Set new cmd_executing flag. */ /* 03/03/08 JRB 66 Call mvl61850_ctl_req_done to do what's needed*/ /* when IEC 61850 control request completes. */ /* 02/18/08 JRB 65 Use new mvl_var_create_derived function. */ /* 02/07/08 JRB 64 Fix alignment of ptr passed to mvlu_clone.. */ /* 12/18/07 MDE 63 Fixed lastDollar crash for IOS */ /* 11/12/07 MDE 62 Changed MMSOP_RDWR_USR_HANDLED to _RD_ */ /* 07/20/07 JRB 61 getGnlVarNames: fix crash when flatname */ /* too long; add return code & check it. */ /* 05/09/07 JRB 60 Fix alt_acc arg passed to mvlu_find_component*/ /* Fix log macro args. */ /* 04/30/07 JRB 59 Fix to work with RUNTIME_TYPE change. */ /* 04/17/07 JRB 58 Fix calc of offset_from_base & prim_num */ /* to work for Alt Access on arrays (corrects */ /* primData, prim_num, and prim_offset_base */ /* passed to leaf functions). */ /* mvlu_find_struct_comp: add args to compute */ /* offset & prim_num. */ /* 03/07/07 JRB 57 Add mvlu_get_leaf_val_int_any. */ /* 11/21/06 JRB 56 Send LastApplError if write of Oper, etc fails.*/ /* 10/30/06 JRB 55 Use new mvl_vmd_* object handling functions. */ /* mvlu_find_base_va: add args. */ /* u_mvl_get_va_aa: add args. */ /* u_gnl_ind_*: add net_info arg to */ /* elim use of global var "_mvl_curr_net_info". */ /* u_mvl_get_va_aa: do not set va->usr_ind_ctrl */ /* (done by calling functions). */ /* 10/26/06 JRB 54 Del unused local vars. */ /* 09/27/06 MDE 53 Added MMSOP_RD_USR_HANDLED for IOS */ /* 09/13/06 JRB 52 mvlu_find_comp_type: allow non-dynamic types.*/ /* 08/09/06 JRB 51 Del u_mvl_get_nvl, u_mvl_free_nvl functions */ /* (not needed with new improved Foundry). */ /* Use "mvl_var_create/destroy" so all variables*/ /* created/destroyed in one place. */ /* 07/24/06 JRB 50 Chg some common ERR logs to FLOW. */ /* 03/27/06 JRB 49 Add more mvlu_get_leaf_* functions. */ /* 07/29/05 MDE 48 Fixed static data use for write handling */ /* 07/11/05 JRB 47 Call user fcts (u_mvl61850_ctl_oper_*) if */ /* !defined (MVL61850_CTL_DISABLE). */ /* 01/19/05 JRB 46 u_gnl_ind_* return (-1) on error. */ /* 12/09/04 JRB 45 init_prim_info_recursive: fix ARR_END handling.*/ /* init_prim_info_arr: simplify & ret ST_VOID. */ /* Add mvlu_find_comp_type, mvlu_get_leaf_val*. */ /* Chg trim_branch_name to global mvlu_trim_.. */ /* & simplify it using strrchr. */ /* 09/20/04 JRB 44 startElWrites: if writing "Oper" or "Cancel" */ /* struct, check SBO state. */ /* mvlu_wr_prim_done call mvlu_sbo_ctrl_free. */ /* 06/29/04 JRB 43 Del global var mvluUseStaticData, instead use*/ /* use_static_data flag in MVL_VAR_ASSOC for Read.*/ /* NEVER use static data for Write. */ /* startElReads, etc: add prim_info arg. */ /* Del elmntOffset, use prim_info->prim_offset. */ /* 11/24/03 JRB 42 getGnlVarNames: fix prefix len by using */ /* MAX_IDENT_LEN, chk overflow BEFORE writing, */ /* & add logging & asserts. */ /* 09/18/03 JRB 41 Allow alt acc on array of "nested" structures*/ /* Add some debug logging & extra comments. */ /* 05/02/03 JRB 40 switch(rt->el_tag): Use default for most cases*/ /* 04/04/03 JRB 39 Fix integrity/GI scan code so multiple */ /* concurrent scans don't corrupt one another. */ /* 12/20/02 JRB 38 Moved mvlu_set_leaf_param to mvluleaf.c */ /* 12/12/02 JRB 37 Use usr_resp_fun ptr to call scan done funcs.*/ /* 12/09/02 MDE 36 Made mvlu_find_uca_var global */ /* 11/27/02 MDE 35 Addded leaf indication handlers */ /* Addded mvlu_find_rt_leaf */ /* Addded mvlu_set_leaf_param */ /* 11/29/01 MDE 34 Added GOOSE function pointer */ /* 11/14/01 EJV 33 Added support for new MMS type UtcTime: */ /* 11/13/01 MDE 32 Added GOOSE scan support */ /* 05/21/01 MDE 31 Cleaned up memory allocation for SMEM */ /* 10/25/00 JRB 30 Del u_mvl & u_gnl funct ptrs. Call directly. */ /* Del mvlu_install (no longer needed). */ /* 08/18/00 JRB 29 Don't clear va_to_free. Need value later. */ /* mvlu_free_nvl free va->va_to_free only if */ /* it was allocated by mvlu_get_nvl. */ /* 08/18/00 RKR 28 Added rt fields to MVLU_ typedefs */ /* 07/13/00 JRB 27 Cleanup ms_comp_na.. chg for MVL_XNAME. */ /* 07/13/00 JRB 26 Use new ms_comp_name_find to get comp names. */ /* 07/13/00 JRB 25 Move these functs to mvl_type.c: */ /* mvlu_add_rt_type, mvlu_free_rt_type. */ /* 06/21/00 MDE 24 Now copy base VA user_info to new VA */ /* 05/15/00 MDE 23 Now filder out too-long variable names */ /* 04/14/00 JRB 22 Lint cleanup. */ /* 04/05/00 RKR 21 Made MVL_XNAME a compile time option */ /* 04/03/00 RKR 20 Added the xName to UCA Rd and Wr Ind funs */ /* 03/30/00 RKR 19 Passed the expanded UCA var name to ind fun */ /* 01/21/00 MDE 18 Now use MEM_SMEM for dynamic memory */ /* 12/20/99 MDE 17 Fix getArrAARtType to return SUCCESS/FAIL */ /* 11/03/99 JRB 16 Fix GetNameList if CA name = base var name. */ /* 09/30/99 EJV 15 Added slog macro to mvlu_rd_prim_done */ /* 09/13/99 MDE 14 Added SD_CONST modifiers */ /* 09/07/99 MDE 13 Revised and enhanced the UCA report system */ /* 06/04/99 MDE 12 Now allow arrays as base VA type, other */ /* minor changes to VA processing */ /* 06/04/99 MDE 11 Fixed memory leak for nested array alt acc */ /* 04/07/99 MDE 10 Logging improvements (fixed wrong AA log too)*/ /* 03/09/99 JRB 12 Fix illegal free of gnlNameBuf. */ /* 02/22/99 JRB 11 BUG FIX: Always start with clean "arrCtrl". */ /* 01/08/99 JRB 10 Use new "bsearch" object model. Don't use */ /* "_UCA_" prefix on va and nvl names. */ /* 12/11/98 MDE 09 Removed scope references from VA */ /* 11/17/98 MDE 08 Made mvlu_get_va_aa alloc space for name */ /* 11/16/98 MDE 07 Renamed internal functions (prefix '_') */ /* 09/21/98 MDE 06 Uninitialized ptr fix, Minor lint cleanup */ /* 08/11/98 MDE 05 Added UCA variable array support */ /* 07/13/98 MDE 04 Mixed scope NVL fixes, data alignment fix */ /* 06/29/98 MDE 03 Added report function pointers */ /* 06/15/98 MDE 02 Changes to allow compile under C++ */ /* 01/02/98 MDE 01 New */ /************************************************************************/ #include "glbtypes.h" #include "sysincs.h" #include "glbsem.h" #include "mmsdefs.h" #include "mms_pvar.h" #include "mms_vvar.h" #include "mvl_uca.h" #include "mvl_log.h" #if defined(MVL_UCA) /* This entire module is only valid for UCA. */ /************************************************************************/ /* For debug version, use a static pointer to avoid duplication of */ /* __FILE__ strings. */ #ifdef DEBUG_SISCO SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__; #endif /* PRIM_INFO struct: extra info about primitive. */ typedef struct { ST_UINT prim_num; /* index to data */ ST_UINT prim_offset; /* mem offset from start of var */ ST_UINT prim_offset_base; /* mem offset from start of "base" var */ } PRIM_INFO; /* extra info about primitive */ /************************************************************************/ static ST_VOID mvluDefGetVaDataBufFun (ST_INT service, MVL_VAR_ASSOC *va, ST_INT size); static ST_VOID mvluDefFreeVaDataBufFun (ST_INT service, MVL_VAR_ASSOC *va); /************************************************************************/ /* Read/Write leaf indication override handlers */ ST_VOID (*u_mvlu_leaf_rd_ind_fun)(MVLU_RD_VA_CTRL *mvluRdVaCtrl); ST_VOID (*u_mvlu_leaf_wr_ind_fun)(MVLU_WR_VA_CTRL *mvluWrVaCtrl); /************************************************************************/ /* STATIC VARIABLES, LOCAL DEFINES */ /* GNL Variables */ static ST_CHAR *gnlNameBuf; static ST_CHAR *currGnlNamePos; /* Index to "u_no_write_allowed" function. It is set to correct value */ /* at startup by calling "mvlu_find_wr_ind_fun_index" (one time). */ static ST_RTINT idx_u_no_write_allowed = -2; /* start with illegal val*/ #define _OBJ_NAME_CLONE_NAME_SIZE (65) #define _OBJ_NAME_CLONE_SIZE (sizeof(OBJECT_NAME)+(2 * _OBJ_NAME_CLONE_NAME_SIZE)) /************************************************************************/ /* STATIC FUNCTIONS */ ST_RET mvlu_find_component ( ST_CHAR *flatname, /* flattened name (e.g. ST$Mod$stVal) */ ALT_ACCESS *alt_acc, RUNTIME_TYPE **rtIo, ST_INT *numRtIo, ST_INT *offset_io, ST_RTINT *prim_num_io); static ST_RET mvlu_find_struct_comp (ST_CHAR *compName, RUNTIME_TYPE **rtIo, ST_INT *numRtIo, ST_INT *offset_io, ST_RTINT *prim_num_io); static ST_VOID mvlu_handle_alt_acc (OBJECT_NAME *obj, ALT_ACCESS *alt_acc, MVL_ARR_CTRL *arrCtrl); static ST_RET getArrAARtType (MVL_ARR_CTRL *arrCtrl, RUNTIME_TYPE **pRt, ST_INT *pNumRt); static ST_VOID cloneArrAA (ALT_ACC_EL *arrAa, ALT_ACCESS *dest); static ST_VOID mvlu_find_base_va (MVL_VMD_CTRL *vmd_ctrl, OBJECT_NAME *obj, MVL_NET_INFO *net_info, MVL_VAR_ASSOC **vaOut); ST_RET getGnlVarNames (MVL_VAR_ASSOC *va, ST_CHAR *caPtr, ST_CHAR **dest, ST_INT maxNames, ST_INT *numNames, ST_BOOLEAN *moreFollowsOut); static ST_VOID mvlu_clone_objname (OBJECT_NAME *dest, OBJECT_NAME *src); static ST_VOID startArrRds (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl, MVLAS_RD_VA_CTRL *rdVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info); static ST_VOID startElReads (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl, MVLAS_RD_VA_CTRL *rdVaCtrl, RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info); static ST_VOID startArrWrs (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl, MVLAS_WR_VA_CTRL *wrVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info); static ST_VOID startElWrites (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl, MVLAS_WR_VA_CTRL *wrVaCtrl, RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info); static ST_INT countPrimEl (MVL_VAR_ASSOC *va, RUNTIME_TYPE *rt, ST_INT rt_num); static ST_RET chk_var_writable (RUNTIME_TYPE *rt, ST_INT rt_num); static ST_RET chk_write_resp_ready (MVL_IND_PEND *indCtrl); static ST_VOID set_write_resp_dae (MVLAS_WR_VA_CTRL *wrVaCtrl, ST_INT16 dae); /* DataAccessError code */ /************************************************************************/ /* Global function pointers. May be set by user. */ /************************************************************************/ /* This function pointer called BEFORE all write leaf functions. */ /* NOTE: called only for complex variables (struct or array). */ ST_RET (*u_mvl_wr_ind_var_start)(MVL_IND_PEND *indCtrl, MVLAS_WR_VA_CTRL *wrVaCtrl); /* current var in list of var */ /* This function pointer called AFTER all write leaf functions. */ /* NOTE: called only for complex variables (struct or array). */ ST_RET (*u_mvl_wr_ind_var_end)(MVL_IND_PEND *indCtrl, MVLAS_WR_VA_CTRL *wrVaCtrl); /* current var in list of var */ /************************************************************************/ /* init_prim_info */ /* Initialize PRIM_INFO struct using info saved in MVL_VAR_ASSOC. */ /************************************************************************/ ST_RET init_prim_info (MVL_VAR_ASSOC *va, PRIM_INFO *prim_info) { /* Just use values computed earlier & saved in va. */ prim_info->prim_num = va->prim_num; prim_info->prim_offset = 0; prim_info->prim_offset_base = va->offset_from_base; return (SD_SUCCESS); } /************************************************************************/ /************************************************************************/ /* MANUFACTURED VARIABLE RESOLUTION FUNCTIONS */ /************************************************************************/ /* u_mvl_get_va_aa */ /************************************************************************/ /* This function is called from MVL when it is unable to find a */ /* configured MMS server variable for a READ, WRITE, or GET VARIABLE */ /* ACCESS ATTRIBUTES indication. */ MVL_VAR_ASSOC *u_mvl_get_va_aa (MVL_VMD_CTRL *vmd_ctrl, ST_INT service, OBJECT_NAME *obj, MVL_NET_INFO *netInfo, ST_BOOLEAN alt_access_pres, ALT_ACCESS *alt_acc, ST_BOOLEAN *alt_access_done_out) { ST_CHAR *name; MVL_VAR_ASSOC *va; MVL_VAR_ASSOC *baseVa; ST_RET rc; OBJECT_NAME *objClone; /*ptr to OBJECT_NAME plus 2 name strings*/ /* Space for OBJECT_NAME struct plus 2 name strings. */ /* objClone will point to this, passed to mvlu_clone_objname. */ /* CRITICAL: Must be a struct so alignment is correct. */ struct { ST_CHAR bytearray[_OBJ_NAME_CLONE_SIZE]; } objNameBuf; RUNTIME_TYPE *baseRt; ST_INT numBaseRt; RUNTIME_TYPE *ucaRt; ST_INT numUcaRt; ST_CHAR *subStart; ST_INT subTypeId; MVL_ARR_CTRL arrCtrl; ST_CHAR *flatname; /* flattened variable name (for mvlu_find_component)*/ RUNTIME_TYPE *comp_rt; /* set by mvlu_find_component */ ST_INT comp_num_rt; /* set by mvlu_find_component */ ST_INT offset_from_base; /* set by mvlu_find_component */ ST_RTINT prim_num; /* set by mvlu_find_component */ /* Make a working copy of the variable name (mvlu_handle_alt_acc may change it) */ objClone = (OBJECT_NAME *) (&objNameBuf); mvlu_clone_objname (objClone, obj); /* We will handle any alternate access here, by creating the UCA from */ /* variable name. */ memset (&arrCtrl, 0, sizeof (MVL_ARR_CTRL)); /* start with clean "arrCtrl"*/ if (alt_access_pres) { mvlu_handle_alt_acc (objClone, alt_acc, &arrCtrl); *alt_access_done_out = SD_TRUE; } /* We need to find the base type for this variable */ mvlu_find_base_va (vmd_ctrl, objClone, netInfo, &baseVa); if (baseVa == NULL) { MVL_LOG_NERR1 ("Could not resolve UCA variable '%s'", objClone->obj_name.vmd_spec); return (NULL); } /* Use mvlu_find_component to compute offset_from_base & prim_num. */ /* Look for '$' in name. If found, point after it, else use NULL. */ flatname = strstr (obj->obj_name.vmd_spec, "$"); if (flatname != NULL) flatname++; /* point after the '$' */ /* Start with base variable RUNTIME_TYPE. */ rc = mvl_get_runtime (baseVa->type_id, &comp_rt, &comp_num_rt); if (rc != SD_SUCCESS) { MVL_LOG_NERR1 ("Could not get RT type for type id %d", baseVa->type_id); return (NULL); } offset_from_base = 0; prim_num = 0; /* NOTE: mvlu_find_component changes comp_rt, comp_num_rt, */ /* offset_from_base, prim_num. */ /* NOTE: offset_from_base, prim_num different for each array elem.*/ /* CRITICAL: mvlu_find_component expects alt_acc=NULL if Alt Access */ /* Spec NOT present. Make sure "alt_acc" is set correctly. */ if (!alt_access_pres) alt_acc = NULL; /* CRITICAL */ rc = mvlu_find_component (flatname, alt_acc, &comp_rt, &comp_num_rt, &offset_from_base, &prim_num); if (rc) return (NULL); /* if this fails, stop now. */ /* Now resolve this variable's type given it's name and the base */ /* type. */ rc = mvl_get_runtime (baseVa->type_id, &baseRt, &numBaseRt); if (rc != SD_SUCCESS) { MVL_LOG_NERR1 ("Could not get RT type for type id %d", baseVa->type_id); return (NULL); } ucaRt = baseRt; numUcaRt = numBaseRt; /* Check to see if this is for base var... name has no embedded '$' */ /* If is a derived variable we need to create from the variable name */ /* 'path' and runtime type. */ name = objClone->obj_name.vmd_spec; /* We know this is a union ... */ subStart = strstr (name,"$"); /* Skip the outer (base) name */ if (subStart) { /* This is a derived variable, and we have the base UCA variable */ /* Using the base runtime table and the variable name, we can get the */ /* subset type for this variable */ ++subStart; rc = mvlu_find_uca_var (&ucaRt, &numUcaRt, subStart); if (rc != SD_SUCCESS) { MVL_LOG_NERR1 ("Error - could not find subcomponent '%s'", subStart); return (NULL); } } /* If this is an alternate access on an array, we need to copy the */ /* RT type so we can modify the number of elements and total size */ if (arrCtrl.arrAltAccPres == SD_TRUE) { rc = getArrAARtType (&arrCtrl, &ucaRt, &numUcaRt); if (rc != SD_SUCCESS) { M_FREE (MSMEM_MVLU_AA, arrCtrl.alt_acc.aa); return (NULL); } } /* Make sure type computed matches what mvlu_find_component computed. */ /* DEBUG: delete these before release? */ assert (comp_num_rt == numUcaRt); assert (comp_rt->el_tag == ucaRt->el_tag); assert (comp_rt->mvluTypeInfo.prim_count == ucaRt->mvluTypeInfo.prim_count); /* OK, now we have the sub-runtime type, go ahead and create a temp */ /* RT type and variable association. */ rc = mvlu_add_rt_type (ucaRt, numUcaRt, &subTypeId); if (rc != SD_SUCCESS) { MVL_LOG_NERR0 ("Error - could not add temp RT type"); return (NULL); } /* Create "derived" variable from baseVa. */ /* NOTE: This var is NOT added to list so its name is NOT sent */ /* in GetNameList responses. */ va = mvl_var_create_derived (objClone->obj_name.vmd_spec, subTypeId, baseVa, offset_from_base, prim_num); /* CRITICAL: If use_static_data==SD_FALSE, mvl_var_create_derived could */ /* not set "va->data". Must call this function to allocate it. */ /* NOTE: Since this code executes every time a variable is accessed, */ /* it requires MANY more allocations when use_static_data==SD_FALSE. */ if (baseVa->use_static_data==SD_FALSE) mvluDefGetVaDataBufFun (service, va, ucaRt->offset_to_last); #if defined(MVLU_USE_REF) va->ref = ucaRt->mvluTypeInfo.ref; #endif /* Copy the array AA information */ memcpy (&va->arrCtrl, &arrCtrl, sizeof (MVL_ARR_CTRL)); va->arrCtrl.curr_index = arrCtrl.low_index; /* Good work, we are done here. */ return (va); } /************************************************************************/ /* getArrAARtType */ /************************************************************************/ static ST_RET getArrAARtType (MVL_ARR_CTRL *arrCtrl, RUNTIME_TYPE **pRt, ST_INT *pNumRt) { ST_INT numRt; ST_INT i, j; RUNTIME_TYPE *rt; RUNTIME_TYPE *endRt; RUNTIME_TYPE *newRt; ALT_ACCESS *alt_acc; ALT_ACC_EL *alt_acc_el; /* ptr to current entry in alt acc array */ ST_INT nest_level = 0; rt = *pRt; numRt = *pNumRt; /* Check to see if further nesting with this AA selection ... */ if (arrCtrl->nested == SD_FALSE) { /* If just one element, need to lose the outer array */ if (arrCtrl->num_elmnts == 1) { numRt -= 2; ++rt; } } else /* Further nesting is requested, let's do it! */ { /* We only support a single drill down AA */ if (arrCtrl->num_elmnts > 1) { MVL_LOG_NERR0 ("AA resolution problem - multiple elements and nested"); return (SD_FAILURE); } numRt -= 2; ++rt; /* OK, we now need to find the type of the component ... */ alt_acc = &arrCtrl->alt_acc; /* Only support AA_COMP and AA_COMP_NEST for now. */ /* If AA_COMP_NEST is used, this is recursive process. */ for (j=0; j < alt_acc->num_aa; j++) { alt_acc_el = &alt_acc->aa[j]; if (alt_acc_el->sel_type == AA_COMP || alt_acc_el->sel_type == AA_COMP_NEST) { if (alt_acc_el->sel_type == AA_COMP_NEST) nest_level++; /* find this component name within the current RUNTIME_TYPE array*/ for (i = 0; i < numRt; ++i, ++rt) { if (!strcmp (alt_acc_el->u.component, ms_comp_name_find (rt))) break; } if (i >= numRt) { MVL_LOG_NERR1 ("AA resolution problem - could not find component '%s'", alt_acc_el->u.component); return (SD_FAILURE); } if (rt->el_tag == RT_STR_START) numRt = rt->u.str.num_rt_blks+2; else if (rt->el_tag == RT_ARR_START) numRt = rt->u.arr.num_rt_blks+2; else numRt = 1; } else if (alt_acc_el->sel_type == AA_END_NEST) nest_level--; else { MVL_LOG_NERR0 ("AA resolution problem - complex nested AA on array"); return (SD_FAILURE); } } /* end "for" loop */ if (nest_level != 0) { MVL_LOG_NERR0 ("AA resolution problem - invalid nesting"); return (SD_FAILURE); } } /* OK, now copy the runtime type elements into a new one so we can fool */ /* with it. */ newRt = (RUNTIME_TYPE *) M_MALLOC (MSMEM_DYN_RT, numRt * sizeof (RUNTIME_TYPE)); memcpy (newRt, rt, numRt * sizeof (RUNTIME_TYPE)); /* Adjust the total size of the type, using scaling */ if (arrCtrl->num_elmnts > 1) { newRt->offset_to_last = rt[1].offset_to_last * arrCtrl->num_elmnts; /* Now set the number of elements as desired */ newRt->u.arr.num_elmnts = arrCtrl->num_elmnts; endRt = newRt + (newRt->u.arr.num_rt_blks + 1); endRt->u.arr.num_elmnts = arrCtrl->num_elmnts; /* We don't want end of array padding */ endRt->el_size = 0; } *pRt = newRt; *pNumRt = numRt; return (SD_SUCCESS); } /************************************************************************/ /* mvlu_handle_alt_acc */ /************************************************************************/ /* Here we deal with an alternate access. We will simply add the */ /* sub-name components to the base name to create a fully qualified UCA */ /* sub-var name */ /* Of course, this only handles the most simple form of alternate */ /* access correctly ... */ static ST_VOID mvlu_handle_alt_acc (OBJECT_NAME *obj, ALT_ACCESS *alt_acc, MVL_ARR_CTRL *arrCtrl) { ST_INT i; ST_CHAR *name; ST_BOOLEAN done; name = obj->obj_name.vmd_spec; /* We know this is a union ... */ arrCtrl->nested = SD_FALSE; done = SD_FALSE; for (i = 0; i < alt_acc->num_aa && !done; ++i) { switch (alt_acc->aa[i].sel_type) { case AA_COMP : case AA_COMP_NEST : strcat (name, "$"); strcat (name, alt_acc->aa[i].u.component); break; case AA_INDEX_NEST : arrCtrl->nested = SD_TRUE; cloneArrAA (&alt_acc->aa[i], &arrCtrl->alt_acc); /* Lets fall through into common code ... */ case AA_INDEX : arrCtrl->arrAltAccPres = SD_TRUE; arrCtrl->low_index = (ST_RTINT) alt_acc->aa[i].u.index; arrCtrl->num_elmnts = 1; done = SD_TRUE; break; case AA_INDEX_RANGE_NEST : arrCtrl->nested = SD_TRUE; cloneArrAA (&alt_acc->aa[i], &arrCtrl->alt_acc); /* Lets fall through into common code ... */ case AA_INDEX_RANGE : arrCtrl->arrAltAccPres = SD_TRUE; arrCtrl->low_index = (ST_RTINT) alt_acc->aa[i].u.ir.low_index; arrCtrl->num_elmnts = (ST_RTINT) alt_acc->aa[i].u.ir.num_elmnts; done = SD_TRUE; break; case AA_ALL: case AA_ALL_NEST : arrCtrl->nested = SD_TRUE; cloneArrAA (&alt_acc->aa[i], &arrCtrl->alt_acc); arrCtrl->arrAltAccPres = SD_TRUE; arrCtrl->low_index = 0; arrCtrl->num_elmnts = 0; /* 'all' flag */ done = SD_TRUE; break; case AA_END_NEST : done = SD_TRUE; break; default: MVL_LOG_NERR1 ("Error: Invalid alt access sel_type = %d", alt_acc->aa[i].sel_type); break; } } } /************************************************************************/ /* cloneArrAA */ /************************************************************************/ static ST_VOID cloneArrAA (ALT_ACC_EL *arrAa, ALT_ACCESS *dest) { ST_INT elCount; ST_INT nestLevel; ALT_ACC_EL *aaEl; /* First let's count how many we need */ nestLevel = 0; elCount = 0; aaEl = arrAa + 1; while (SD_TRUE) { if (nestLevel == 0 && aaEl->sel_type == AA_END_NEST) break; ++elCount; if (aaEl->sel_type == AA_COMP_NEST || aaEl->sel_type == AA_INDEX_NEST || aaEl->sel_type == AA_INDEX_RANGE_NEST || aaEl->sel_type == AA_ALL_NEST) { ++nestLevel; } if (aaEl->sel_type == AA_END_NEST) { --nestLevel; } ++aaEl; } /* OK, now just calloc and copy the nested alternate access */ if (elCount) { dest->num_aa = elCount; dest->aa = (ALT_ACC_EL *) M_MALLOC (MSMEM_MVLU_AA, elCount * sizeof (ALT_ACC_EL)); memcpy (dest->aa, arrAa+1, elCount * sizeof (ALT_ACC_EL)); } else { MVL_LOG_NERR0 ("Nested AA construction problem"); } } /************************************************************************/ /* mvlu_find_base_va */ /************************************************************************/ /* This function takes a MMS variable name and determines the type ID */ /* for the base type for the variable. This is done here by */ /* extracting the name root then looking for a configured variable */ /* of that name. */ static ST_VOID mvlu_find_base_va (MVL_VMD_CTRL *vmd_ctrl, OBJECT_NAME *obj, MVL_NET_INFO *net_info, MVL_VAR_ASSOC **vaOut) { MVL_VAR_ASSOC *va; ST_CHAR *p; ST_CHAR *name; OBJECT_NAME *objClone; /*ptr to OBJECT_NAME plus 2 name strings*/ /* Space for OBJECT_NAME struct plus 2 name strings. */ /* objClone will point to this, passed to mvlu_clone_objname. */ /* CRITICAL: Must be a struct so alignment is correct. */ struct { ST_CHAR bytearray[_OBJ_NAME_CLONE_SIZE]; } objNameBuf; objClone = (OBJECT_NAME *) (&objNameBuf); mvlu_clone_objname (objClone, obj); name = objClone->obj_name.vmd_spec; /* We know this is a union ... */ /* See if this variable name has embedded '$', and if so wack it so */ /* that we have the base name to work with. */ p = strstr (name,"$"); if (p) *p = 0; va = mvl_vmd_find_var (vmd_ctrl, objClone, net_info); if (va && (va->flags & MVL_VAR_FLAG_UCA) == 0) va = NULL; /* Found va but not UCA variable, so don't return it. */ *vaOut = va; } /************************************************************************/ /* mvlu_find_uca_var */ /************************************************************************/ /* This function searches a runtime type for the given UCA variable */ /* name. It does this by breaking the UCA name into its components */ /* and then finding the name in the current level of the runtime type. */ ST_RET mvlu_find_uca_var (RUNTIME_TYPE **rtIo, ST_INT *numRtIo, ST_CHAR *varName) { ST_CHAR nameBuf[MAX_IDENT_LEN+1]; ST_CHAR *nameToFind; ST_CHAR *compEnd,*arr; ST_BOOLEAN nameDone; ST_RET ret; ST_INT offset=0; /* mvlu_find_struct_comp needs it but not used */ ST_RTINT prim_num=0; /* mvlu_find_struct_comp needs it but not used */ /* Note that varName does not have the base name prefix */ strcpy (nameBuf, varName); nameToFind = nameBuf; nameDone = SD_FALSE; while (nameDone == SD_FALSE) { /* Isolate the component name for this level, removing subcomp names */ compEnd = strstr (nameToFind, "$"); if (compEnd != NULL) *compEnd = 0; else /* This is the last nest level */ nameDone = SD_TRUE; /*renxiaobao 数组*/ arr = strstr (nameToFind, "["); if (arr != NULL) *arr = 0; /* Find the component name in the current runtime type nest level */ ret = mvlu_find_struct_comp (nameToFind, rtIo, numRtIo, &offset, &prim_num); if (ret == SD_FAILURE) { /* Many things can cause this so just use FLOW Logging. */ MVLU_LOG_FLOW2 ("Could not find name component %s from name %s", nameToFind, varName); return (SD_FAILURE); } /* OK, we now have found the component in the runtime type, and our */ /* runtime pointer and numRt reflect the sub-runtime type. */ /* next component. */ /* Prepare to find the next level component name */ nameToFind = compEnd+1; } return (SD_SUCCESS); } /************************************************************************/ /* mvlu_find_struct_comp */ /************************************************************************/ /* This function searches a structure runtime type for the given */ /* component name at the outer level. */ /* NOTE: modifies (*rtIo), (*numRtIo), (*offset_io), (*prim_num_io). */ static ST_RET mvlu_find_struct_comp (ST_CHAR *compName, RUNTIME_TYPE **rtIo, ST_INT *numRtIo, ST_INT *offset_io, ST_RTINT *prim_num_io) { RUNTIME_TYPE *rt; RUNTIME_TYPE *endRt; ST_BOOLEAN foundRt; foundRt = SD_FALSE; rt = *rtIo; endRt = rt + *numRtIo; /* renxiaobao 数组*/ if (rt->el_tag == RT_ARR_START) { ++rt; } if (rt->el_tag != RT_STR_START) { MVL_LOG_NERR0 ("Find struct comp: First RT is not structure start"); return (SD_FAILURE); } ++rt; /* Skip the structure start */ while (rt < endRt) { if (!strcmp (compName, ms_comp_name_find (rt))) { *rtIo = rt; foundRt = SD_TRUE; } switch (rt->el_tag) { case RT_STR_START : if (foundRt == SD_TRUE) { *numRtIo = rt->u.str.num_rt_blks+2; return (SD_SUCCESS); } /* Not found yet. */ *offset_io += rt->offset_to_last; /* adjust offset */ *prim_num_io += rt->mvluTypeInfo.prim_count; /* adjust prim_num*/ rt += rt->u.str.num_rt_blks+2; /* Skip the structure contents */ break; case RT_ARR_START : if (foundRt == SD_TRUE) { *numRtIo = rt->u.arr.num_rt_blks+2; return (SD_SUCCESS); } /* Not found yet. */ *offset_io += rt->offset_to_last; /* adjust offset */ *prim_num_io += rt->mvluTypeInfo.prim_count; /* adjust prim_num*/ rt += rt->u.arr.num_rt_blks+1; /* Skip the array contents */ break; case RT_STR_END : case RT_ARR_END : ++rt; break; default: if (foundRt == SD_TRUE) { *numRtIo = 1; return (SD_SUCCESS); } /* Not found yet. */ *offset_io += rt->offset_to_last; /* adjust offset */ *prim_num_io += rt->mvluTypeInfo.prim_count; /* adjust prim_num*/ ++rt; break; } } return (SD_FAILURE); } /************************************************************************/ /* u_mvl_free_va */ /************************************************************************/ /* MVL calls this function when it is through with it. We will free */ /* the data buffer, and then the VA (unless it was a base VA). */ ST_VOID u_mvl_free_va (ST_INT service, MVL_VAR_ASSOC *va, MVL_NET_INFO *netInfo) { RUNTIME_TYPE *ucaRt; ST_INT numUcaRt; ST_RET rc; /* If "va->data" was allocated, free it now. */ if (va->base_va->use_static_data==SD_FALSE) mvluDefFreeVaDataBufFun(service, va); /* If this was not a 'base' VA, free the derived type */ if ( (va->flags & MVL_VAR_FLAG_UCA) == 0) { /* See if we allocated the runtime type ... */ if (va->arrCtrl.arrAltAccPres == SD_TRUE) { rc = mvl_get_runtime (va->type_id, &ucaRt, &numUcaRt); if (rc == SD_SUCCESS) M_FREE (MSMEM_DYN_RT, ucaRt); else { MVL_LOG_NERR0 ("Error: internal error"); } if (va->arrCtrl.nested == SD_TRUE) M_FREE (MSMEM_MVLU_AA, va->arrCtrl.alt_acc.aa); } mvlu_free_rt_type (va->type_id); mvl_var_destroy (va); } } /************************************************************************/ /************************************************************************/ /* MANUFACTURED VARIABLE_LIST RESOLUTION FUNCTIONS */ /************************************************************************/ /* u_mvl_get_nvl, u_mvl_free_nvl functions deleted (no longer needed). */ /* They were only needed because Foundry could not find the variables */ /* for the NVL to set "vl->entries". But new improved Foundry */ /* generates code like the following (only done once at startup, */ /* so it is much more efficient): */ /* * varObjName.obj_name.vmd_spec = "DI$Name"; * varObjName.object_tag = DOM_SPEC; * varObjName.domain_id = mvl_vmd.dom_tbl[6]->name; * vl->entries [0] = u_mvl_get_va_aa (MMSOP_INFO_RPT, &varObjName, NULL, SD_FALSE, NULL, NULL); */ /************************************************************************/ /************************************************************************/ /* GET NAMELIST HELPER FUNCTIONS */ /* These functions are necessary because MVL does not know about our */ /* manufactured variables and variable lists. We will fill in part of */ /* the namelist response data structure. */ /************************************************************************/ /************************************************************************/ /* u_gnl_ind_vars */ /************************************************************************/ ST_INT u_gnl_ind_vars (MVL_NET_INFO *net_info, NAMELIST_REQ_INFO *req_info, ST_CHAR **ptr, ST_BOOLEAN *moreFollowsOut, ST_INT maxNames) { ST_INT i; ST_INT numRespNames; ST_CHAR caBuf[100]; ST_CHAR *caPtr; ST_CHAR *p; ST_INT numVa; ST_INT v; MVL_VAR_ASSOC **va; MVL_AA_OBJ_CTRL *aa; MVL_DOM_CTRL *domCtrl; gnlNameBuf = (ST_CHAR *) M_CALLOC (MSMEM_MVLU_GNL, 1, maxNames * (MAX_IDENT_LEN +1)); /* Start by finding the VA that we should start with */ /* Get the variable associations in the selected scope */ numVa = 0; if (req_info->objscope == VMD_SPEC) { numVa = mvl_vmd.num_var_assoc; va = mvl_vmd.var_assoc_tbl; } else if (req_info->objscope == DOM_SPEC) { domCtrl = mvl_vmd_find_dom (&mvl_vmd, req_info->dname); if (domCtrl) { numVa = domCtrl->num_var_assoc; va = domCtrl->var_assoc_tbl; } else { MVL_LOG_NERR1 ("GetNameList variables: Domain '%s' not found", req_info->dname); *moreFollowsOut = SD_FALSE; return (-1); /* error. This triggers error response */ } } else /* AA_SPEC */ { aa = (MVL_AA_OBJ_CTRL *) net_info->aa_objs; if (aa) { numVa = aa->num_var_assoc; va = aa->var_assoc_tbl; } else { *moreFollowsOut = SD_FALSE; return (-1); /* error. This triggers error response */ } } /* Take care of the 'continue after' business as necessary. Note that */ /* we will only look at the base name. */ i = 0; if (req_info->cont_after_pres) { caPtr = req_info->continue_after; strcpy (caBuf, req_info->continue_after); p = strstr (caBuf,"$"); if (p) *p = 0; while (i < numVa) { p = va[i]->name; v = strcmp (caBuf, p); if (v == 0) /* Exact match */ break; if (v < 0) /* CA is less than real VA name */ { if (i > 0) /* Start with the previous VA */ --i; break; } ++i; /* We have not found our place yet ... */ } } else caPtr = NULL; *moreFollowsOut = SD_FALSE; currGnlNamePos = gnlNameBuf; /* OK, va[i] is where we start putting together our names */ for (numRespNames = 0; numRespNames < maxNames && i < numVa; ++i) { ST_INT j; ST_INT numRespNamesStart = numRespNames; /* save count modified by fct*/ if (getGnlVarNames (va[i], caPtr, &ptr[numRespNames], maxNames, &numRespNames, moreFollowsOut) != SD_SUCCESS) return (-1); /* error response will be sent */ MVLU_LOG_DEBUG0 ("Names returned from getGnlVarNames"); for (j = numRespNamesStart; jflags & MVL_VAR_FLAG_UCA) == 0) { ++(*numNames); dest[0] = va->name; return (SD_SUCCESS); } memset (namePrefix, 0, sizeof(namePrefix)); strcpy (namePrefix[0], va->name); /* First we need to find the starting 'sortedNum', based on the CA name */ nameCount = *numNames; startSortedNum = 1; if (caPtr != NULL) { baseIndexOffset = 0; /* CA name could be base name, or derived name. */ if (strcmp (caPtr, va->name) == 0) { /* CA name equals base name. Start with next name AFTER base. */ startSortedNum = 1; } else if ((subStart = strstr (caPtr,"$")) == NULL) /* Skip the base name*/ { MVL_LOG_ERR1 ("Problem finding sub-type in '%s'", caPtr); return (SD_FAILURE); } else { rc = mvl_get_runtime (va->type_id, &ucaRt, &numUcaRt); ++subStart; rc = mvlu_find_uca_var (&ucaRt, &numUcaRt, subStart); if (rc == SD_SUCCESS) startSortedNum = ucaRt->mvluTypeInfo.sortedNum + 1; } } else /* No continue after, we need to include the base name */ { dest[0] = va->name; ++nameCount; baseIndexOffset = 1; --maxNames; } maxRetNames = maxNames - *numNames; maxSortedNum = startSortedNum + maxRetNames - 1; /* OK, now we start doing the real thing. Derive names for this */ /* type, put them into the dest array. */ /* We will save those elements with 'sortedNum' between */ /* 'startSortedNum' and 'maxSortedNum' */ compress = SD_FALSE; rc = mvl_get_runtime (va->type_id, &ucaRt, &numUcaRt); /* This code assumes all UCA vars are structs, so first tag must be RT_STR_START*/ /* DEBUG: change this to "assert"? */ if (ucaRt->el_tag != RT_STR_START) MVL_LOG_ERR1 ("IEC/UCA type (type_id=%d) is not a struct. Cannot derive variable names for GetNameList response.", va->type_id); nestLevel = 0; for (i = 0; i < numUcaRt; ++i, ++ucaRt) { sortedNum = ucaRt->mvluTypeInfo.sortedNum; comp_name = ms_comp_name_find (ucaRt); if (strlen (comp_name) && sortedNum >= startSortedNum && sortedNum <= maxSortedNum) { /* Chk len is legal BEFORE writing (need room for 2 strings + '$'.*/ if (strlen (namePrefix[nestLevel]) + strlen (comp_name) + 1 <= MAX_IDENT_LEN) { sprintf (nameBuf, "%s$%s", namePrefix[nestLevel], comp_name); strLen = strlen (nameBuf); assert (strLen <= MAX_IDENT_LEN); /* if this fails, len chk in "if" is wrong*/ dest[sortedNum-startSortedNum+baseIndexOffset] = currGnlNamePos; strcpy (currGnlNamePos, nameBuf); currGnlNamePos += strLen +1; ++nameCount; } else { /* NOTE: should never happen if len checked in _mvlu_save_rt_uca_name at startup.*/ MVL_LOG_ERR2 ("GetNameList error: Derived variable name '%s$%s' too long", namePrefix[nestLevel], comp_name); compress = SD_TRUE; } } if (ucaRt->el_tag == RT_STR_START) { comp_name = ms_comp_name_find (ucaRt); if (strlen (comp_name)) { /* Chk len is legal BEFORE writing (need room for 2 strings + '$').*/ if (strlen (namePrefix[nestLevel]) + strlen (comp_name) + 1 <= MAX_IDENT_LEN) { ++nestLevel; assert (nestLevel < MAX_NEST_LEVEL); assert (nestLevel > 0); strcpy (namePrefix[nestLevel], namePrefix[nestLevel-1]); strcat (namePrefix[nestLevel], "$"); strcat (namePrefix[nestLevel], comp_name); } else { /* NOTE: should never happen if len checked in _mvlu_save_rt_uca_name at startup.*/ MVL_LOG_ERR2 ("GetNameList error: Flattened variable name too long for struct '%s$%s'", namePrefix[nestLevel], comp_name); MVL_LOG_CERR0 ("Flattened name of struct and all lower components cannot be returned"); compress=SD_TRUE; /* Skip to end of struct */ i += (ucaRt->u.str.num_rt_blks + 1); ucaRt += (ucaRt->u.str.num_rt_blks + 1); } } } else if (ucaRt->el_tag == RT_STR_END) --nestLevel; else if (ucaRt->el_tag == RT_ARR_START) { /* Skip the array elements */ /* There is no way to create UCA names for objects inside an */ /* array, so skip over the array. */ i += (ucaRt->u.arr.num_rt_blks + 1); ucaRt += (ucaRt->u.arr.num_rt_blks + 1); } if (sortedNum > maxSortedNum) *moreFollowsOut = SD_TRUE; } /* DEBUG: at this point loop, 'nestLevel' should equal (-1). This may */ /* sound strange, but nestLevel NOT incremented on first RT_STR_START,*/ /* so last RT_STR_END makes it -1. Add assert here? */ /* If we had to skip one or more names, we need to eliminate the */ /* holes in the name pointer table. */ if (compress == SD_TRUE) { get = 0; put = 0; numNewNames = nameCount - *numNames; while (put < numNewNames) { if (dest[get] != NULL) dest[put++] = dest[get]; ++get; } } *numNames = nameCount; return (SD_SUCCESS); } /************************************************************************/ /* u_gnl_done */ /************************************************************************/ /* This function is called after MVL has send the MMS response to a */ /* GET_NAME_LIST indication. We will clean up memory resources. */ ST_VOID u_gnl_done (ST_INT16 mms_class, NAMELIST_RESP_INFO *resp_info) { if (mms_class == MMS_CLASS_VAR) M_FREE (MSMEM_MVLU_GNL, gnlNameBuf); } /************************************************************************/ /************************************************************************/ /* u_gnl_ind_nvls */ /************************************************************************/ ST_INT u_gnl_ind_nvls (MVL_NET_INFO *net_info, NAMELIST_REQ_INFO *req_info, ST_CHAR **ptr, ST_BOOLEAN *moreFollowsOut, ST_INT maxNames) { ST_INT v; ST_INT i; ST_INT num_obj; MVL_AA_OBJ_CTRL *aa; MVL_NVLIST_CTRL **vl; MVL_DOM_CTRL *domCtrl; ST_INT startIndex; ST_INT numObjLeft; ST_INT numRetNames; ST_CHAR *name; /* First get the number of objects and pointer to the object table */ num_obj = 0; if (req_info->objscope == VMD_SPEC) { num_obj = mvl_vmd.num_nvlist; vl = mvl_vmd.nvlist_tbl; } else if (req_info->objscope == DOM_SPEC) { domCtrl = mvl_vmd_find_dom (&mvl_vmd, req_info->dname); if (domCtrl) { num_obj = domCtrl->num_nvlist; vl = domCtrl->nvlist_tbl; } else { MVL_LOG_NERR1 ("GetNameList NVL: Domain '%s' not found", req_info->dname); *moreFollowsOut = SD_FALSE; return (-1); /* error. This triggers error response */ } } else /* AA_SPEC */ { aa = (MVL_AA_OBJ_CTRL *) net_info->aa_objs; if (aa) { num_obj = aa->num_nvlist; vl = aa->nvlist_tbl; } else { *moreFollowsOut = SD_FALSE; return (-1); /* error. This triggers error response */ } } if (num_obj) { /* Take care of the 'continue after' business if necessary */ startIndex = 0; if (req_info->cont_after_pres) { while (startIndex < num_obj) { name = vl[startIndex]->name; v = strcmp (req_info->continue_after, name); if (v == 0) { ++startIndex; /* Index to the next one */ break; } if (v < 0) break; ++startIndex; /* We have not found our place yet ... */ } } numObjLeft = num_obj-startIndex; numRetNames = min(numObjLeft, maxNames); if (numRetNames < numObjLeft) *moreFollowsOut = SD_TRUE; /* Now make the list for the response */ for (i = 0; i < numRetNames; ++i, ++ptr) *ptr = vl[startIndex+i]->name; return (numRetNames); } return (0); } /************************************************************************/ /************************************************************************/ /* mvlu_clone_objname */ /************************************************************************/ /* This function 'clones' a MMS OBJECT_NAME structure, which means that */ /* it must allocate the various storage elements as required. Note */ /* that the name storage allocation is 65, to give calling routines */ /* room to work. */ static ST_VOID mvlu_clone_objname (OBJECT_NAME *dest, OBJECT_NAME *src) { memset (dest, 0, _OBJ_NAME_CLONE_SIZE); dest->object_tag = src->object_tag; dest->obj_name.vmd_spec = (ST_CHAR *) (dest + 1); strcpy (dest->obj_name.vmd_spec, src->obj_name.vmd_spec); if (src->object_tag == DOM_SPEC) { dest->domain_id = dest->obj_name.vmd_spec + _OBJ_NAME_CLONE_NAME_SIZE; strcpy (dest->domain_id, src->domain_id); } } /************************************************************************/ /************************************************************************/ /* MVLU READ/WRITE HANDLERS */ /************************************************************************/ static ST_VOID mvluDefAsyncWrIndFun (struct mvlu_wr_va_ctrl *mvluWrVaCtrl); static ST_VOID mvluDefAsyncRdIndFun (struct mvlu_rd_va_ctrl *mvluRdVaCtrl); /* Function pointers for non-UCA variable handling */ ST_VOID(*mvluAsyncRdIndFun)(struct mvlu_rd_va_ctrl *mvluRdVaCtrl) = mvluDefAsyncRdIndFun; ST_VOID(*mvluAsyncWrIndFun)(struct mvlu_wr_va_ctrl *mvluWrVaCtrl) = mvluDefAsyncWrIndFun; /************************************************************************/ /************************************************************************/ /************************************************************************/ /* mvl_read_ind */ /************************************************************************/ ST_VOID u_mvl_read_ind (MVL_IND_PEND *indCtrl) { MVLAS_READ_CTRL *rdCtrl; MVLAS_RD_VA_CTRL *rdVaCtrl; MVL_VAR_ASSOC *va; MVLU_RD_VA_CTRL *mvluRdVaCtrl; ST_INT i; ST_INT numVar; RUNTIME_TYPE *rt; ST_INT numRt; PRIM_INFO prim_info; rdCtrl = &indCtrl->u.rd; numVar = rdCtrl->numVar; /* First we will go through each variable being read and count the */ /* primitive elelemts. */ rdVaCtrl = rdCtrl->vaCtrlTbl; for (i = 0; i < numVar; ++i, ++rdVaCtrl) { va = rdVaCtrl->va; if (va) /* VA was resolved, we can deal with it */ { mvl_get_runtime (va->type_id, &rt, &numRt); rdVaCtrl->acc_rslt_tag = ACC_RSLT_SUCCESS; rdVaCtrl->numPrimDataDone = 0; if (va->base_va != NULL) /* UCA variable handling ... */ { rdVaCtrl->numPrimData = countPrimEl (va, rt, numRt); } else rdVaCtrl->numPrimData = 1; } else rdVaCtrl->numPrimData = 1; } /* Now we will go through each var being read and invoke the rdInd */ /* function for it. */ rdVaCtrl = rdCtrl->vaCtrlTbl; for (i = 0; i < numVar; ++i, ++rdVaCtrl) { va = rdVaCtrl->va; if (va) /* VA was resolved, we can deal with it */ { /* The VA's data pointer is valid, as is the type ID. */ /* We want to call the handlers for all primitive level functions */ /* for this data type */ if (va->base_va != NULL) /* UCA variable handling ... */ { mvl_get_runtime (va->type_id, &rt, &numRt); /* Initialize prim_info struct. */ if (init_prim_info (va, &prim_info)) { /* Failed. Can't process this var. Skip to next var in list*/ MVL_LOG_ERR1 ("init_prim_info failed for 'Read' of variable '%s'", va->name); rdVaCtrl->acc_rslt_tag = ACC_RSLT_FAILURE; continue; /* skip to next variable */ } startElReads (indCtrl, rdCtrl, rdVaCtrl, rt, numRt, &prim_info); } else /* Non-UCA variable handling ... */ { mvluRdVaCtrl = (MVLU_RD_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1, sizeof (MVLU_RD_VA_CTRL)); mvluRdVaCtrl->primData = (ST_CHAR *) va->data; mvluRdVaCtrl->indCtrl = indCtrl; mvluRdVaCtrl->rdVaCtrl = rdVaCtrl; (*mvluAsyncRdIndFun)(mvluRdVaCtrl); } } else /* VA not found, let it be done */ { mvluRdVaCtrl = (MVLU_RD_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1, sizeof (MVLU_RD_VA_CTRL)); mvluRdVaCtrl->indCtrl = indCtrl; mvluRdVaCtrl->rdVaCtrl = rdVaCtrl; mvlu_rd_prim_done (mvluRdVaCtrl, SD_SUCCESS); } } } /************************************************************************/ /* mvlu_trim_branch_name */ /* Find last '$' in name and replace it with NULL. */ /************************************************************************/ ST_VOID mvlu_trim_branch_name (ST_CHAR *branch_name) { ST_CHAR *ptr; if ((ptr = strrchr (branch_name, '$')) != NULL) /* find last '$'*/ *ptr = 0; /* replace '$' with NULL */ return; } /************************************************************************/ /* startElReads */ /************************************************************************/ static ST_VOID startElReads (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl, MVLAS_RD_VA_CTRL *rdVaCtrl, RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info) { ST_INT i; MVLU_RD_VA_CTRL *mvluRdVaCtrl; ST_RTINT rdIndFunIndex; ST_UCHAR el_tag; ST_INT el_size; ST_RTINT num_rt_blks; #if defined(MVL_XNAME) ST_CHAR element_name[MAX_IDENT_LEN+1]; ST_CHAR branch_name[MAX_IDENT_LEN+1]; MVL_VAR_ASSOC *va; ST_CHAR *comp_name; #endif #if defined(MVL_XNAME) va = rdVaCtrl->va; element_name[0]=0; strcpy (branch_name,va->name); /* if this is a substructure we need to trim back one level of the name */ /* because the first rt element will be the name of the component */ /* already part of the va->name */ if ((rt_num > 1) && (strstr (branch_name,"$"))) { mvlu_trim_branch_name (branch_name); } #endif for (i = 0; i < rt_num; ++i, ++rt) { #if defined(MVL_XNAME) if (rt_num > 1) { comp_name = ms_comp_name_find (rt); if (strlen (comp_name)) { if ((rt->el_tag == RT_STR_START)) { strcat (branch_name, "$"); strcat (branch_name, comp_name); strcpy (element_name, branch_name); } else { element_name[0]=0; strcat (element_name, branch_name); strcat (element_name, "$"); strcat (element_name, comp_name); } } if (rt->el_tag == RT_STR_END) { strcpy (element_name, branch_name); mvlu_trim_branch_name (branch_name); } } else { /* only one element in the RT table assume it's a primitive */ strcpy (element_name, branch_name); } #endif el_tag = rt->el_tag; num_rt_blks = rt->u.arr.num_rt_blks; el_size = rt->el_size; if (ms_is_rt_prim (rt) == SD_TRUE) { rdIndFunIndex = rt->mvluTypeInfo.rdIndFunIndex; if (u_mvlu_leaf_rd_ind_fun != NULL || //(rdIndFunIndex >= 0 && rdIndFunIndex < mvluNumRdFunEntries))//lnk编译修改,原前置程序未使用 0)//lnk编译修改,原前置程序未使用 { mvluRdVaCtrl = (MVLU_RD_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1, sizeof (MVLU_RD_VA_CTRL)); mvluRdVaCtrl->rt = rt; #if defined(MVL_XNAME) strcpy (mvluRdVaCtrl->xName, element_name); #endif mvluRdVaCtrl->primData = (ST_CHAR *) rdVaCtrl->va->data + prim_info->prim_offset; mvluRdVaCtrl->indCtrl = indCtrl; mvluRdVaCtrl->rdVaCtrl = rdVaCtrl; #if defined(MVLU_USE_REF) mvluRdVaCtrl->primRef = rt->mvluTypeInfo.ref; #endif mvluRdVaCtrl->prim_num = prim_info->prim_num; mvluRdVaCtrl->prim_offset_base = prim_info->prim_offset_base; if (u_mvlu_leaf_rd_ind_fun == NULL) {}//(*mvluRdFunInfoTbl[rdIndFunIndex].fun_ptr)(mvluRdVaCtrl);//lnk编译删除,原前置程序未使用 else (*u_mvlu_leaf_rd_ind_fun)(mvluRdVaCtrl); } else rdVaCtrl->acc_rslt_tag = ACC_RSLT_FAILURE; prim_info->prim_num++; } if (el_tag == RT_ARR_START) { startArrRds (indCtrl, rdCtrl, rdVaCtrl, rt, prim_info); i += (num_rt_blks + 1); rt += (num_rt_blks + 1); } else { prim_info->prim_offset += el_size; prim_info->prim_offset_base += el_size; } } } /************************************************************************/ /* startArrRds */ /************************************************************************/ static ST_VOID startArrRds (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl, MVLAS_RD_VA_CTRL *rdVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info) { ST_RTINT i; MVL_VAR_ASSOC *va; ST_INT numRt; /* DEBUG: To allow bigger arrays, must chg type of these two vars */ /* & va->arrCtrl.low_index & va->arrCtrl.curr_index. */ ST_RTINT low_index; ST_RTINT num_elmnts; numRt = rt->u.arr.num_rt_blks+2; va = rdVaCtrl->va; if (va->arrCtrl.arrAltAccPres == SD_TRUE) { low_index = va->arrCtrl.low_index; num_elmnts = va->arrCtrl.num_elmnts; } else { low_index = 0; num_elmnts = (ST_RTINT) rt->u.arr.num_elmnts; } /* Let's check to see if the client is selecting a sub-object ... */ prim_info->prim_offset += rt->el_size; prim_info->prim_offset_base += rt->el_size; for (i = 0; i < num_elmnts; ++i) { va->arrCtrl.curr_index = low_index + i; startElReads (indCtrl, rdCtrl, rdVaCtrl, rt+1, numRt-2, prim_info); } prim_info->prim_offset += (rt+numRt-1)->el_size; prim_info->prim_offset_base += (rt+numRt-1)->el_size; } /************************************************************************/ /* mvlu_rd_prim_done */ /************************************************************************/ /* The user calls this function acynchronously when the primitive data */ /* has been put in the 'primData' buffer. */ ST_VOID mvlu_rd_prim_done (MVLU_RD_VA_CTRL *mvluRdVaCtrl, ST_RET rc) { MVL_IND_PEND *indCtrl; MVLAS_READ_CTRL *rdCtrl; MVLAS_RD_VA_CTRL *rdVaCtrl; ST_INT i; rdVaCtrl = mvluRdVaCtrl->rdVaCtrl; if (rc != SD_SUCCESS) { /* DEBUG LIZ: added logging why read failed */ MVL_LOG_NERR2 ("Read failed for UCA variable '%s' rc=%d", rdVaCtrl->va->name, rc); rdVaCtrl->acc_rslt_tag = ACC_RSLT_FAILURE; } ++rdVaCtrl->numPrimDataDone; if (rdVaCtrl->numPrimDataDone == rdVaCtrl->numPrimData) { /* All primitives for "one" variable are complete. */ indCtrl = mvluRdVaCtrl->indCtrl; if (indCtrl->scan_va_done_fun) (*indCtrl->scan_va_done_fun)(indCtrl, rdVaCtrl->va); /* If all primitives for all variables are complete, respond now */ rdCtrl = &indCtrl->u.rd; rdVaCtrl = rdCtrl->vaCtrlTbl; for (i = 0; i < rdCtrl->numVar; ++i, ++rdVaCtrl) { if (rdVaCtrl->numPrimDataDone != rdVaCtrl->numPrimData) break; } if (i == rdCtrl->numVar) { if (indCtrl->op != MMSOP_READ) { /* Not normal read. Do special processing. */ if (indCtrl->usr_resp_fun) { /* If user set custom resp fun, call it. */ (*indCtrl->usr_resp_fun) (indCtrl); } } else mvlas_read_resp (indCtrl); } } M_FREE (MSMEM_MVLU_VA_CTRL, mvluRdVaCtrl); } /************************************************************************/ /* mvluDefAsyncRdIndFun */ /************************************************************************/ static ST_VOID mvluDefAsyncRdIndFun (struct mvlu_rd_va_ctrl *mvluRdVaCtrl) { mvlu_rd_prim_done (mvluRdVaCtrl, SD_SUCCESS); } /************************************************************************/ /************************************************************************/ /* u_mvl_write_ind */ /* NOTE: "indCtrl" is freed when "mvlas_write_resp" is called to send */ /* response. To assure "indCtrl" is valid until the end of this function,*/ /* the "ind_funct_done" flag is used to indicate if this function */ /* is done. It is accessible in all lower level functions. */ /* At end of this function, if response ready, "mvlas_write_resp" is */ /* called & indCtrl is freed (all done). If response NOT ready, this */ /* flag is set to (SD_TRUE), then "mvlu_wr_prim_done" will call */ /* "mvlas_write_resp" later when response is ready. */ /************************************************************************/ ST_VOID u_mvl_write_ind (MVL_IND_PEND *indCtrl) { MVLAS_WRITE_CTRL *wrCtrl; MVLAS_WR_VA_CTRL *wrVaCtrl; MVL_VAR_ASSOC *va; MVLU_WR_VA_CTRL *mvluWrVaCtrl; ST_INT i; ST_INT numVar; RUNTIME_TYPE *rt; ST_INT numRt; PRIM_INFO prim_info; /* NOTE: (indCtrl->u.wr.ind_funct_done==SD_FALSE) because indCtrl calloced.*/ /* Changed to SD_TRUE only when this function completes. */ /* Checked in "mvlu_wr_prim_done" funct. */ wrCtrl = &indCtrl->u.wr; numVar = wrCtrl->numVar; /* First we will go through each variable being written and count the */ /* primitive elelemts. */ wrVaCtrl = wrCtrl->vaCtrlTbl; for (i = 0; i < numVar; ++i, ++wrVaCtrl) { va = wrVaCtrl->va; if (va) /* VA was resolved, we can look it over */ { mvl_get_runtime (va->type_id, &rt, &numRt); wrVaCtrl->resp_tag = WR_RSLT_SUCCESS; wrVaCtrl->numPrimDataDone = 0; if (va->base_va != NULL) /* UCA variable handling ... */ wrVaCtrl->numPrimData = countPrimEl (va, rt, numRt); else wrVaCtrl->numPrimData = 1; } else wrVaCtrl->numPrimData = 1; } /* Now we will go through each var being written and invoke the wrInd */ /* function for it. */ wrVaCtrl = wrCtrl->vaCtrlTbl; for (i = 0; i < numVar; ++i, ++wrVaCtrl) { va = wrVaCtrl->va; if (va) /* VA was resolved, we can look it over */ { /* The VA's data pointer is valid, as is the type ID. */ /* We want to call the handlers for all primitive level functions */ /* for this data type */ if (va->base_va != NULL) /* UCA variable handling ... */ { mvl_get_runtime (va->type_id, &rt, &numRt); if (chk_var_writable (rt, numRt)) { /* This var NOT writable. Set error & skip to next var. */ set_write_resp_dae (wrVaCtrl, ARE_OBJ_ACCESS_DENIED); continue; /* skip to next variable */ } /* Initialize prim_info struct. */ if (init_prim_info (va, &prim_info)) { /* Failed. Can't process this var. Skip to next var in list*/ MVL_LOG_ERR1 ("init_prim_info failed for 'Write' of variable '%s'", va->name); set_write_resp_dae (wrVaCtrl, ARE_OBJ_ACCESS_DENIED); continue; /* skip to next variable */ } /* If this is struct (or array), user function pointer !=NULL, */ /* and function returns error, do not allow write. */ if (numRt > 1 /* must be struct or array */ && u_mvl_wr_ind_var_start != NULL && (*u_mvl_wr_ind_var_start)(indCtrl, wrVaCtrl) != SD_SUCCESS) { /* This var NOT writable. Set error & skip to next var. */ set_write_resp_dae (wrVaCtrl, ARE_OBJ_ACCESS_DENIED); continue; /* skip to next variable */ } startElWrites (indCtrl, wrCtrl,wrVaCtrl, rt, numRt, &prim_info); /* If this is struct (or array), user function pointer !=NULL, */ /* and function returns error, do not allow write. */ /* CAUTION: If error is detected here, must make sure that leaf */ /* functions did NOT write any data. */ if (numRt > 1 /* must be struct or array */ && u_mvl_wr_ind_var_end != NULL && (*u_mvl_wr_ind_var_end)(indCtrl, wrVaCtrl) != SD_SUCCESS) { /* This var NOT writable. Set error & skip to next var. */ set_write_resp_dae (wrVaCtrl, ARE_OBJ_ACCESS_DENIED); continue; /* skip to next variable */ } } else /* Non-UCA variable handling ... */ { mvluWrVaCtrl = (MVLU_WR_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1, sizeof (MVLU_WR_VA_CTRL)); mvluWrVaCtrl->primData = (ST_CHAR *) va->data; mvluWrVaCtrl->indCtrl = indCtrl; mvluWrVaCtrl->wrVaCtrl = wrVaCtrl; (*mvluAsyncWrIndFun)(mvluWrVaCtrl); } } else { mvluWrVaCtrl = (MVLU_WR_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1, sizeof (MVLU_WR_VA_CTRL)); mvluWrVaCtrl->indCtrl = indCtrl; mvluWrVaCtrl->wrVaCtrl = wrVaCtrl; mvlu_wr_prim_done (mvluWrVaCtrl, SD_SUCCESS); } } /* If response is ready (i.e. all synchronous leafs), send it now. */ /* If not, just set flag (SD_TRUE) & mvlu_wr_prim_done will send resp.*/ if (chk_write_resp_ready (indCtrl) == SD_SUCCESS) mvlas_write_resp (indCtrl); /* sends resp & frees indCtrl */ else indCtrl->u.wr.ind_funct_done = SD_TRUE; /* this funct is done */ } /************************************************************************/ /* startElWrites */ /* NOTE: Resp NOT sent while in this function (see ind_funct_done flag).*/ /* Therefore, indCtrl is valid throughout this function */ /* (i.e. it is NOT freed). */ /************************************************************************/ static ST_VOID startElWrites (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl, MVLAS_WR_VA_CTRL *wrVaCtrl, RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info) { ST_INT i; MVLU_WR_VA_CTRL *mvluWrVaCtrl; ST_RTINT wrIndFunIndex; ST_UCHAR el_tag; ST_INT el_size; ST_RTINT num_rt_blks; #if defined(MVL_XNAME) ST_CHAR element_name[MAX_IDENT_LEN+1]; ST_CHAR branch_name[MAX_IDENT_LEN+1]; MVL_VAR_ASSOC *va; ST_CHAR *comp_name; #endif ST_CHAR sboName[MVL61850_MAX_OBJREF_LEN+1]; RUNTIME_TYPE *rt_first; /* to save ptr to first before incrementing */ #if !defined(MVL61850_CTL_DISABLE) ST_INT oper_nest_level = 0; #endif #if defined(MVL_XNAME) va = wrVaCtrl->va; element_name[0]=0; strcpy (branch_name,va->name); /* if this is a substructure we need to trim back one level of the name */ /* because the first rt element will be the name of the component */ /* already part of the va->name */ if ((rt_num > 1) && (strstr (branch_name, "$"))) { mvlu_trim_branch_name (branch_name); } #endif rt_first = rt; /* save first for comparison */ for (i = 0; i < rt_num; ++i, ++rt) { #if defined(MVL_XNAME) if (rt_num > 1) /* is this a collection of elements? */ { comp_name = ms_comp_name_find (rt); if (strlen (comp_name)) { if ((rt->el_tag == RT_STR_START)) { strcat (branch_name, "$"); strcat (branch_name, comp_name); strcpy (element_name, branch_name); } else { element_name[0]=0; strcat (element_name, branch_name); strcat (element_name, "$"); strcat (element_name, comp_name); } } if (rt->el_tag == RT_STR_END) { strcpy (element_name, branch_name); mvlu_trim_branch_name (branch_name); } } else { /* only one element in the RT table assume it's a primitive */ strcpy (element_name, branch_name); } #endif el_tag = rt->el_tag; num_rt_blks = rt->u.arr.num_rt_blks; el_size = rt->el_size; /* Do some special stuff for IEC 61850 SBO controls. */ /* If this comp is start of struct & comp name is "Oper" or "Cancel",*/ /* check SBO state. */ /* NOTE: must be writing "Oper" or "Cancel" struct and ONLY the struct.*/ if (rt == rt_first && /* this is first element */ rt->el_tag == RT_STR_START && /* it is a struct */ (strcmp (ms_comp_name_find(rt), "Oper") == 0 || strcmp (ms_comp_name_find(rt), "Cancel") == 0)) { /* Check "Select" timeouts for ALL SBO controls. */ //mvlu_sbo_chk_timers ();//lnk编译删除,不影响客户端 /* Chk SBO state. Save in wrVaCtrl->sboCtrl (NULL if "Select" not done).*/ //mvl61850_sbo_create_sboname (wrVaCtrl->va, &wrVaCtrl->va_scope, sboName);//lnk编译删除,不影响客户端 //wrVaCtrl->sboCtrl = mvlu_sbo_chk_state (sboName, indCtrl->event->net_info);//lnk编译修改,不影响客户端 wrVaCtrl->sboCtrl = NULL;//lnk编译修改,不影响客户端 } #if !defined(MVL61850_CTL_DISABLE) if (rt->el_tag == RT_STR_START) { /* only increment this if this is "Oper" or already inside "Oper". */ if (oper_nest_level>0) oper_nest_level++; else if (strcmp (ms_comp_name_find(rt), "Oper") == 0) { if (wrVaCtrl->sboCtrl) wrVaCtrl->sboCtrl->ctlState = MVL61850_CTLSTATE_READY; //u_mvl61850_ctl_oper_begin (sboName);//lnk编译删除,不影响客户端 oper_nest_level++; } } if (rt->el_tag == RT_STR_END) { if (oper_nest_level>0) { if (--oper_nest_level==0) { /* All leaf functs have been called for "Oper" struct.*/ /*renxiaobao 控制*/ /*u_mvl61850_ctl_oper_end (indCtrl->event->net_info, sboName, wrVaCtrl->va->base_va);*/ } } } #endif /* !defined(MVL61850_CTL_DISABLE) */ if (ms_is_rt_prim (rt) == SD_TRUE) { wrIndFunIndex = rt->mvluTypeInfo.wrIndFunIndex; if (u_mvlu_leaf_wr_ind_fun != NULL || //(wrIndFunIndex >= 0 && wrIndFunIndex < mvluNumWrFunEntries))//lnk编译修改,原前置程序未使用 0) { mvluWrVaCtrl = (MVLU_WR_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1, sizeof (MVLU_WR_VA_CTRL)); #if defined(MVL_XNAME) strcpy (mvluWrVaCtrl->xName, element_name); #endif mvluWrVaCtrl->primData = (ST_CHAR *) wrVaCtrl->va->data + prim_info->prim_offset; mvluWrVaCtrl->indCtrl = indCtrl; mvluWrVaCtrl->wrVaCtrl = wrVaCtrl; mvluWrVaCtrl->rt = rt; #if defined(MVLU_USE_REF) mvluWrVaCtrl->primRef = rt->mvluTypeInfo.ref; #endif mvluWrVaCtrl->prim_num = prim_info->prim_num; mvluWrVaCtrl->prim_offset_base = prim_info->prim_offset_base; if (u_mvlu_leaf_wr_ind_fun == NULL) {}//(*mvluWrFunInfoTbl[wrIndFunIndex].fun_ptr)(mvluWrVaCtrl);//lnk编译删除,原前置程序未使用 else (*u_mvlu_leaf_wr_ind_fun)(mvluWrVaCtrl); } else wrVaCtrl->resp_tag = WR_RSLT_FAILURE; prim_info->prim_num++; } if (el_tag == RT_ARR_START) { startArrWrs (indCtrl, wrCtrl, wrVaCtrl, rt, prim_info); i += (num_rt_blks + 1); rt += (num_rt_blks + 1); } else { prim_info->prim_offset += el_size; prim_info->prim_offset_base += el_size; } } } /************************************************************************/ /* startArrWrs */ /************************************************************************/ static ST_VOID startArrWrs (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl, MVLAS_WR_VA_CTRL *wrVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info) { ST_RTINT i; MVL_VAR_ASSOC *va; ST_INT numRt; /* DEBUG: To allow bigger arrays, must chg type of these two vars */ /* & va->arrCtrl.low_index & va->arrCtrl.curr_index. */ ST_RTINT low_index; ST_RTINT num_elmnts; numRt = rt->u.arr.num_rt_blks+2; va = wrVaCtrl->va; if (va->arrCtrl.arrAltAccPres == SD_TRUE) { low_index = va->arrCtrl.low_index; num_elmnts = va->arrCtrl.num_elmnts; } else { low_index = 0; num_elmnts = (ST_RTINT) rt->u.arr.num_elmnts; } /* Let's check to see if the client is selecting a sub-object ... */ prim_info->prim_offset += rt->el_size; prim_info->prim_offset_base += rt->el_size; for (i = 0; i < num_elmnts; ++i) { va->arrCtrl.curr_index = low_index + i; startElWrites (indCtrl, wrCtrl, wrVaCtrl, rt+1, numRt-2, prim_info); } prim_info->prim_offset += (rt+numRt-1)->el_size; prim_info->prim_offset_base += (rt+numRt-1)->el_size; } /************************************************************************/ /* mvlu_wr_prim_done */ /************************************************************************/ /* The user calls this function acynchronously when the primitive data */ /* has been put in the 'primData' buffer. */ ST_VOID mvlu_wr_prim_done (MVLU_WR_VA_CTRL *mvluWrVaCtrl, ST_RET rc) { MVLAS_WR_VA_CTRL *wrVaCtrl; wrVaCtrl = mvluWrVaCtrl->wrVaCtrl; if (rc != SD_SUCCESS) wrVaCtrl->resp_tag = WR_RSLT_FAILURE; ++wrVaCtrl->numPrimDataDone; if (wrVaCtrl->numPrimDataDone == wrVaCtrl->numPrimData) { /* all prims done for this variable */ MVL_IND_PEND *indCtrl = mvluWrVaCtrl->indCtrl; #if !defined(MVL61850_CTL_DISABLE) //mvl61850_ctl_req_done (wrVaCtrl, indCtrl->event->net_info);//lnk编译删除,原前置程序未使用 #endif /* !defined(MVL61850_CTL_DISABLE) */ /* Response "usually" sent from u_mvl_write_ind. If it finished */ /* without sending resp AND resp is ready now, send it. */ if (indCtrl->u.wr.ind_funct_done /* ind funct completed */ && chk_write_resp_ready(indCtrl)==SD_SUCCESS) /* resp ready */ mvlas_write_resp (indCtrl); /* send resp */ } M_FREE (MSMEM_MVLU_VA_CTRL, mvluWrVaCtrl); } /************************************************************************/ /* mvluDefAsyncWrIndFun */ /************************************************************************/ static ST_VOID mvluDefAsyncWrIndFun (struct mvlu_wr_va_ctrl *mvluWrVaCtrl) { mvlu_wr_prim_done (mvluWrVaCtrl, SD_SUCCESS); } /************************************************************************/ /************************************************************************/ /* countPrimEl */ /************************************************************************/ static ST_INT countPrimEl (MVL_VAR_ASSOC *va, RUNTIME_TYPE *rt, ST_INT rt_num) { ST_INT i; ST_INT num_elmnts; ST_INT numPrimData; ST_INT subElCount; numPrimData = 0; for (i = 0; i < rt_num; ++i, ++rt) { if (ms_is_rt_prim (rt) == SD_TRUE) ++numPrimData; if (rt->el_tag == RT_ARR_START) { subElCount = countPrimEl (va, rt+1, rt->u.arr.num_rt_blks); if (va->arrCtrl.arrAltAccPres == SD_FALSE) num_elmnts = rt->u.arr.num_elmnts; else num_elmnts = va->arrCtrl.num_elmnts; numPrimData += (subElCount * num_elmnts); i += (rt->u.arr.num_rt_blks + 1); rt += (rt->u.arr.num_rt_blks + 1); } } return (numPrimData); } /************************************************************************/ /* mvluDefGetVaDataBufFun */ /* NOTE: only called when va->base_va->use_static_data==SD_FALSE. */ /************************************************************************/ static ST_VOID mvluDefGetVaDataBufFun (ST_INT service, MVL_VAR_ASSOC *va, ST_INT size) { if (service == MMSOP_READ || service == MMSOP_WRITE || service == MMSOP_INFO_RPT || service == MMSOP_RD_USR_HANDLED) { va->data = M_MALLOC (MSMEM_MVLU_VA_DATA, size); } } /************************************************************************/ /* mvluDefFreeVaDataBufFun */ /* NOTE: only called when va->base_va->use_static_data==SD_FALSE. */ /************************************************************************/ static ST_VOID mvluDefFreeVaDataBufFun (ST_INT service, MVL_VAR_ASSOC *va) { if (service == MMSOP_READ || service == MMSOP_WRITE || service == MMSOP_INFO_RPT || service == MMSOP_RD_USR_HANDLED) { M_FREE (MSMEM_MVLU_VA_DATA, va->data); } } /************************************************************************/ /* mvlu_find_rt_leaf */ /************************************************************************/ RUNTIME_TYPE *mvlu_find_rt_leaf (ST_INT type_id, ST_CHAR *leafName) { RUNTIME_TYPE *rt; ST_INT numRt; ST_RET ret; ret = mvl_get_runtime (type_id, &rt, &numRt); if (ret != SD_SUCCESS) { MVL_LOG_NERR1 ("Could not get RT type for type id %d", type_id); return (NULL); } ret = mvlu_find_uca_var (&rt, &numRt, leafName); if (ret != SD_SUCCESS) { MVL_LOG_NERR1 ("Could not find leaf '%s'", leafName); return (NULL); } if (numRt != 1) { /* See if this is an array with a single primitive element */ if (rt->el_tag == RT_ARR_START && numRt == 3) return (rt+1); MVL_LOG_NERR1 ("'%s' is a branch, not a leaf", leafName); return (NULL); } return (rt); } /************************************************************************/ /* mvlu_find_comp_type */ /* Arguments: */ /* base_type_id type ID for top level variable */ /* flatname flattened component name with top level var */ /* name (Logical Node) stripped off. */ /* sub_rt_type Ptr to (RUNTIME_TYPE *) to be filled in. */ /* sub_rt_num Ptr to "count" to be filled in. */ /************************************************************************/ ST_RET mvlu_find_comp_type (ST_INT base_type_id, ST_CHAR *flatname, RUNTIME_TYPE **sub_rt_type, /* out */ ST_INT *sub_rt_num) /* out */ { MVL_TYPE_CTRL *base_type_ctrl; ST_RET ret; base_type_ctrl = mvl_type_ctrl_find (base_type_id); if (base_type_ctrl) { *sub_rt_type = base_type_ctrl->rt; *sub_rt_num = base_type_ctrl->num_rt; ret = mvlu_find_uca_var (sub_rt_type, sub_rt_num, flatname); } else ret = SD_FAILURE; /* NOTE: don't log error here. Caller may often pass invalid names. */ return (ret); } /************************************************************************/ /* mvlu_get_leaf_val_int8 */ /* Get leaf data value for type ST_INT8. */ /* Arguments: */ /* base_va base variable (i.e. logical node) */ /* flatname flattened leaf name (e.g. ST$Mod$stVal) */ /* data ptr to data written by this function */ /* RETURNS: SD_SUCCESS or SD_FAILURE */ /************************************************************************/ ST_RET mvlu_get_leaf_val_int8 (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_INT8 *data) { RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */ ST_INT comp_rt_num; /* num of rt_types in component */ ST_RET ret = SD_FAILURE; /* Find the attribute type. */ if (mvlu_find_comp_type (base_va->type_id, flatname, /* flattened name */ &comp_rt_type, /* ptr to val set by function */ &comp_rt_num) /* ptr to val set by function */ == SD_SUCCESS) { if (comp_rt_type->el_tag == RT_INTEGER && comp_rt_type->u.p.el_len == 1) { /* must be INT8 */ *data = *(ST_INT8 *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet); ret = SD_SUCCESS; } else MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname); } else MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'", flatname, base_va->name); return (ret); } /************************************************************************/ /* mvlu_get_leaf_val_int32 */ /* Get leaf data value for type ST_INT32. */ /* Arguments: */ /* base_va base variable (i.e. logical node) */ /* flatname flattened leaf name (e.g. ST$Mod$stVal) */ /* data ptr to data written by this function */ /* RETURNS: SD_SUCCESS or SD_FAILURE */ /************************************************************************/ ST_RET mvlu_get_leaf_val_int32 (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_INT32 *data) { RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */ ST_INT comp_rt_num; /* num of rt_types in component */ ST_RET ret = SD_FAILURE; /* Find the attribute type. */ if (mvlu_find_comp_type (base_va->type_id, flatname, /* flattened name */ &comp_rt_type, /* ptr to val set by function */ &comp_rt_num) /* ptr to val set by function */ == SD_SUCCESS) { if (comp_rt_type->el_tag == RT_INTEGER && comp_rt_type->u.p.el_len == 4) { /* must be INT32 */ *data = *(ST_INT32 *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet); ret = SD_SUCCESS; } else MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname); } else MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'", flatname, base_va->name); return (ret); } /************************************************************************/ /* mvlu_get_leaf_val_int_any */ /* Get leaf value for any signed integer. Cast value to ST_INT32. */ /************************************************************************/ ST_RET mvlu_get_leaf_val_int_any (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_INT32 *data) { RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */ ST_INT comp_rt_num; /* num of rt_types in component */ ST_CHAR *raw_data; /* ptr to data. May be any integer size */ ST_INT8 tmp_int8; /* Used to get val as INT8, then cast to INT32 */ ST_INT16 tmp_int16; /* Used to get val as INT16, then cast to INT32 */ ST_RET retcode = SD_SUCCESS; /* Find the attribute type. */ if (mvlu_find_comp_type (base_va->type_id, flatname, /* flattened name */ &comp_rt_type, /* ptr to val set by function */ &comp_rt_num) /* ptr to val set by function */ == SD_SUCCESS) { raw_data = (ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet; if (comp_rt_type->el_tag == RT_INTEGER) { if (comp_rt_type->u.p.el_len == 1) /* INT8 */ { tmp_int8 = *(ST_INT8 *) raw_data; *data = (ST_INT32) tmp_int8; } else if (comp_rt_type->u.p.el_len == 2) /* INT16 */ { tmp_int16 = *(ST_INT16 *) raw_data; *data = (ST_INT32) tmp_int16; } else if (comp_rt_type->u.p.el_len == 4) /* INT32 */ { *data = *(ST_INT32 *) raw_data; } else { MVL_LOG_ERR2 ("Unsupported integer size for '%s' attribute in variable '%s'", flatname, base_va->name); retcode = SD_FAILURE; } } else { MVL_LOG_ERR2 ("Invalid type for '%s' attribute in variable '%s'. Cannot get value.", flatname, base_va->name); retcode = SD_FAILURE; } } else { MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in variable '%s'", flatname, base_va->name); retcode = SD_FAILURE; } return (retcode); } /************************************************************************/ /* mvlu_get_leaf_val_uint32 */ /* Get leaf data value for type ST_UINT32. */ /* Arguments: */ /* base_va base variable (i.e. logical node) */ /* flatname flattened leaf name (e.g. ST$Mod$stVal) */ /* data ptr to data written by this function */ /* RETURNS: SD_SUCCESS or SD_FAILURE */ /************************************************************************/ ST_RET mvlu_get_leaf_val_uint32 (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_UINT32 *data) { RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */ ST_INT comp_rt_num; /* num of rt_types in component */ ST_RET ret = SD_FAILURE; /* Find the attribute type. */ if (mvlu_find_comp_type (base_va->type_id, flatname, /* flattened name */ &comp_rt_type, /* ptr to val set by function */ &comp_rt_num) /* ptr to val set by function */ == SD_SUCCESS) { if (comp_rt_type->el_tag == RT_UNSIGNED && comp_rt_type->u.p.el_len == 4) { /* must be UINT32 */ *data = *(ST_UINT32 *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet); ret = SD_SUCCESS; } else MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname); } else MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'", flatname, base_va->name); return (ret); } /************************************************************************/ /* mvlu_get_leaf_val_boolean */ /* Get leaf data value for type ST_BOOLEAN. */ /* Arguments: */ /* base_va base variable (i.e. logical node) */ /* flatname flattened leaf name (e.g. ST$Mod$stVal) */ /* data ptr to data written by this function */ /* RETURNS: SD_SUCCESS or SD_FAILURE */ /************************************************************************/ ST_RET mvlu_get_leaf_val_boolean (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_BOOLEAN *data) { RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */ ST_INT comp_rt_num; /* num of rt_types in component */ ST_RET ret = SD_FAILURE; /* Find the attribute type. */ if (mvlu_find_comp_type (base_va->type_id, flatname, /* flattened name */ &comp_rt_type, /* ptr to val set by function */ &comp_rt_num) /* ptr to val set by function */ == SD_SUCCESS) { if (comp_rt_type->el_tag == RT_BOOL) { /* must be BOOLEAN */ *data = *(ST_BOOLEAN *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet); ret = SD_SUCCESS; } else MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname); } else MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'", flatname, base_va->name); return (ret); } /************************************************************************/ /* mvlu_get_leaf_val_bvstring */ /* Get leaf data value for type MMS_BVSTRING. */ /* Arguments: */ /* base_va base variable (i.e. logical node) */ /* flatname flattened leaf name (e.g. ST$Mod$stVal) */ /* data ptr to data written by this function */ /* RETURNS: SD_SUCCESS or SD_FAILURE */ /************************************************************************/ ST_RET mvlu_get_leaf_val_bvstring (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, MMS_BVSTRING *data, ST_INT max_num_bits) { RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */ ST_INT comp_rt_num; /* num of rt_types in component */ ST_RET ret = SD_FAILURE; ST_INT num_bytes; /* Find the attribute type. */ if (mvlu_find_comp_type (base_va->type_id, flatname, /* flattened name */ &comp_rt_type, /* ptr to val set by function */ &comp_rt_num) /* ptr to val set by function */ == SD_SUCCESS) { /* NOTE: for Bvstring, el_len is negative. */ if (comp_rt_type->el_tag == RT_BIT_STRING && comp_rt_type->u.p.el_len < 0 && abs (comp_rt_type->u.p.el_len) <= max_num_bits) { num_bytes = 2 + (abs (comp_rt_type->u.p.el_len) + 7)/8; memcpy (data, (ST_CHAR *)base_va->data + comp_rt_type->mvluTypeInfo.offSet, num_bytes); ret = SD_SUCCESS; } else MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname); } else MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'", flatname, base_va->name); return (ret); } /************************************************************************/ /* mvlu_get_leaf_data_ptr */ /* Get leaf data pointer (for any type of leaf). */ /* Arguments: */ /* base_va base variable (i.e. logical node) */ /* flatname flattened leaf name (e.g. ST$Mod$stVal) */ /* rt_type ptr to ptr to type (function sets "*rt_type") */ /* RETURNS: pointer to leaf data (NULL on error) */ /************************************************************************/ ST_VOID *mvlu_get_leaf_data_ptr (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, RUNTIME_TYPE **rt_type) { RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */ ST_INT comp_rt_num; /* num of rt_types in component */ ST_VOID *data; /* Find the attribute type. */ if (mvlu_find_comp_type (base_va->type_id, flatname, /* flattened name */ &comp_rt_type, /* ptr to val set by function */ &comp_rt_num) /* ptr to val set by function */ == SD_SUCCESS) { data = ((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet); *rt_type = comp_rt_type; } else { data = NULL; MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'", flatname, base_va->name); } return (data); } /************************************************************************/ /* mvlu_find_component */ /* Find a lower level variable by processing "flattened" variable name */ /* AND/OR Alternate Access. */ /* NOTE: Similar to "mvlu_find_uca_var" but handles Alternate Access too.*/ /************************************************************************/ ST_RET mvlu_find_component ( ST_CHAR *flatname, /* flattened name (e.g. ST$Mod$stVal) */ ALT_ACCESS *alt_acc, /* NULL if Alt Access Spec not present */ RUNTIME_TYPE **rtIo, ST_INT *numRtIo, ST_INT *offset_io, ST_RTINT *prim_num_io) { ST_RET ret; ST_INT aa_idx; /* index to next alternate access element */ RUNTIME_TYPE *rt_type; ST_CHAR nameBuf[MAX_IDENT_LEN+1]; ST_CHAR *nameToFind; ST_CHAR *compEnd; ST_BOOLEAN nameDone; /* Deal with "flattened" naming first (i.e. fake Alternate Access). */ if (flatname != NULL) { strcpy (nameBuf, flatname); /* copy so we can modify it */ nameToFind = nameBuf; nameDone = SD_FALSE; while (nameDone == SD_FALSE) { /* Isolate the component name for this level, removing subcomp names */ compEnd = strstr (nameToFind, "$"); if (compEnd != NULL) *compEnd = 0; else /* This is the last nest level */ nameDone = SD_TRUE; /* Find the component name in the current runtime type nest level */ /* NOTE: this modifies (*rtIo), (*numRtIo), (*offset_io), (*prim_num_io).*/ ret = mvlu_find_struct_comp (nameToFind, rtIo, numRtIo, offset_io, prim_num_io); if (ret == SD_FAILURE) { /* Many things can cause this so just use FLOW Logging. */ MVLU_LOG_FLOW2 ("Could not find name component %s from name %s", nameToFind, flatname); return (SD_FAILURE); } /* OK, we now have found the component in the runtime type, and our */ /* runtime pointer and numRt reflect the sub-runtime type. */ /* next component. */ /* Prepare to find the next level component name */ nameToFind = compEnd+1; } /* end while */ } /* end if (flatname != NULL)*/ /* Now deal with real alternate access. */ if (alt_acc != NULL) { for (aa_idx = 0; aa_idx < alt_acc->num_aa; aa_idx++) { ALT_ACC_EL *next_el; ST_CHAR *comp_name; /* component name (used only if AA_COMP..)*/ ST_INT arr_index; /* array index (used only if AA_INDEX..)*/ next_el = &alt_acc->aa[aa_idx]; switch (next_el->sel_type) { case AA_COMP : case AA_COMP_NEST : comp_name = next_el->u.component; /* Find the component name in the current runtime type nest level */ ret = mvlu_find_struct_comp (comp_name, rtIo, numRtIo, offset_io, prim_num_io); if (ret == SD_FAILURE) { /* Many things can cause this so just use FLOW Logging. */ MVLU_LOG_FLOW1 ("Could not find name component %s", comp_name); return (SD_FAILURE); } break; case AA_INDEX_NEST : case AA_INDEX : case AA_INDEX_RANGE_NEST : case AA_INDEX_RANGE : rt_type = *rtIo; if (rt_type->el_tag != RT_ARR_START) { MVL_LOG_ERR1 ("Specified array index on non-array component %s", comp_name); return (SD_FAILURE); } /* If Array index range, just need low index for this computation.*/ if (next_el->sel_type== AA_INDEX_RANGE_NEST || next_el->sel_type== AA_INDEX_RANGE) arr_index = next_el->u.ir.low_index; else arr_index = next_el->u.index; *offset_io += rt_type->el_size; /* ARR_START size (usually 0)*/ /* Multiply array element size by array index. */ /* offset_to_last in next rt_type contains array element size.*/ *offset_io += (rt_type+1)->offset_to_last * arr_index; *prim_num_io += (rt_type+1)->mvluTypeInfo.prim_count * arr_index; *rtIo = rt_type + 1; /* point to array elem */ *numRtIo-=2; break; case AA_END_NEST : break; /* just ignore these */ default: MVL_LOG_ERR1 ("Alternate access type '%d' not supported", next_el->sel_type); return (SD_FAILURE); } /* OK, we now have found the component in the runtime type, and our */ /* runtime pointer and numRt reflect the sub-runtime type. */ } } return (SD_SUCCESS); } /************************************************************************/ /* chk_var_writable */ /* Check if EVERY leaf in this variable is writable. If so, variable */ /* is writable. */ /* RETURNS: SD_SUCCESS if variable is writable */ /* SD_FAILURE if variable is NOT writable */ /************************************************************************/ static ST_RET chk_var_writable (RUNTIME_TYPE *rt, ST_INT rt_num) { ST_INT j; /* DEBUG: This mode is almost never used. */ /* 1 leaf function for all leafs. Assume all leafs writable. */ if (u_mvlu_leaf_wr_ind_fun) { return (SD_SUCCESS); /* var is writable */ } /* Find index to "u_no_write_allowed" function & save in static var. */ /* This calls "mvlu_find.." only once, the first time through here. */ /* NOTE: if not found, idx will be -1. If found, idx will be >= 0. */ if (idx_u_no_write_allowed == -2) /* not yet initialized */ {idx_u_no_write_allowed = -1;}//idx_u_no_write_allowed = mvlu_find_wr_ind_fun_index ("u_no_write_allowed");//lnk编译修改,原前置程序未使用 if (idx_u_no_write_allowed < 0) return (SD_SUCCESS); /* funct not found. Assume var is writable*/ /* Loop through type def to see if ANY leaf is unwritable. */ for (j = 0; j < rt_num; ++j, ++rt) { if (ms_is_rt_prim (rt) && rt->mvluTypeInfo.wrIndFunIndex == idx_u_no_write_allowed) { return (SD_FAILURE); /* var not writable */ } } return (SD_SUCCESS); /* var is writable */ } /************************************************************************/ /* chk_write_resp_ready */ /* Check if everything ready to send Write response. */ /* RETURNS: SD_SUCCESS if ready */ /* SD_FAILURE if NOT ready */ /************************************************************************/ static ST_RET chk_write_resp_ready (MVL_IND_PEND *indCtrl) { MVLAS_WRITE_CTRL *wrCtrl; MVLAS_WR_VA_CTRL *wrVaCtrl; /* current var in list of var */ ST_INT i; ST_BOOLEAN retcode; wrCtrl = &indCtrl->u.wr; /* If all primitives for all variables are complete, ready to send resp.*/ wrVaCtrl = wrCtrl->vaCtrlTbl; for (i = 0; i < wrCtrl->numVar; ++i, ++wrVaCtrl) { if (wrVaCtrl->numPrimDataDone != wrVaCtrl->numPrimData) break; /* prims NOT done for this var */ } if (i == wrCtrl->numVar) retcode = SD_SUCCESS; /* ready to send response. */ else retcode = SD_FAILURE; /* not ready */ return (retcode); } /************************************************************************/ /* set_write_resp_dae */ /* Set MMS Write Response DataAccessError for this variable. */ /************************************************************************/ static ST_VOID set_write_resp_dae (MVLAS_WR_VA_CTRL *wrVaCtrl, ST_INT16 dae) /* MMS DataAccessError code */ { wrVaCtrl->resp_tag = WR_RSLT_FAILURE; wrVaCtrl->failure = dae; wrVaCtrl->numPrimDataDone = wrVaCtrl->numPrimData; /* pretend like all prim done*/ } /************************************************************************/ #endif /* defined(MVL_UCA) */ /************************************************************************/