summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--epan/dissectors/packet-epl-profile-parser.c548
-rw-r--r--epan/dissectors/packet-epl.c2158
-rw-r--r--epan/dissectors/packet-epl.h96
3 files changed, 2505 insertions, 297 deletions
diff --git a/epan/dissectors/packet-epl-profile-parser.c b/epan/dissectors/packet-epl-profile-parser.c
index 771a395009..7ac6d2a1e3 100644
--- a/epan/dissectors/packet-epl-profile-parser.c
+++ b/epan/dissectors/packet-epl-profile-parser.c
@@ -39,12 +39,100 @@
#include <string.h>
#include <stdlib.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/str_util.h>
#include <epan/wmem/wmem.h>
-/* XXX: Temporary. successive related change makes use of the functions here */
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Wunused-function"
-#endif
+#if defined HAVE_LIBXML2
+#include <libxml/xmlversion.h>
+
+#if defined LIBXML_XPATH_ENABLED \
+&& defined LIBXML_SAX1_ENABLED \
+&& defined LIBXML_TREE_ENABLED
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#define PARSE_XDD 1
+
+typedef int xpath_handler(xmlNodeSetPtr, void*);
+static xpath_handler populate_object_list, populate_datatype_list, populate_profile_name;
+
+static struct xpath_namespace {
+ const xmlChar *prefix, *href;
+} namespaces[] = {
+ { BAD_CAST "x", BAD_CAST "http://www.ethernet-powerlink.org" },
+ { BAD_CAST "xsi", BAD_CAST "http://www.w3.org/2001/XMLSchema-instance" },
+ { NULL, NULL }
+};
+
+static struct xpath {
+ const xmlChar *expr;
+ xpath_handler *handler;
+} xpaths[] = {
+ {
+ BAD_CAST "//x:ISO15745Profile[x:ProfileHeader/x:ProfileIdentification='Powerlink_Communication_Profile']/x:ProfileHeader/x:ProfileName",
+ populate_profile_name
+ },
+ {
+ BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:DataTypeList/x:defType",
+ populate_datatype_list
+ },
+ {
+ BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:ObjectList/x:Object",
+ populate_object_list
+ },
+ { NULL, NULL }
+};
+
+
+#endif /* LIBXML_XPATH_ENABLED && LIBXML_SAX1_ENABLED && LIBXML_TREE_ENABLED */
+
+#endif /* HAVE_LIBXML2 */
+
+struct datatype {
+ guint16 id;
+ const struct epl_datatype *ptr;
+};
+
+static struct typemap_entry {
+ guint16 id;
+ const char *name;
+ struct epl_datatype *type;
+} epl_datatypes[] = {
+ {0x0001, "Boolean", NULL},
+ {0x0002, "Integer8", NULL},
+ {0x0003, "Integer16", NULL},
+ {0x0004, "Integer32", NULL},
+ {0x0005, "Unsigned8", NULL},
+ {0x0006, "Unsigned16", NULL},
+ {0x0007, "Unsigned32", NULL},
+ {0x0008, "Real32", NULL},
+ {0x0009, "Visible_String", NULL},
+ {0x0010, "Integer24", NULL},
+ {0x0011, "Real64", NULL},
+ {0x0012, "Integer40", NULL},
+ {0x0013, "Integer48", NULL},
+ {0x0014, "Integer56", NULL},
+ {0x0015, "Integer64", NULL},
+ {0x000A, "Octet_String", NULL},
+ {0x000B, "Unicode_String", NULL},
+ {0x000C, "Time_of_Day", NULL},
+ {0x000D, "Time_Diff", NULL},
+ {0x000F, "Domain", NULL},
+ {0x0016, "Unsigned24", NULL},
+ {0x0018, "Unsigned40", NULL},
+ {0x0019, "Unsigned48", NULL},
+ {0x001A, "Unsigned56", NULL},
+ {0x001B, "Unsigned64", NULL},
+ {0x0401, "MAC_ADDRESS", NULL},
+ {0x0402, "IP_ADDRESS", NULL},
+ {0x0403, "NETTIME", NULL},
+ {0x0000, NULL, NULL}
+};
+
+static wmem_map_t *eds_typemap;
struct epl_wmem_iarray {
GEqualFunc equal;
@@ -55,12 +143,446 @@ struct epl_wmem_iarray {
};
static epl_wmem_iarray_t *epl_wmem_iarray_new(wmem_allocator_t *allocator, const guint elem_size, GEqualFunc cmp) G_GNUC_MALLOC;
-static gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr);
-static gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr);
+gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr);
+gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr);
static void epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data);
static void epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr);
-static range_admin_t * epl_wmem_iarray_find(epl_wmem_iarray_t *arr, guint32 value);
+static gboolean
+epl_ishex(const char *num)
+{
+ if (g_str_has_prefix(num, "0x"))
+ return TRUE;
+
+ for (; g_ascii_isxdigit(*num); num++)
+ ;
+
+ if (g_ascii_tolower(*num) == 'h')
+ return TRUE;
+
+ return FALSE;
+}
+
+static guint16
+epl_g_key_file_get_uint16(GKeyFile *gkf, const gchar *group_name, const gchar *key, GError **error)
+{
+ guint16 ret = 0;
+ const char *endptr;
+ char *val = g_key_file_get_string(gkf, group_name, key, error);
+
+ if (!val)
+ return 0;
+
+ if (epl_ishex(val)) /* We need to support XXh, but no octals (is that right?) */
+ ws_hexstrtou16(val, &endptr, &ret);
+ else
+ ws_strtou16(val, &endptr, &ret);
+
+ g_free(val);
+ return ret;
+}
+
+static void
+sort_subindices(void *key _U_, void *value, void *user_data _U_)
+{
+ epl_wmem_iarray_t *subindices = ((struct object*)value)->subindices;
+ if (subindices)
+ epl_wmem_iarray_sort_and_compact(subindices);
+}
+
+void
+epl_eds_init(void)
+{
+ struct typemap_entry *entry;
+ eds_typemap = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
+ for (entry = epl_datatypes; entry->name; entry++)
+ {
+ const struct epl_datatype *type = epl_type_to_hf(entry->name);
+ wmem_map_insert(eds_typemap, GUINT_TO_POINTER(entry->id), (void*)type);
+ }
+}
+
+struct profile *
+epl_eds_load(struct profile *profile, const char *eds_file)
+{
+ GKeyFile* gkf;
+ GError *err;
+ char **group, **groups;
+ char *val;
+ gsize groups_count;
+
+ gkf = g_key_file_new();
+
+ /* Load EDS document */
+ if (!g_key_file_load_from_file(gkf, eds_file, G_KEY_FILE_NONE, &err)){
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", eds_file);
+ profile = NULL;
+ goto cleanup;
+ }
+
+ profile->path = wmem_strdup(profile->scope, eds_file);
+
+ val = g_key_file_get_string(gkf, "FileInfo", "Description", NULL);
+ /* This leaves a trailing space, but that's ok */
+ profile->name = wmem_strndup(profile->scope, val, strcspn(val, "#"));
+ g_free(val);
+
+ groups = g_key_file_get_groups(gkf, &groups_count);
+ for (group = groups; *group; group++)
+ {
+ char *name;
+ const char *endptr;
+ guint16 idx = 0, datatype;
+ struct object *obj = NULL;
+ struct od_entry tmpobj = {0};
+ gboolean is_object = TRUE;
+
+ if (!g_ascii_isxdigit(**group))
+ continue;
+
+ ws_hexstrtou16(*group, &endptr, &idx);
+ if (*endptr == '\0')
+ { /* index */
+ tmpobj.idx = idx;
+ }
+ else if (g_str_has_prefix(endptr, "sub"))
+ { /* subindex */
+ if (!ws_hexstrtou16(endptr + 3, &endptr, &tmpobj.idx)
+ || tmpobj.idx > 0xFF)
+ continue;
+
+ is_object = FALSE;
+ }
+ else continue;
+
+ tmpobj.type_class = epl_g_key_file_get_uint16(gkf, *group, "ObjectType", NULL);
+ if (!tmpobj.type_class)
+ continue;
+
+ datatype = epl_g_key_file_get_uint16(gkf, *group, "DataType", NULL);
+ if (datatype)
+ tmpobj.type = (const struct epl_datatype*)wmem_map_lookup(eds_typemap, GUINT_TO_POINTER(datatype));
+
+ if ((name = g_key_file_get_string(gkf, *group, "ParameterName", NULL)))
+ {
+ gsize count = strcspn(name, "#") + 1;
+ g_strlcpy(
+ tmpobj.name,
+ name,
+ count > sizeof tmpobj.name ? sizeof tmpobj.name : count
+ );
+ g_free(name);
+ }
+
+ obj = epl_profile_object_lookup_or_add(profile, idx);
+
+ if (is_object)
+ { /* Let's add a new object! Exciting! */
+ obj->info = tmpobj;
+ }
+ else
+ { /* Object already there, let's add subindices */
+ struct subobject subobj = {0};
+ if (!obj->subindices)
+ {
+ obj->subindices = epl_wmem_iarray_new(
+ profile->scope,
+ sizeof (struct subobject),
+ subobject_equal
+ );
+ }
+
+ subobj.info = tmpobj;
+ epl_wmem_iarray_insert(obj->subindices, subobj.info.idx, &subobj.range);
+ }
+ }
+
+ /* Unlike with XDDs, subindices might interleave with others, so let's sort them now */
+ wmem_map_foreach(profile->objects, sort_subindices, NULL);
+
+ /* We don't read object mappings from EDS files */
+ /* epl_profile_object_mappings_update(profile); */
+
+cleanup:
+ g_key_file_free(gkf);
+ return profile;
+}
+
+#ifdef PARSE_XDD
+
+void
+epl_xdd_init(void)
+{
+}
+
+struct profile *
+epl_xdd_load(struct profile *profile, const char *xml_file)
+{
+ xmlXPathContextPtr xpathCtx = NULL;
+ xmlDoc *doc = NULL;
+ struct xpath_namespace *ns = NULL;
+ struct xpath *xpath = NULL;
+ GHashTable *typemap = NULL;
+
+ /* Load XML document */
+ doc = xmlParseFile(xml_file);
+ if (!doc)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", xml_file);
+ profile = NULL;
+ goto cleanup;
+ }
+
+
+ /* Create xpath evaluation context */
+ xpathCtx = xmlXPathNewContext(doc);
+ if(!xpathCtx)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to create new XPath context\n");
+ profile = NULL;
+ goto cleanup;
+ }
+
+ /* Register namespaces from list */
+ for (ns = namespaces; ns->href; ns++)
+ {
+ if(xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href) != 0)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
+ profile = NULL;
+ goto cleanup;
+ }
+ }
+
+ profile->path = wmem_strdup(profile->scope, xml_file);
+
+ /* mapping type ids to &hf_s */
+ profile->data = typemap = (GHashTable*)g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+
+ /* Evaluate xpath expressions */
+ for (xpath = xpaths; xpath->expr; xpath++)
+ {
+ xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpath->expr, xpathCtx);
+ if (!xpathObj || !xpathObj->nodesetval)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to evaluate xpath expression \"%s\"\n", xpath->expr);
+ xmlXPathFreeObject(xpathObj);
+ profile = NULL;
+ goto cleanup;
+ }
+
+ /* run handler */
+ if (xpath->handler && xpathObj->nodesetval->nodeNr)
+ xpath->handler(xpathObj->nodesetval, profile);
+ xmlXPathFreeObject(xpathObj);
+ }
+
+ /* We create ObjectMappings while reading the XML, this is makes it likely,
+ * that we won't be able to reference a mapped object in the ObjectMapping
+ * as we didn't reach its XML tag yet. Therefore, after reading the XDD
+ * completely, we update mappings in the profile
+ */
+ epl_profile_object_mappings_update(profile);
+
+cleanup:
+ if (typemap)
+ g_hash_table_destroy(typemap);
+
+ if (xpathCtx)
+ xmlXPathFreeContext(xpathCtx);
+ if (doc)
+ xmlFreeDoc(doc);
+
+ return profile;
+}
+
+static int
+populate_profile_name(xmlNodeSetPtr nodes, void *_profile)
+{
+ struct profile *profile = (struct profile*)_profile;
+ if (nodes->nodeNr == 1
+ && nodes->nodeTab[0]->type == XML_ELEMENT_NODE
+ && nodes->nodeTab[0]->children)
+ {
+ profile->name = wmem_strdup(profile->scope, (char*)nodes->nodeTab[0]->children->content);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+populate_datatype_list(xmlNodeSetPtr nodes, void *_profile)
+{
+ xmlNodePtr cur;
+ int i;
+ struct profile *profile = (struct profile*)_profile;
+
+ for(i = 0; i < nodes->nodeNr; ++i)
+ {
+ xmlAttrPtr attr;
+
+ if(!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE)
+ return -1;
+
+ cur = nodes->nodeTab[i];
+
+
+ for(attr = cur->properties; attr; attr = attr->next)
+ {
+ const char *endptr;
+ const char *key = (const char*)attr->name;
+ const char *val = (const char*)attr->children->content;
+
+ if (g_str_equal("dataType", key))
+ {
+ xmlNode *subnode;
+ guint16 idx = 0;
+
+ if (!ws_hexstrtou16(val, &endptr, &idx))
+ continue;
+
+ for (subnode = cur->children; subnode; subnode = subnode->next)
+ {
+ if (subnode->type == XML_ELEMENT_NODE)
+ {
+ struct datatype *type;
+ const struct epl_datatype *ptr = epl_type_to_hf((char*)subnode->name);
+ if (!ptr)
+ {
+ g_log(NULL, G_LOG_LEVEL_INFO, "Skipping unknown type '%s'\n", subnode->name);
+ continue;
+ }
+ type = g_new(struct datatype, 1);
+ type->id = idx;
+ type->ptr = ptr;
+ g_hash_table_insert((GHashTable*)profile->data, GUINT_TO_POINTER(type->id), type);
+ continue;
+ }
+ }
+
+ }
+ }
+ }
+
+ return 0;
+}
+
+static gboolean
+parse_obj_tag(xmlNode *cur, struct od_entry *out, struct profile *profile) {
+ xmlAttrPtr attr;
+ const char *defaultValue = NULL, *actualValue = NULL;
+ const char *endptr;
+
+ for(attr = cur->properties; attr; attr = attr->next)
+ {
+ const char *key = (char*)attr->name,
+ *val = (char*)attr->children->content;
+
+ if (g_str_equal("index", key))
+ {
+ if (!ws_hexstrtou16(val, &endptr, &out->idx))
+ return FALSE;
+
+ } else if (g_str_equal("subIndex", key)) {
+ if (!ws_hexstrtou16(val, &endptr, &out->idx))
+ return FALSE;
+
+ } else if (g_str_equal("name", key)) {
+ g_strlcpy(out->name, val, sizeof out->name);
+
+ } else if (g_str_equal("objectType", key)) {
+ out->type_class = 0;
+ ws_hexstrtou16(val, &endptr, &out->type_class);
+
+ } else if (g_str_equal("dataType", key)) {
+ guint16 id;
+ if (ws_hexstrtou16(val, &endptr, &id))
+ {
+ struct datatype *type = (struct datatype*)g_hash_table_lookup((GHashTable*)profile->data, GUINT_TO_POINTER(id));
+ if (type) out->type = type->ptr;
+ }
+
+ } else if (g_str_equal("defaultValue", key)) {
+ defaultValue = val;
+
+ } else if (g_str_equal("actualValue", key)) {
+ actualValue = val;
+ }
+#if 0
+ else if (g_str_equal("PDOmapping", key)) {
+ obj.PDOmapping = get_index(ObjectPDOmapping_tostr, val);
+ assert(obj.PDOmapping >= 0);
+ }
+#endif
+ }
+
+ if (actualValue)
+ out->value = g_ascii_strtoull(actualValue, NULL, 0);
+ else if (defaultValue)
+ out->value = g_ascii_strtoull(defaultValue, NULL, 0);
+ else
+ out->value = 0;
+
+
+ return TRUE;
+}
+
+static int
+populate_object_list(xmlNodeSetPtr nodes, void *_profile)
+{
+ int i;
+ struct profile *profile = (struct profile*)_profile;
+
+ for(i = 0; i < nodes->nodeNr; ++i)
+ {
+ xmlNodePtr cur = nodes->nodeTab[i];
+ struct od_entry tmpobj = {0};
+
+ if (!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE)
+ continue;
+
+ parse_obj_tag(cur, &tmpobj, profile);
+
+ if (tmpobj.idx)
+ {
+ struct object *obj = epl_profile_object_add(profile, tmpobj.idx);
+ obj->info = tmpobj;
+
+ if (tmpobj.type_class == OD_ENTRY_ARRAY || tmpobj.type_class == OD_ENTRY_RECORD)
+ {
+ xmlNode *subcur;
+ struct subobject subobj = {0};
+
+ obj->subindices = epl_wmem_iarray_new(profile->scope, sizeof (struct subobject), subobject_equal);
+
+ for (subcur = cur->children; subcur; subcur = subcur->next)
+ {
+ if (subcur->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (parse_obj_tag(subcur, &subobj.info, profile))
+ {
+ epl_wmem_iarray_insert(obj->subindices,
+ subobj.info.idx, &subobj.range);
+ }
+ if (subobj.info.value && epl_profile_object_mapping_add(
+ profile, obj->info.idx, (guint8)subobj.info.idx, subobj.info.value))
+ {
+ g_log(NULL, G_LOG_LEVEL_INFO,
+ "Loaded mapping from XDC %s:%s", obj->info.name, subobj.info.name);
+ }
+ }
+ epl_wmem_iarray_sort_and_compact(obj->subindices);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+#endif /* PARSE_XDD */
/**
* A sorted array keyed by intervals
@@ -104,7 +626,7 @@ free_garray(wmem_allocator_t *scope _U_, wmem_cb_event_t event _U_, void *data)
* it's always the second argument that's getting removed.
*/
-epl_wmem_iarray_t *
+static epl_wmem_iarray_t *
epl_wmem_iarray_new(wmem_allocator_t *scope, const guint elem_size, GEqualFunc equal)
{
epl_wmem_iarray_t *iarr;
@@ -126,21 +648,21 @@ epl_wmem_iarray_new(wmem_allocator_t *scope, const guint elem_size, GEqualFunc e
/** Returns true if the iarr is empty. */
-static gboolean
+gboolean
epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr)
{
return iarr->arr->len == 0;
}
/** Returns true if the iarr is sorted. */
-static gboolean
+gboolean
epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr)
{
return iarr->is_sorted;
}
/** Inserts an element */
-void
+static void
epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data)
{
if (iarr->arr->len)
@@ -157,7 +679,7 @@ epl_wmem_iarray_cmp(const void *a, const void *b)
}
/** Makes array suitable for searching */
-void
+static void
epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr)
{
range_admin_t *elem, *prev = NULL;
@@ -207,7 +729,7 @@ bsearch_garray(const void *key, GArray *arr, int (*cmp)(const void*, const void*
* Finds an element in the interval array. Returns NULL if it doesn't exist
* Calling this is unspecified if the array wasn't sorted before
*/
-static range_admin_t *
+range_admin_t *
epl_wmem_iarray_find(epl_wmem_iarray_t *iarr, guint32 value) {
epl_wmem_iarray_sort_and_compact(iarr);
diff --git a/epan/dissectors/packet-epl.c b/epan/dissectors/packet-epl.c
index 684f7fdd3a..eb6a210e7f 100644
--- a/epan/dissectors/packet-epl.c
+++ b/epan/dissectors/packet-epl.c
@@ -27,6 +27,15 @@
* - Straighten text formatting
* - Remove unneccessary if(tree) checks
*
+ * Copyright (c) 2017: Karlsruhe Institute of Technology (KIT)
+ * Institute for Anthropomatics and Robotics (IAR)
+ * Intelligent Process Control and Robotics (IPR)
+ * http://rob.ipr.kit.edu/
+ *
+ * - Ahmad Fatoum <ahmad[AT]a3f.at>
+ * - ObjectMappings now used for dissecting PDOs
+ * - XDD/EDS files can be read for name/type information
+ *
* A dissector for:
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
@@ -49,12 +58,27 @@
#include "config.h"
+#include "packet-epl.h"
+
+#include <epan/conversation.h>
#include <epan/packet.h>
#include <epan/etypes.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <epan/reassemble.h>
#include <epan/proto_data.h>
+#include <epan/uat.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/file_util.h>
+#include <wsutil/report_message.h>
+#include <glib.h>
+#include <string.h>
+
+#ifdef HAVE_LIBXML2
+ #define IF_LIBXML(x) x
+#else
+ #define IF_LIBXML(x)
+#endif
void proto_register_epl(void);
void proto_reg_handoff_epl(void);
@@ -80,6 +104,7 @@ typedef struct _epl_info_t {
#define EPL_DIAGNOSTIC_DEVICE_NODEID 253
#define EPL_TO_LEGACY_ETHERNET_ROUTER_NODEID 254
#define EPL_BROADCAST_NODEID 255
+#define EPL_IS_CN_NODEID(nodeid) (EPL_DYNAMIC_NODEID < (nodeid) && (nodeid) < EPL_MN_NODEID)
static const value_string addr_str_vals[] = {
{EPL_DYNAMIC_NODEID, " (Dynamically assigned)" },
@@ -541,6 +566,83 @@ static const value_string epl_device_profiles[] = {
{0,NULL}
};
+/* EPL Device Profiles loading */
+/* User Access Table Checkers */
+static gboolean epl_profile_uat_fld_fileopen_check_cb(void *, const char *, unsigned, const void *, const void *, char **);
+static gboolean epl_uat_fld_cn_check_cb(void *, const char *, unsigned, const void *, const void *, char **);
+static gboolean epl_uat_fld_uint16dec_check_cb(void *, const char *, unsigned, const void *, const void *, char **);
+static gboolean epl_uat_fld_uint32hex_check_cb(void *, const char *, unsigned, const void *, const void *, char **);
+
+/* DeviceType:Path User Access Table */
+struct device_profile_uat_assoc {
+ char *path;
+
+ guint device_type;
+ guint vendor_id;
+ guint product_code;
+};
+
+static uat_t *device_profile_uat = NULL;
+static struct device_profile_uat_assoc *device_profile_list_uats = NULL;
+static guint ndevice_profile_uat = 0;
+
+static void *device_profile_uat_copy_cb(void *, const void *, size_t);
+static void device_profile_uat_free_cb(void *);
+static gboolean device_profile_uat_update_record(void *, char **);
+static void device_profile_parse_uat(void);
+
+UAT_DEC_CB_DEF(device_profile_list_uats, device_type, struct device_profile_uat_assoc)
+UAT_HEX_CB_DEF(device_profile_list_uats, vendor_id, struct device_profile_uat_assoc)
+UAT_HEX_CB_DEF(device_profile_list_uats, product_code, struct device_profile_uat_assoc)
+UAT_FILENAME_CB_DEF(device_profile_list_uats, path, struct device_profile_uat_assoc)
+
+static uat_field_t device_profile_list_uats_flds[] = {
+ UAT_FLD_CSTRING_OTHER(device_profile_list_uats, device_type, "DeviceType", epl_uat_fld_uint16dec_check_cb, "e.g. 401"),
+ UAT_FLD_CSTRING_OTHER(device_profile_list_uats, vendor_id, "VendorId", epl_uat_fld_uint32hex_check_cb, "e.g. DEADBEEF"),
+ UAT_FLD_CSTRING_OTHER(device_profile_list_uats, product_code, "ProductCode", epl_uat_fld_uint32hex_check_cb, "e.g. 8BADFOOD"),
+
+ UAT_FLD_FILENAME_OTHER(device_profile_list_uats, path, "Profile Path", epl_profile_uat_fld_fileopen_check_cb, "Path to the EDS" IF_LIBXML("/XDD/XDC")),
+
+ UAT_END_FIELDS
+};
+
+/* NodeID:Path User Access Table */
+struct nodeid_profile_uat_assoc {
+ char *path;
+
+ guint8 is_nodeid:1;
+
+ union {
+ guint8 id;
+ address addr;
+ } node;
+
+ char *id_str;
+};
+
+static uat_t *nodeid_profile_uat = NULL;
+static struct nodeid_profile_uat_assoc *nodeid_profile_list_uats = NULL;
+static guint nnodeid_profile_uat = 0;
+
+
+static void nodeid_profile_list_uats_nodeid_set_cb(void *, const char *, unsigned, const void*, const void*);
+static void nodeid_profile_list_uats_nodeid_tostr_cb(void *, char **, unsigned *, const void*, const void*);
+static void *nodeid_profile_uat_copy_cb(void *, const void *, size_t);
+static void nodeid_profile_uat_free_cb(void *);
+static gboolean nodeid_profile_uat_update_record(void *, char **);
+static void nodeid_profile_parse_uat(void);
+
+UAT_FILENAME_CB_DEF(nodeid_profile_list_uats, path, struct nodeid_profile_uat_assoc)
+
+static uat_field_t nodeid_profile_list_uats_flds[] = {
+ UAT_FLD_CSTRING_OTHER(nodeid_profile_list_uats, nodeid, "Node ID", epl_uat_fld_cn_check_cb, "e.g. 1 or 00-00-5E-00-53-00"),
+
+ UAT_FLD_FILENAME_OTHER(nodeid_profile_list_uats, path, "Profile Path", epl_profile_uat_fld_fileopen_check_cb, "Path to the EDS" IF_LIBXML("/XDD/XDC")),
+
+ UAT_END_FIELDS
+};
+
+
/* SDO SequenceLayer */
#define EPL_ASND_SDO_SEQ_RECEIVE_SEQUENCE_NUMBER_OFFSET 4
@@ -1213,34 +1315,95 @@ static value_string_ext epl_sdo_asnd_commands_short_ext = VALUE_STRING_EXT_INIT(
static const gchar* addr_str_cn = " (Controlled Node)";
static const gchar* addr_str_res = " (reserved)";
-static gint dissect_epl_payload ( proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, gint len, guint8 msgType );
+struct object_mapping {
+ struct {
+ guint16 idx;
+ guint8 subindex;
+ } pdo, /* The PDO to be mapped */
+ param; /* The ObjectMapping OD entry that mapped it */
+
+ guint16 bit_offset;
+ guint16 no_of_bits;
+ int ett;
+ /* info */
+ struct {
+ guint32 first, last;
+ } frame; /* frames for which object_mapping applies */
+ struct od_entry *info;
+ const char *index_name;
+ const char *title;
+};
+
+#define CONVO_FOR_RESPONSE 1
+#define CONVO_FOR_REQUEST 2
+#define CONVO_ALWAYS_CREATE 4
+
+struct read_req {
+ guint16 idx;
+ guint8 subindex;
+
+ guint8 sendsequence;
+
+ const char *index_name;
+ struct od_entry *info;
+};
+
+struct epl_convo {
+ guint8 CN;
+
+ guint16 device_type;
+ guint32 response_time;
+ guint32 vendor_id;
+ guint32 product_code;
+
+ guint generation; /* FIXME remove */
+ wmem_array_t *TPDO; /* CN->MN */
+ wmem_array_t *RPDO; /* MN->CN */
+
+ struct profile *profile;
+
+ guint32 last_frame;
+ guint8 next_read_req;
+ guint8 seq_send;
+
+ struct read_req read_reqs[4];
+
+ /* In lieu of allocating an unknown number of read requests, we'll keep a ring
+ * buff of the 4 most recent ones and when a response comes we add them as packet
+ * data
+ */
+};
+
+static gint dissect_epl_payload(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, gint len, const struct epl_datatype *type, guint8 msgType);
static gint dissect_epl_soc(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
-static gint dissect_epl_preq(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
-static gint dissect_epl_pres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset);
-static gint dissect_epl_soa(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset);
+static gint dissect_epl_preq(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
+static gint dissect_epl_pres(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
+static gint dissect_epl_soa(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
-static gint dissect_epl_asnd_ires(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset);
-static gint dissect_epl_asnd_sres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset);
+static gint dissect_epl_asnd_ires(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
+static gint dissect_epl_asnd_sres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
static gint dissect_epl_asnd_nmtcmd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
static gint dissect_epl_asnd_nmtreq(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
static gint dissect_epl_asnd_nmtdna(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
-static gint dissect_epl_asnd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset);
-static gint dissect_epl_ainv(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset);
+static gint dissect_epl_asnd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
+static gint dissect_epl_ainv(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
static gint dissect_epl_asnd_sdo(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
static gint dissect_epl_asnd_resp(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
-static gint dissect_epl_sdo_sequence(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
-static gint dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset);
-static gint dissect_epl_sdo_command_write_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
-static gint dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
-static gint dissect_epl_sdo_command_read_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
-static gint dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
+static gint dissect_epl_sdo_sequence(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 *seq);
+static gint dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 seq);
+static gint dissect_epl_sdo_command_write_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
+static gint dissect_epl_sdo_command_write_multiple_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
+static gint dissect_epl_sdo_command_read_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
+static gint dissect_epl_sdo_command_read_multiple_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size);
+static gint dissect_object_mapping(struct profile *profile, wmem_array_t *mappings, proto_tree *epl_tree, tvbuff_t *tvb, guint32 framenum, gint offset, guint16 idx, guint8 subindex);
static const gchar* decode_epl_address(guchar adr);
/* Initialize the protocol and registered fields */
static gint proto_epl = -1;
+
static gint hf_epl_mtyp = -1;
static gint hf_epl_node = -1;
static gint hf_epl_dest = -1;
@@ -1348,7 +1511,7 @@ static gint hf_epl_asnd_identresponse_pis = -1;
static gint hf_epl_asnd_identresponse_pos = -1;
static gint hf_epl_asnd_identresponse_rst = -1;
static gint hf_epl_asnd_identresponse_dt = -1;
-static gint hf_epl_asnd_identresponse_profile = -1;
+static gint hf_epl_asnd_identresponse_dt_add = -1;
static gint hf_epl_asnd_identresponse_vid = -1;
static gint hf_epl_asnd_identresponse_productcode = -1;
static gint hf_epl_asnd_identresponse_rno = -1;
@@ -1433,6 +1596,13 @@ static gint hf_epl_asnd_sdo_seq_send_con = -1;
static gint hf_epl_asnd_sdo_cmd = -1;
static gint hf_epl_asnd_sdo_cmd_transaction_id = -1;
static gint hf_epl_asnd_sdo_cmd_response = -1;
+
+#if 0
+static gint hf_epl_asnd_sdo_resp_in = -1;
+static gint hf_epl_asnd_sdo_no_resp = -1;
+static gint hf_epl_asnd_sdo_resp_to = -1;
+#endif
+
static gint hf_epl_asnd_sdo_cmd_abort = -1;
static gint hf_epl_asnd_sdo_cmd_sub_abort = -1;
static gint hf_epl_asnd_sdo_cmd_segmentation = -1;
@@ -1464,6 +1634,84 @@ static gint hf_epl_reassembled_length = -1;
static gint hf_epl_reassembled_data = -1;
static gint hf_epl_sdo_multi_param_sub_abort = -1;
+static gint hf_epl_asnd_identresponse_profile_path = -1;
+
+/* EPL OD Data Types */
+static gint hf_epl_pdo = -1;
+static gint hf_epl_pdo_index = -1;
+static gint hf_epl_pdo_subindex = -1;
+
+static gint hf_epl_od_meta = -1;
+static gint hf_epl_od_meta_mapping_index = -1;
+static gint hf_epl_od_meta_mapping_subindex = -1;
+static gint hf_epl_od_meta_lifetime_start = -1;
+static gint hf_epl_od_meta_lifetime_end = -1;
+static gint hf_epl_od_meta_offset = -1;
+static gint hf_epl_od_meta_length = -1;
+
+static gint hf_epl_od_boolean = -1;
+static gint hf_epl_od_int = -1;
+static gint hf_epl_od_uint = -1;
+static gint hf_epl_od_real = -1;
+static gint hf_epl_od_string = -1;
+static gint hf_epl_od_octet_string = -1;
+static gint hf_epl_od_time = -1;
+#if 0
+static gint hf_epl_od_time_difference = -1;
+static gint hf_epl_od_domain = -1;
+#endif
+static gint hf_epl_od_mac = -1;
+static gint hf_epl_od_ipv4 = -1;
+
+#define EPL_PDO_TYPE_COUNT 8
+
+static const struct epl_datatype {
+ const char *name;
+ gint *hf;
+ guint encoding;
+ guint8 len;
+} epl_datatype[] = {
+ { "Boolean", &hf_epl_od_boolean, ENC_LITTLE_ENDIAN , 1 },
+ /* integer types */
+ { "Integer8", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 1 },
+ { "Integer16", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 2 },
+ { "Integer24", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 3 },
+ { "Integer32", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 4 },
+ { "Integer40", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 5 },
+ { "Integer48", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 6 },
+ { "Integer56", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 7 },
+ { "Integer64", &hf_epl_od_int, ENC_LITTLE_ENDIAN, 8 },
+
+ { "Unsigned8", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 1 },
+ { "Unsigned16", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 2 },
+ { "Unsigned24", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 3 },
+ { "Unsigned32", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 4 },
+ { "Unsigned40", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 5 },
+ { "Unsigned48", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 6 },
+ { "Unsigned56", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 7 },
+ { "Unsigned64", &hf_epl_od_uint, ENC_LITTLE_ENDIAN, 8 },
+
+ /* non-integer types */
+ { "Real32", &hf_epl_od_real, ENC_LITTLE_ENDIAN, 4 },
+ { "Real64", &hf_epl_od_real, ENC_LITTLE_ENDIAN, 8 },
+ { "Visible_String", &hf_epl_od_string, ENC_ASCII, 0 },
+ { "Octet_String", &hf_epl_od_octet_string, ENC_NA, 0 },
+ { "Unicode_String", &hf_epl_od_string, ENC_UCS_2, 0 },
+
+ { "MAC_ADDRESS", &hf_epl_od_mac, ENC_BIG_ENDIAN, 6 },
+ { "IP_ADDRESS", &hf_epl_od_ipv4, ENC_BIG_ENDIAN, 4 },
+#if 0
+ { "Domain", &hf_epl_od_domain, ENC_NA },
+
+ { "Time_of_Day", &hf_epl_od_time, ENC_NA },
+ { "Time_Diff", &hf_epl_od_time_difference, ENC_NA },
+#endif
+ { "NETTIME", &hf_epl_od_time, ENC_TIME_TIMESPEC, 8 },
+
+ { NULL, NULL }
+};
+
+
static gint ett_epl_fragment = -1;
static gint ett_epl_fragments = -1;
@@ -1491,14 +1739,16 @@ static const fragment_items epl_frag_items = {
};
static gint hf_epl_asnd_sdo_cmd_abort_code = -1;
-/*static gint hf_epl_asnd_sdo_cmd_abort_flag = -1;*/
-/*static gint hf_epl_asnd_sdo_cmd_segmentation_flag = -1;*/
-/*static gint hf_epl_asnd_sdo_cmd_cmd_valid_test = -1;*/
+#if 0
+static gint hf_epl_asnd_sdo_cmd_abort_flag = -1;
+static gint hf_epl_asnd_sdo_cmd_segmentation_flag = -1;
+static gint hf_epl_asnd_sdo_cmd_cmd_valid_test = -1;
-/*static gint hf_epl_asnd_sdo_actual_command_id = -1;*/
+static gint hf_epl_asnd_sdo_actual_command_id = -1;
-/*static gint hf_epl_asnd_sdo_actual_segment_size = -1;*/
-/*static gint hf_epl_asnd_sdo_actual_payload_size_read = -1;*/
+static gint hf_epl_asnd_sdo_actual_segment_size = -1;
+static gint hf_epl_asnd_sdo_actual_payload_size_read = -1;
+#endif
/* Initialize the subtree pointers */
static gint ett_epl = -1;
@@ -1521,6 +1771,8 @@ static gint ett_epl_asnd_sdo_cmd_data_mapping = -1;
static gint ett_epl_soa_sync = -1;
static gint ett_epl_asnd_sync = -1;
+static gint ett_epl_pdo_meta = -1;
+
static expert_field ei_duplicated_frame = EI_INIT;
static expert_field ei_recvseq_value = EI_INIT;
static expert_field ei_sendseq_value = EI_INIT;
@@ -1531,11 +1783,503 @@ static expert_field ei_real_length_differs = EI_INIT;
static dissector_handle_t epl_handle;
static gboolean show_cmd_layer_for_duplicated = FALSE;
+static gboolean show_pdo_meta_info = FALSE;
+static gboolean use_xdc_mappings = TRUE;
+static gboolean interpret_untyped_as_le = TRUE;
+static gboolean use_sdo_mappings = TRUE;
+
static gint ett_epl_asnd_sdo_data_reassembled = -1;
static reassembly_table epl_reassembly_table;
static GHashTable *epl_duplication_table = NULL;
+const struct
+epl_datatype *epl_type_to_hf(const char *name)
+{
+ const struct epl_datatype *entry;
+ for (entry = epl_datatype; entry->name; entry++)
+ {
+ if (strcmp(name, entry->name) == 0)
+ return entry;
+ }
+ return NULL;
+}
+
+static guint
+epl_address_hash(gconstpointer a)
+{
+ return add_address_to_hash(0, (const address*)a);
+}
+static gboolean
+epl_address_equal(gconstpointer a, gconstpointer b)
+{
+ return addresses_equal((const address*)a, (const address*)b);
+}
+
+/* FIXME
+ * PDO Mappings store object/subobjct pointers and thus need to be
+ * updated after a profile change. We purge them by resetting the
+ * memory pool. As PDO Mappings are refereneced via Conversations,
+ * we need to fix up those too. I didn't figure out how to clear
+ * conversations yet, so till now, we keep a variable to tell us
+ * if we have dangling pointers. Courtesy of Peter Wu.
+ */
+
+guint current_convo_generation = 0; /* FIXME remove */
+static wmem_allocator_t *pdo_mapping_scope;
+static struct object_mapping *
+get_object_mappings(wmem_array_t *arr, guint *len)
+{
+ *len = wmem_array_get_count(arr);
+ return (struct object_mapping*)wmem_array_get_raw(arr);
+}
+static int
+object_mapping_cmp(const void *_a, const void *_b)
+{
+ const struct object_mapping *a = (const struct object_mapping*)_a;
+ const struct object_mapping *b = (const struct object_mapping*)_b;
+
+ if (a->bit_offset < b->bit_offset) return -1;
+ if (a->bit_offset > b->bit_offset) return +1;
+ return 0;
+}
+static gboolean
+object_mapping_eq(struct object_mapping *a, struct object_mapping *b)
+{
+ return a->pdo.idx == b->pdo.idx
+ && a->pdo.subindex == b->pdo.subindex
+ && a->frame.first == b->frame.first
+ && a->param.idx == b->param.idx
+ && a->param.subindex == b->param.subindex;
+}
+static guint
+add_object_mapping(wmem_array_t *arr, struct object_mapping *mapping)
+{
+ /* let's check if this overwrites an existing mapping */
+ guint i, len;
+ /* A bit ineffecient (looping backwards would be better), but it's acyclic anyway */
+ struct object_mapping *old = get_object_mappings(arr, &len);
+ for (i = 0; i < len; i++)
+ {
+ if (object_mapping_eq(&old[i], mapping))
+ return len;
+
+ if (old[i].frame.first < mapping->frame.first
+ && (CHECK_OVERLAP_LENGTH(old[i].bit_offset, old[i].no_of_bits, mapping->bit_offset, mapping->no_of_bits)
+ || (old[i].param.idx == mapping->param.idx && old[i].param.subindex == mapping->param.subindex
+ && CHECK_OVERLAP_ENDS(old[i].frame.first, old[i].frame.last, mapping->frame.first, mapping->frame.last))))
+ {
+ old[i].frame.last = mapping->frame.first;
+ }
+ }
+
+ wmem_array_append(arr, mapping, 1);
+ wmem_array_sort(arr, object_mapping_cmp);
+ return len + 1;
+}
+
+static wmem_map_t *epl_profiles_by_device, *epl_profiles_by_nodeid, *epl_profiles_by_address;
+static struct profile *epl_default_profile;
+static const char *epl_default_profile_path = NULL, *epl_default_profile_path_last = NULL;
+
+static gboolean
+profile_del_cb(wmem_allocator_t *pool _U_, wmem_cb_event_t event _U_, void *_profile)
+{
+ struct profile *profile = (struct profile*)_profile;
+ if (profile->parent_map)
+ wmem_map_remove(profile->parent_map, profile->data);
+ wmem_destroy_allocator(profile->scope);
+ return FALSE;
+}
+
+static void
+profile_del(struct profile *profile)
+{
+ if (!profile) return;
+ wmem_unregister_callback(profile->parent_scope, profile->cb_id);
+ profile_del_cb(NULL, WMEM_CB_DESTROY_EVENT, profile);
+}
+
+static struct profile *
+profile_new(wmem_allocator_t *parent_pool)
+{
+ wmem_allocator_t *pool;
+ struct profile *profile;
+
+ pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
+ profile = wmem_new0(pool, struct profile);
+ profile->cb_id = wmem_register_callback(parent_pool, profile_del_cb, profile);
+
+ profile->scope = pool;
+ profile->parent_scope = parent_pool;
+ profile->parent_map = NULL;
+ profile->objects = wmem_map_new(pool, g_direct_hash, g_direct_equal);
+ profile->name = NULL;
+ profile->path = NULL;
+ profile->RPDO = wmem_array_new(pool, sizeof (struct object_mapping));
+ profile->TPDO = wmem_array_new(pool, sizeof (struct object_mapping));
+ profile->next = NULL;
+
+ return profile;
+}
+
+static struct object *object_lookup(struct profile *profile, guint16 idx);
+static struct subobject *subobject_lookup(struct object *obj, guint8 subindex);
+
+struct object *
+epl_profile_object_add(struct profile *profile, guint16 idx)
+{
+ struct object *object = wmem_new0(profile->scope, struct object);
+
+ object->info.idx = idx;
+
+ wmem_map_insert(profile->objects, GUINT_TO_POINTER(object->info.idx), object);
+ return object;
+}
+
+struct object *
+epl_profile_object_lookup_or_add(struct profile *profile, guint16 idx)
+{
+ struct object *obj = object_lookup(profile, idx);
+ return obj ? obj : epl_profile_object_add(profile, idx);
+}
+
+
+gboolean
+ epl_profile_object_mapping_add(struct profile *profile, guint16 idx, guint8 subindex, guint64 mapping)
+{
+ wmem_array_t *mappings;
+ tvbuff_t *tvb;
+ guint64 mapping_le;
+
+ if (!use_xdc_mappings)
+ return FALSE;
+
+ if(idx == EPL_SOD_PDO_RX_MAPP && subindex >= 0x01 && subindex <= 0xfe)
+ mappings = profile->RPDO;
+ else if (idx == EPL_SOD_PDO_TX_MAPP && subindex >= 0x01 && subindex <= 0xfe)
+ mappings = profile->TPDO;
+ else
+ return FALSE;
+
+ mapping_le = GUINT64_TO_LE(mapping);
+ tvb = tvb_new_real_data((guint8*)&mapping_le, sizeof mapping_le, sizeof mapping_le);
+
+ return dissect_object_mapping(profile, mappings, NULL, tvb, 0, 0, idx, subindex) == EPL_OBJECT_MAPPING_SIZE;
+}
+
+gboolean
+epl_profile_object_mappings_update(struct profile *profile)
+{
+ gboolean updated_any = FALSE;
+ struct object_mapping *mappings;
+ wmem_array_t *PDOs[3], **PDO;
+
+ if (!use_xdc_mappings)
+ return FALSE;
+
+
+ PDOs[0] = profile->RPDO;
+ PDOs[1] = profile->TPDO;
+ PDOs[2] = NULL;
+
+ for (PDO = PDOs; *PDO; PDO++)
+ {
+ guint i, len;
+ len = wmem_array_get_count(*PDO);
+ mappings = (struct object_mapping*)wmem_array_get_raw(*PDO);
+
+ for (i = 0; i < len; i++)
+ {
+ struct object_mapping *map = &mappings[i];
+ struct object *mapping_obj;
+ struct subobject *mapping_subobj;
+
+ if (!(mapping_obj = object_lookup(profile, map->pdo.idx)))
+ continue;
+ map->info = &mapping_obj->info;
+ map->index_name = map->info->name;
+ updated_any = TRUE;
+ if (!(mapping_subobj = subobject_lookup(mapping_obj, map->pdo.subindex)))
+ continue;
+ map->info = &mapping_subobj->info;
+ }
+ }
+
+ return updated_any;
+}
+
+static struct read_req *
+convo_read_req_get(struct epl_convo *convo, packet_info *pinfo, guint8 SendSequenceNumber)
+{
+ guint i;
+ guint32 seq_p_key = (ETHERTYPE_EPL_V2 << 16) | convo->seq_send;
+ struct read_req *req = (struct read_req*)p_get_proto_data(wmem_file_scope(), pinfo, proto_epl, seq_p_key);
+
+ if (req)
+ return req;
+
+ for (i = 0; i < array_length(convo->read_reqs); i++)
+ {
+ if(convo->read_reqs[i].sendsequence == SendSequenceNumber)
+ {
+ req = wmem_new(wmem_file_scope(), struct read_req);
+ *req = convo->read_reqs[i];
+ p_add_proto_data(wmem_file_scope(), pinfo, proto_epl, seq_p_key, req);
+ return req;
+ }
+ }
+
+ return NULL;
+}
+static struct read_req *
+convo_read_req_set(struct epl_convo *convo, guint8 SendSequenceNumber)
+{
+ struct read_req *slot = &convo->read_reqs[convo->next_read_req++];
+ convo->next_read_req %= array_length(convo->read_reqs);
+ slot->sendsequence = SendSequenceNumber;
+ return slot;
+}
+
+
+static int
+dissect_epl_pdo(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint offset, guint len, guint8 msgType)
+{
+ wmem_array_t *mapping = msgType == EPL_PRES ? convo->TPDO : convo->RPDO;
+ tvbuff_t *payload_tvb;
+ guint rem_len, payload_len, payload_len_bits;
+ heur_dtbl_entry_t *hdtbl_entry = NULL;
+ proto_item *item;
+ guint i, maps_count;
+ guint off = 0;
+
+ struct object_mapping *mappings = get_object_mappings(mapping, &maps_count);
+
+ if (len <= 0)
+ return offset;
+
+ rem_len = tvb_captured_length_remaining(tvb, offset);
+ payload_tvb = tvb_new_subset_length(tvb, offset, MIN(len, rem_len));
+ payload_len = tvb_captured_length_remaining(payload_tvb, 0);
+ payload_len_bits = payload_len * 8;
+ if ( payload_len < len )
+ {
+ item = proto_tree_add_uint(epl_tree, hf_epl_payload_real, tvb, offset, payload_len, payload_len);
+ PROTO_ITEM_SET_GENERATED(item);
+ expert_add_info(pinfo, item, &ei_real_length_differs );
+ }
+
+ if ( dissector_try_heuristic(heur_epl_data_subdissector_list, payload_tvb, pinfo, epl_tree, &hdtbl_entry, &msgType))
+ return offset + payload_len;
+
+
+ for (i = 0; i < maps_count; i++)
+ {
+ proto_tree *pdo_tree;
+ struct object_mapping *map = &mappings[i];
+ guint willbe_offset_bits = map->bit_offset + map->no_of_bits;
+
+ if (!(map->frame.first < pinfo->num && pinfo->num < map->frame.last))
+ continue;
+
+ if (willbe_offset_bits > payload_len_bits)
+ break;
+
+ item = proto_tree_add_string_format(epl_tree, hf_epl_pdo, payload_tvb, 0, 0, "", "%s", map->title);
+ pdo_tree = proto_item_add_subtree(item, map->ett);
+
+ item = proto_tree_add_uint_format_value(pdo_tree, hf_epl_pdo_index, payload_tvb, 0, 0, map->pdo.idx, "%04X", map->pdo.idx);
+ PROTO_ITEM_SET_GENERATED(item);
+ if (map->info)
+ proto_item_append_text (item, " (%s)", map->index_name);
+
+ item = proto_tree_add_uint_format_value(pdo_tree, hf_epl_pdo_subindex, payload_tvb, 0, 0, map->pdo.subindex, "%02X", map->pdo.subindex);
+ PROTO_ITEM_SET_GENERATED(item);
+
+ if (map->info && map->info->name != map->index_name)
+ proto_item_append_text (item, " (%s)", map->info->name);
+
+ if (show_pdo_meta_info)
+ {
+ proto_tree *meta_tree;
+ proto_item *meta_item = proto_tree_add_item(pdo_tree, hf_epl_od_meta, tvb, offset, 0, ENC_NA);
+ meta_tree = proto_item_add_subtree(meta_item, ett_epl_pdo_meta);
+
+ item = proto_tree_add_uint(meta_tree, hf_epl_od_meta_mapping_index, tvb, 0, 0, map->param.idx);
+ item = proto_tree_add_uint(meta_tree, hf_epl_od_meta_mapping_subindex, tvb, 0, 0, map->param.subindex);
+ item = proto_tree_add_uint(meta_tree, hf_epl_od_meta_lifetime_start, tvb, 0, 0, map->frame.first);
+
+ if (map->frame.last != G_MAXUINT32)
+ item = proto_tree_add_uint(meta_tree, hf_epl_od_meta_lifetime_end, tvb, 0, 0, map->frame.last);
+
+ item = proto_tree_add_uint(meta_tree, hf_epl_od_meta_offset, tvb, 0, 0, map->bit_offset);
+ proto_item_append_text (item, " bits");
+ item = proto_tree_add_uint(meta_tree, hf_epl_od_meta_length, tvb, 0, 0, map->no_of_bits);
+ proto_item_append_text (item, " bits");
+
+
+ PROTO_ITEM_SET_GENERATED(meta_item);
+ }
+
+ dissect_epl_payload(
+ pdo_tree,
+ tvb_new_octet_aligned(payload_tvb, map->bit_offset, map->no_of_bits),
+ pinfo, 0, map->no_of_bits / 8, map->info ? map->info->type : NULL, msgType
+ );
+
+ payload_len -= map->no_of_bits / 8;
+
+ off = willbe_offset_bits / 8;
+ }
+
+ /* If we don't have more information, resort to data dissector */
+ if (tvb_captured_length_remaining(payload_tvb, off))
+ {
+ return dissect_epl_payload(epl_tree, payload_tvb, pinfo, off, payload_len, NULL, msgType);
+ }
+ return offset + payload_len;
+}
+
+static address epl_placeholder_mac;
+
+static struct epl_convo *
+epl_get_convo(packet_info *pinfo, int opts)
+{
+ struct epl_convo *convo;
+ conversation_t * epan_convo;
+ guint32 node_port;
+ address *node_addr;
+ address *node_dl_addr;
+
+ if (opts & CONVO_FOR_REQUEST)
+ {
+ node_port = pinfo->destport;
+ node_addr = &pinfo->dst;
+ node_dl_addr = &pinfo->dl_dst;
+ }
+ else
+ {
+ node_port = pinfo->srcport;
+ node_addr = &pinfo->src;
+ node_dl_addr = &pinfo->dl_src;
+ }
+ /* It'd be better to consult the Ethernet or IP address when matching conversations,
+ * but an ASnd request is targeted at a Multicast MAC address, so we'll use
+ * a constant address for lookup
+ * TODO: If you, the reader, figure out a way to lookup a conversation by port only
+ * remove the following assignment
+ */
+ node_addr = &epl_placeholder_mac;
+
+ if ((epan_convo = find_conversation(pinfo->num, node_addr, node_addr,
+ pinfo->ptype, node_port, node_port, NO_ADDR_B|NO_PORT_B)))
+ {
+ /* XXX Do I need to check setup_frame != pinfo->num in order to not
+ * create unnecessary new conversations?
+ * if not, move the CONVO_ALWAYS_CREATE check up into the if and drop
+ * the goto
+ */
+ if ((opts & CONVO_ALWAYS_CREATE) && epan_convo->setup_frame != pinfo->num)
+ goto new_convo_creation;
+
+ if (pinfo->num > epan_convo->last_frame)
+ epan_convo->last_frame = pinfo->num;
+ }
+ else
+ {
+new_convo_creation:
+ epan_convo = conversation_new(pinfo->num, node_addr, node_addr,
+ pinfo->ptype, node_port, node_port, NO_ADDR2|NO_PORT2);
+ }
+
+ convo = (struct epl_convo*)conversation_get_proto_data(epan_convo, proto_epl);
+
+ if (convo == NULL)
+ {
+ convo = wmem_new0(wmem_file_scope(), struct epl_convo);
+ convo->CN = (guint8)node_port;
+
+ convo->generation = current_convo_generation; /* FIXME remove */
+ convo->TPDO = wmem_array_new(pdo_mapping_scope, sizeof (struct object_mapping));
+ convo->RPDO = wmem_array_new(pdo_mapping_scope, sizeof (struct object_mapping));
+
+ convo->profile = (struct profile*)wmem_map_lookup(epl_profiles_by_address, node_dl_addr);
+ if (!convo->profile)
+ convo->profile = (struct profile*)wmem_map_lookup(epl_profiles_by_nodeid, GUINT_TO_POINTER(convo->CN));
+
+ if (!convo->profile)
+ convo->profile = epl_default_profile;
+
+ convo->seq_send = 0x00;
+ conversation_add_proto_data(epan_convo, proto_epl, (void *)convo);
+ }
+
+ if (convo->generation != current_convo_generation)
+ { /* FIXME remove */
+ convo->TPDO = wmem_array_new(pdo_mapping_scope, sizeof (struct object_mapping));
+ convo->RPDO = wmem_array_new(pdo_mapping_scope, sizeof (struct object_mapping));
+ convo->generation = current_convo_generation;
+ }
+
+
+ return convo;
+}
+
+static gboolean
+epl_update_convo_cn_profile(struct epl_convo *convo)
+{
+ struct profile *candidate; /* Best matching profile */
+ if ((candidate = (struct profile*)wmem_map_lookup(epl_profiles_by_device, GUINT_TO_POINTER(convo->device_type))))
+ {
+ struct profile *iter = candidate;
+ do {
+ if ((iter->vendor_id == 0 && convo->product_code == 0 && !candidate->vendor_id)
+ || (iter->vendor_id == convo->vendor_id && !candidate->product_code)
+ || (iter->vendor_id == convo->vendor_id && iter->product_code == convo->product_code))
+ {
+ candidate = iter;
+ }
+
+ } while ((iter = iter->next));
+
+
+ convo->profile = candidate;
+
+ if (!wmem_array_get_count(convo->RPDO))
+ {
+ wmem_array_append(convo->RPDO,
+ wmem_array_get_raw(candidate->RPDO),
+ wmem_array_get_count(candidate->RPDO)
+ );
+ }
+ if (!wmem_array_get_count(convo->TPDO))
+ {
+ wmem_array_append(convo->TPDO,
+ wmem_array_get_raw(candidate->TPDO),
+ wmem_array_get_count(candidate->TPDO)
+ );
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static struct object *
+object_lookup(struct profile *profile, guint16 idx)
+{
+ if (profile == NULL)
+ return NULL;
+
+ return (struct object*)wmem_map_lookup(profile->objects, GUINT_TO_POINTER(idx));
+}
+
+static struct subobject *
+subobject_lookup(struct object *obj, guint8 subindex)
+{
+ if (!obj || !obj->subindices) return NULL;
+ return (struct subobject*)epl_wmem_iarray_find(obj->subindices, subindex);
+}
+
/* epl duplication table hash function */
static guint
epl_duplication_hash(gconstpointer k)
@@ -1659,14 +2403,20 @@ setup_dissector(void)
/* init duplication hash table */
epl_duplication_table = g_hash_table_new(epl_duplication_hash, epl_duplication_equal);
- /* create memory block for uploda/download */
+ /* create memory block for upload/download */
memset(&epl_asnd_sdo_reassembly_write, 0, sizeof(epl_sdo_reassembly));
memset(&epl_asnd_sdo_reassembly_read, 0, sizeof(epl_sdo_reassembly));
+
+ /* free object mappings in one swoop */
+ pdo_mapping_scope = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
}
static void
cleanup_dissector(void)
{
+ wmem_destroy_allocator(pdo_mapping_scope);
+ pdo_mapping_scope = NULL;
+
g_hash_table_destroy(epl_duplication_table);
count = 0;
ct = 0;
@@ -1712,7 +2462,7 @@ elp_version( gchar *result, guint32 version )
static int
dissect_eplpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean udpencap)
{
- guint8 epl_mtyp, epl_src, epl_dest;
+ guint8 epl_mtyp;
const gchar *src_str, *dest_str;
/* static epl_info_t mi; */
/* Set up structures needed to add the protocol subtree and manage it */
@@ -1728,15 +2478,7 @@ dissect_eplpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean udp
}
/* Make entries in Protocol column and Info column on summary display */
- if (!udpencap)
- {
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "POWERLINK");
- }
- else
- {
- /* guess that this is an EPL frame encapsulated into an UDP datagram */
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "POWERLINK/UDP");
- }
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, udpencap ? "POWERLINK/UDP" : "POWERLINK");
/* Get message type */
epl_mtyp = tvb_get_guint8(tvb, EPL_MTYP_OFFSET) & 0x7F;
@@ -1754,15 +2496,24 @@ dissect_eplpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean udp
tap_queue_packet(epl_tap, pinfo, &mi);
*/
+ /* IP addresses are always in 192.168.100.0/24
+ * with last octet being the node id
+ * The original src/dest node ids are reserved
+ */
+
+ pinfo->ptype = PT_NONE;
+
/* Get Destination */
- epl_dest = tvb_get_guint8(tvb, EPL_DEST_OFFSET);
- epl_segmentation.dest = epl_dest;
- dest_str = decode_epl_address(epl_dest);
+ pinfo->destport = !udpencap ? tvb_get_guint8(tvb, EPL_DEST_OFFSET)
+ : ((guint8*)pinfo->net_dst.data)[3];
+ epl_segmentation.dest = pinfo->destport;
+ dest_str = decode_epl_address(pinfo->destport);
/* Get Source */
- epl_src = tvb_get_guint8(tvb, EPL_SRC_OFFSET);
- epl_segmentation.src = epl_src;
- src_str = decode_epl_address(epl_src);
+ pinfo->srcport = !udpencap ? tvb_get_guint8(tvb, EPL_SRC_OFFSET)
+ : ((guint8*)pinfo->net_src.data)[3];
+ epl_segmentation.src = pinfo->srcport;
+ src_str = decode_epl_address(pinfo->srcport);
col_clear(pinfo->cinfo, COL_INFO);
@@ -1770,45 +2521,31 @@ dissect_eplpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean udp
switch (epl_mtyp)
{
case EPL_SOC:
- col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d SoC ", epl_src, epl_dest);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d SoC ", pinfo->srcport, pinfo->destport);
break;
case EPL_PREQ:
- col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d PReq ", epl_src, epl_dest);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d PReq ", pinfo->srcport, pinfo->destport);
break;
case EPL_PRES:
- col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d PRes ", epl_src, epl_dest);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d PRes ", pinfo->srcport, pinfo->destport);
break;
case EPL_SOA:
- col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d SoA ", epl_src, epl_dest);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d SoA ", pinfo->srcport, pinfo->destport);
break;
case EPL_ASND:
- if (udpencap)
- {
- col_set_str(pinfo->cinfo, COL_INFO, " ASnd ");
- }
- else
- {
- col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d ASnd ", epl_src, epl_dest);
- }
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d ASnd ", pinfo->srcport, pinfo->destport);
break;
case EPL_AINV:
- if (udpencap)
- {
- col_set_str(pinfo->cinfo, COL_INFO, " AInv ");
- }
- else
- {
- col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d AInv ", epl_src, epl_dest);
- }
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d AInv ", pinfo->srcport, pinfo->destport);
break;
case EPL_AMNI:
- col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d AMNI ", epl_src, epl_dest);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%3d->%3d AMNI ", pinfo->srcport, pinfo->destport);
break;
default: /* no valid EPL packet */
@@ -1848,28 +2585,31 @@ dissect_eplpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean udp
/* The rest of the EPL dissector depends on the message type */
switch (epl_mtyp)
{
+ struct epl_convo *convo;
case EPL_SOC:
offset = dissect_epl_soc(epl_tree, tvb, pinfo, offset);
break;
case EPL_PREQ:
- offset = dissect_epl_preq(epl_tree, tvb, pinfo, offset);
+ convo = epl_get_convo(pinfo, CONVO_FOR_REQUEST);
+ offset = dissect_epl_preq(convo, epl_tree, tvb, pinfo, offset);
break;
case EPL_PRES:
- offset = dissect_epl_pres(epl_tree, tvb, pinfo, epl_src, offset);
+ convo = epl_get_convo(pinfo, CONVO_FOR_RESPONSE);
+ offset = dissect_epl_pres(convo, epl_tree, tvb, pinfo, offset);
break;
case EPL_SOA:
- offset = dissect_epl_soa(epl_tree, tvb, pinfo, epl_src, offset);
+ offset = dissect_epl_soa(epl_tree, tvb, pinfo, offset);
break;
case EPL_ASND:
- offset = dissect_epl_asnd(epl_tree, tvb, pinfo, epl_src, offset);
+ offset = dissect_epl_asnd(epl_tree, tvb, pinfo, offset);
break;
case EPL_AINV:
- offset = dissect_epl_ainv(epl_tree, tvb, pinfo, epl_src, offset);
+ offset = dissect_epl_ainv(epl_tree, tvb, pinfo, offset);
break;
case EPL_AMNI:
@@ -1877,11 +2617,10 @@ dissect_eplpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean udp
* there's nothing to dissect! Everything is given to the heuristic,
* which will dissect as data, if no heuristic dissector uses it. */
size = tvb_captured_length_remaining(tvb, offset);
- offset = dissect_epl_payload(epl_tree, tvb, pinfo, offset, size, EPL_AMNI);
+ offset = dissect_epl_payload(epl_tree, tvb, pinfo, offset, size, NULL, EPL_AMNI);
break;
- /* Default case can not happen as it is caught by an earlier switch
- * statement */
+ /* Switch cases are exhaustive. Default case never occurs */
}
@@ -1901,7 +2640,7 @@ dissect_epludp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _
}
-const gchar*
+static const gchar*
decode_epl_address (guchar adr)
{
const gchar *addr_str;
@@ -1914,7 +2653,7 @@ decode_epl_address (guchar adr)
}
else
{
- if (( adr < EPL_MN_NODEID) && (adr > EPL_DYNAMIC_NODEID))
+ if (EPL_IS_CN_NODEID(adr))
{
return addr_str_cn;
}
@@ -1926,35 +2665,78 @@ decode_epl_address (guchar adr)
}
static gint
-dissect_epl_payload ( proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, gint len, guint8 msgType )
+dissect_epl_payload(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, gint len, const struct epl_datatype *type, guint8 msgType)
{
gint rem_len = 0, payload_len = 0;
- tvbuff_t * payload_tvb = NULL;
+ tvbuff_t *payload_tvb = NULL;
heur_dtbl_entry_t *hdtbl_entry = NULL;
- proto_item * item = NULL;
+ proto_item *item = NULL;
+
+ if (len <= 0)
+ return offset;
+
+ rem_len = tvb_captured_length_remaining(tvb, offset);
+ payload_tvb = tvb_new_subset_length(tvb, offset, MIN(len, rem_len));
+ payload_len = tvb_captured_length_remaining(payload_tvb, 0);
+
+ if ( payload_len < len )
+ {
+ item = proto_tree_add_uint(epl_tree, hf_epl_payload_real, tvb, offset, payload_len, payload_len);
+ PROTO_ITEM_SET_GENERATED(item);
+ expert_add_info(pinfo, item, &ei_real_length_differs );
+ }
+
+ /* To satisfy heurstic dissectors, we need to pass then the whole PDO payload as-is,
+ * so we check whether we were called from dissect_epl_pdo and skip trying heuristic
+ * dissectors for the PDO's components
+ */
+ if (msgType != EPL_PREQ && msgType != EPL_PRES)
+ {
+ if ( dissector_try_heuristic(heur_epl_data_subdissector_list, payload_tvb, pinfo, epl_tree, &hdtbl_entry, &msgType))
+ return offset + payload_len;
+ }
- if (len > 0)
+ if (type && (!type->len || type->len == payload_len))
{
- rem_len = tvb_captured_length_remaining(tvb, offset);
- payload_tvb = tvb_new_subset_length(tvb, offset, MIN(len, rem_len));
- payload_len = tvb_captured_length_remaining(payload_tvb, 0);
- if ( payload_len < len )
+ if (*type->hf != hf_epl_od_uint)
{
- item = proto_tree_add_uint(epl_tree, hf_epl_payload_real, tvb, offset, payload_len, payload_len);
- PROTO_ITEM_SET_GENERATED(item);
- expert_add_info(pinfo, item, &ei_real_length_differs );
+ proto_tree_add_item(epl_tree, *type->hf, tvb, offset, type->len, type->encoding);
}
-
- if ( ! dissector_try_heuristic(heur_epl_data_subdissector_list, payload_tvb, pinfo, epl_tree, &hdtbl_entry, &msgType))
+ else
+ {
+ /* proto_tree_add_item would zero-pad our hex representation
+ * to full 64 bit, which looks kind of ugly, so we add the
+ * HEX part of BASE_DEC_HEX ourselves
+ */
+ guint64 val;
+ item = proto_tree_add_item_ret_uint64(epl_tree, *type->hf,
+ tvb, offset, type->len, type->encoding, &val);
+ proto_item_append_text(item, " (0x%.*" G_GINT64_MODIFIER "x)", 2*type->len, val);
+ }
+ }
+ /* If a mapping uses a type of fixed width that's not equal to
+ * the function argument's length, fallback to raw data dissector
+ */
+ else
+ {
+ /* We don't know the type, so let's use appropriate unsignedX */
+ if (payload_len < (int)sizeof (guint64) && interpret_untyped_as_le)
+ {
+ guint64 val;
+ item = proto_tree_add_item_ret_uint64(epl_tree, hf_epl_od_uint,
+ payload_tvb, 0, payload_len, ENC_LITTLE_ENDIAN, &val);
+ proto_item_append_text(item, " (0x%.*" G_GINT64_MODIFIER "x)", 2*payload_len, val);
+ }
+ else
+ {
call_data_dissector(payload_tvb, pinfo, epl_tree);
-
- offset += payload_len;
+ }
}
- return offset;
+ return offset + payload_len;
}
-gint
+static gint
dissect_epl_soc(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
nstime_t nettime;
@@ -1990,8 +2772,8 @@ dissect_epl_soc(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint of
-gint
-dissect_epl_preq(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
+static gint
+dissect_epl_preq(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint16 len;
guint8 pdoversion;
@@ -2021,15 +2803,15 @@ dissect_epl_preq(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint o
(EPL_PDO_RD_MASK & flags), hi_nibble(pdoversion), lo_nibble(pdoversion));
offset += 2;
- offset += dissect_epl_payload(epl_tree, tvb, pinfo, offset, len, EPL_PREQ );
+ offset = dissect_epl_pdo(convo, epl_tree, tvb, pinfo, offset, len, EPL_PREQ );
return offset;
}
-gint
-dissect_epl_pres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset)
+static gint
+dissect_epl_pres(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint16 len;
guint8 pdoversion;
@@ -2042,7 +2824,7 @@ dissect_epl_pres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
};
state = tvb_get_guint8(tvb, offset);
- if (epl_src != EPL_MN_NODEID) /* check if the sender is CN or MN */
+ if (pinfo->srcport != EPL_MN_NODEID) /* check if the sender is CN or MN */
{
proto_tree_add_item(epl_tree, hf_epl_pres_stat_cs, tvb, offset, 1, ENC_LITTLE_ENDIAN);
}
@@ -2075,7 +2857,7 @@ dissect_epl_pres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
(EPL_PDO_RD_MASK & flags), (EPL_PDO_RS_MASK & flags2), (EPL_PDO_PR_MASK & flags2) >> 3,
hi_nibble(pdoversion), lo_nibble(pdoversion));
- if (epl_src != EPL_MN_NODEID) /* check if the sender is CN or MN */
+ if (pinfo->srcport != EPL_MN_NODEID) /* check if the sender is CN or MN */
{
col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
val_to_str(state, epl_nmt_cs_vals, "Unknown(%d)"));
@@ -2088,14 +2870,14 @@ dissect_epl_pres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
offset += 2;
- offset += dissect_epl_payload ( epl_tree, tvb, pinfo, offset, len, EPL_PRES );
+ offset = dissect_epl_pdo(convo, epl_tree, tvb, pinfo, offset, len, EPL_PRES );
return offset;
}
-gint
-dissect_epl_soa(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset)
+static gint
+dissect_epl_soa(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint8 svid, target;
guint8 state;
@@ -2103,7 +2885,7 @@ dissect_epl_soa(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
proto_tree *psf_tree = NULL;
state = tvb_get_guint8(tvb, offset);
- if (epl_src != EPL_MN_NODEID) /* check if CN or MN */
+ if (pinfo->srcport != EPL_MN_NODEID) /* check if CN or MN */
{
proto_tree_add_item(epl_tree, hf_epl_soa_stat_cs, tvb, offset, 1, ENC_LITTLE_ENDIAN);
}
@@ -2134,7 +2916,7 @@ dissect_epl_soa(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
col_append_fstr(pinfo->cinfo, COL_INFO, "(%s)->%3d",
rval_to_str(svid, soa_svid_id_vals, "Unknown"), target);
- if (epl_src != EPL_MN_NODEID) /* check if CN or MN */
+ if (pinfo->srcport != EPL_MN_NODEID) /* check if CN or MN */
{
col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
val_to_str(state, epl_nmt_cs_vals, "Unknown(%d)"));
@@ -2209,8 +2991,8 @@ dissect_epl_soa(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
-gint
-dissect_epl_asnd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset)
+static gint
+dissect_epl_asnd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint8 svid;
gint size, reported_len;
@@ -2229,12 +3011,14 @@ dissect_epl_asnd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
switch (svid)
{
+ struct epl_convo *convo;
case EPL_ASND_IDENTRESPONSE:
- offset = dissect_epl_asnd_ires(epl_tree, tvb, pinfo, epl_src, offset);
+ convo = epl_get_convo(pinfo, CONVO_FOR_RESPONSE);
+ offset = dissect_epl_asnd_ires(convo, epl_tree, tvb, pinfo, offset);
break;
case EPL_ASND_STATUSRESPONSE:
- offset = dissect_epl_asnd_sres(epl_tree, tvb, pinfo, epl_src, offset);
+ offset = dissect_epl_asnd_sres(epl_tree, tvb, pinfo, offset);
break;
case EPL_ASND_NMTREQUEST:
@@ -2258,28 +3042,25 @@ dissect_epl_asnd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
next_tvb = tvb_new_subset_length_caplen(tvb, offset, size, reported_len);
/* Manufacturer specific entries for ASND services */
- if ( svid >= 0xA0 && svid < 0xFF )
- {
- if (! dissector_try_uint(epl_asnd_dissector_table, svid, next_tvb, pinfo,
- ( epl_tree != NULL ? epl_tree->parent : epl_tree ) ) )
- dissect_epl_payload ( epl_tree, tvb, pinfo, offset, size, EPL_ASND );
- } else {
- dissect_epl_payload ( epl_tree, tvb, pinfo, offset, size, EPL_ASND );
+ if (svid >= 0xA0 && svid < 0xFF && dissector_try_uint(epl_asnd_dissector_table,
+ svid, next_tvb, pinfo, ( epl_tree ? epl_tree->parent : NULL ))) {
+ break;
}
- break;
+
+ dissect_epl_payload(epl_tree, tvb, pinfo, offset, size, NULL, EPL_ASND);
}
return offset;
}
-gint
-dissect_epl_ainv(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset)
+static gint
+dissect_epl_ainv(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint8 svid;
proto_item *item;
proto_tree *subtree;
- if (epl_src != EPL_MN_NODEID) /* check if CN or MN */
+ if (pinfo->srcport != EPL_MN_NODEID) /* check if CN or MN */
{
proto_tree_add_item(epl_tree, hf_epl_soa_stat_cs, tvb, offset, 1, ENC_LITTLE_ENDIAN);
}
@@ -2303,12 +3084,14 @@ dissect_epl_ainv(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
switch (svid)
{
+ struct epl_convo *convo;
case EPL_ASND_IDENTRESPONSE:
- offset = dissect_epl_asnd_ires(epl_tree, tvb, pinfo, epl_src, offset);
+ convo = epl_get_convo(pinfo, CONVO_FOR_RESPONSE);
+ offset = dissect_epl_asnd_ires(convo, epl_tree, tvb, pinfo, offset);
break;
case EPL_ASND_STATUSRESPONSE:
- offset = dissect_epl_asnd_sres(epl_tree, tvb, pinfo, epl_src, offset);
+ offset = dissect_epl_asnd_sres(epl_tree, tvb, pinfo, offset);
break;
case EPL_ASND_NMTREQUEST:
@@ -2335,7 +3118,7 @@ dissect_epl_ainv(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8
}
-gint
+static gint
dissect_epl_asnd_nmtreq(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint8 rcid;
@@ -2354,7 +3137,7 @@ dissect_epl_asnd_nmtreq(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
return offset;
}
-gint
+static gint
dissect_epl_asnd_nmtdna(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
proto_item *ti_dna;
@@ -2405,7 +3188,7 @@ dissect_epl_asnd_nmtdna(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
}
-gint
+static gint
dissect_epl_asnd_nmtcmd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint8 epl_asnd_nmtcommand_cid;
@@ -2460,13 +3243,15 @@ dissect_epl_asnd_nmtcmd(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
-gint
-dissect_epl_asnd_ires(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset)
+static gint
+dissect_epl_asnd_ires(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
- guint16 profile,additional;
guint32 epl_asnd_identresponse_ipa, epl_asnd_identresponse_snm, epl_asnd_identresponse_gtw;
- proto_item *ti_feat;
+ proto_item *ti_feat, *ti;
proto_tree *epl_feat_tree;
+ guint16 device_type;
+ const char *profile_name = NULL;
+ guint32 response_time;
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_en, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_ec, tvb, offset, 1, ENC_LITTLE_ENDIAN);
@@ -2476,7 +3261,7 @@ dissect_epl_asnd_ires(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, g
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_rs, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
- if (epl_src != EPL_MN_NODEID) /* check if CN or MN */
+ if (pinfo->srcport != EPL_MN_NODEID) /* check if CN or MN */
{
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_stat_cs, tvb, offset, 1, ENC_LITTLE_ENDIAN);
}
@@ -2524,21 +3309,44 @@ dissect_epl_asnd_ires(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, g
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_pos, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
+ response_time = tvb_get_letohl(tvb, offset);
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_rst, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 6;
- profile = tvb_get_letohs(tvb, offset);
- additional = tvb_get_letohs(tvb, offset+2);
- proto_tree_add_string_format_value(epl_tree, hf_epl_asnd_identresponse_dt, tvb, offset,
- 4, "", "Profile %d (%s), Additional Information: 0x%4.4X",
- profile, val_to_str_const(profile, epl_device_profiles, "Unknown Profile"), additional);
+ device_type = tvb_get_letohs(tvb, offset);
+
+ if (device_type != convo->device_type)
+ convo = epl_get_convo(pinfo, CONVO_FOR_RESPONSE | CONVO_ALWAYS_CREATE);
+
+ convo->response_time = response_time;
+ convo->device_type = device_type;
+
+ ti = proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_dt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+
+ if (!convo->profile || !convo->profile->nodeid)
+ epl_update_convo_cn_profile(convo);
+ if (convo->profile && convo->profile->name)
+ profile_name = convo->profile->name;
+ if (!profile_name)
+ profile_name = val_to_str_const(convo->device_type, epl_device_profiles, "Unknown Profile");
+
+ proto_item_append_text(ti, " (%s)", profile_name);
+
+ proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_dt_add, tvb, offset+2, 2, ENC_LITTLE_ENDIAN);
+
+ if (convo->profile && convo->profile->path)
+ {
+ ti = proto_tree_add_string(epl_tree, hf_epl_asnd_identresponse_profile_path, tvb, offset, 2, convo->profile->path);
+ PROTO_ITEM_SET_GENERATED(ti);
+ }
- proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_profile, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 4;
+ convo->vendor_id = tvb_get_letohl(tvb, offset);
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_vid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
+ convo->product_code = tvb_get_letohl(tvb, offset);
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_productcode, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
@@ -2581,12 +3389,12 @@ dissect_epl_asnd_ires(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, g
proto_tree_add_item(epl_tree, hf_epl_asnd_identresponse_vex2, tvb, offset, 48, ENC_NA);
offset += 48;
- col_append_str(pinfo->cinfo, COL_INFO, val_to_str(profile, epl_device_profiles, "Device Profile %d"));
+ col_append_str(pinfo->cinfo, COL_INFO, val_to_str(convo->device_type, epl_device_profiles, "Device Profile %d"));
return offset;
}
-gint
+static gint
dissect_epl_asnd_resp(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo _U_, gint offset)
{
proto_item *psf_item = NULL;
@@ -2639,8 +3447,8 @@ dissect_epl_asnd_resp(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo _U
return offset;
}
-gint
-dissect_epl_asnd_sres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, guint8 epl_src, gint offset)
+static gint
+dissect_epl_asnd_sres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
proto_item *ti_el_entry, *ti_el_entry_type;
proto_tree *epl_seb_tree, *epl_el_tree, *epl_el_entry_tree, *epl_el_entry_type_tree;
@@ -2658,7 +3466,7 @@ dissect_epl_asnd_sres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, g
nmt_state = tvb_get_guint8(tvb, offset);
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(nmt_state, epl_nmt_cs_vals, "Unknown (%d)"));
- if (epl_src != EPL_MN_NODEID) /* check if CN or MN */
+ if (pinfo->srcport != EPL_MN_NODEID) /* check if CN or MN */
{
proto_tree_add_uint(epl_tree, hf_epl_asnd_statusresponse_stat_cs, tvb, offset, 1, nmt_state);
}
@@ -2727,11 +3535,12 @@ dissect_epl_asnd_sres(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, g
return offset;
}
-gint
+static gint
dissect_epl_asnd_sdo(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
{
guint16 seqnum = 0x00;
- offset = dissect_epl_sdo_sequence(epl_tree, tvb, pinfo, offset);
+ guint8 seq_read;
+ offset = dissect_epl_sdo_sequence(epl_tree, tvb, pinfo, offset, &seq_read);
seqnum = epl_get_sequence_nr(pinfo);
@@ -2740,15 +3549,15 @@ dissect_epl_asnd_sdo(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gi
{
if (tvb_reported_length_remaining(tvb, offset) > 0)
{
- offset = dissect_epl_sdo_command(epl_tree, tvb, pinfo, offset);
+ offset = dissect_epl_sdo_command(epl_tree, tvb, pinfo, offset, seq_read);
}
else col_append_str(pinfo->cinfo, COL_INFO, "Empty CommandLayer");
}
return offset;
}
-gint
-dissect_epl_sdo_sequence(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
+static gint
+dissect_epl_sdo_sequence(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8* seq)
{
guint8 seq_recv = 0x00, seq_send = 0x00, rcon = 0x00, scon = 0x00;
guint32 frame = 0x00;
@@ -2874,7 +3683,7 @@ dissect_epl_sdo_sequence(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo
proto_tree_add_uint(sod_seq_tree, hf_epl_asnd_sdo_seq_receive_con, tvb, offset, 1, seq_recv);
offset += 1;
- seq_send = tvb_get_guint8(tvb, offset);
+ *seq = seq_send = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(sod_seq_tree, hf_epl_asnd_sdo_seq_send_sequence_number, tvb, offset, 1, seq_send);
proto_tree_add_uint(sod_seq_tree, hf_epl_asnd_sdo_seq_send_con, tvb, offset, 1, seq_send);
@@ -2892,8 +3701,8 @@ dissect_epl_sdo_sequence(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo
return offset;
}
-gint
-dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset)
+static gint
+dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 seq)
{
gint payload_length;
guint8 segmented, command_id, transaction_id;
@@ -2904,6 +3713,7 @@ dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
proto_tree *sdo_cmd_tree = NULL;
proto_item *item;
guint8 sendCon = 0;
+ guint is_response = 0;
offset += 1;
@@ -2930,7 +3740,7 @@ dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
proto_tree_add_item(sdo_cmd_tree, hf_epl_asnd_sdo_cmd_transaction_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
- proto_tree_add_item(sdo_cmd_tree, hf_epl_asnd_sdo_cmd_response, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item_ret_uint(sdo_cmd_tree, hf_epl_asnd_sdo_cmd_response, tvb, offset, 1, ENC_LITTLE_ENDIAN, &is_response);
proto_tree_add_item(sdo_cmd_tree, hf_epl_asnd_sdo_cmd_abort, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_tree_add_item(sdo_cmd_tree, hf_epl_asnd_sdo_cmd_segmentation, tvb, offset, 1, ENC_LITTLE_ENDIAN);
@@ -3000,6 +3810,7 @@ dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
/* the SDO response can contain several abort codes for multiple transfers */
while (remlength > 0)
{
+ /* TODO enchance Index and SubIndex with string representation */
proto_tree_add_item(sdo_cmd_tree, hf_epl_asnd_sdo_cmd_data_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
@@ -3028,22 +3839,26 @@ dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
}
else
{
+ int opts = is_response ? CONVO_FOR_RESPONSE : CONVO_FOR_REQUEST;
+ struct epl_convo *convo = epl_get_convo(pinfo, opts);
+ convo->seq_send = seq;
+
switch (command_id)
{
case EPL_ASND_SDO_COMMAND_WRITE_BY_INDEX:
- offset = dissect_epl_sdo_command_write_by_index(sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
+ offset = dissect_epl_sdo_command_write_by_index(convo, sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
break;
case EPL_ASND_SDO_COMMAND_WRITE_MULTIPLE_PARAMETER_BY_INDEX:
- offset = dissect_epl_sdo_command_write_multiple_by_index(sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
+ offset = dissect_epl_sdo_command_write_multiple_by_index(convo, sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
break;
case EPL_ASND_SDO_COMMAND_READ_MULTIPLE_PARAMETER_BY_INDEX:
- offset = dissect_epl_sdo_command_read_multiple_by_index(sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
+ offset = dissect_epl_sdo_command_read_multiple_by_index(convo, sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
break;
case EPL_ASND_SDO_COMMAND_READ_BY_INDEX:
- offset = dissect_epl_sdo_command_read_by_index(sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
+ offset = dissect_epl_sdo_command_read_by_index(convo, sdo_cmd_tree, tvb, pinfo, offset, segmented, response, segment_size);
break;
default:
@@ -3054,19 +3869,22 @@ dissect_epl_sdo_command(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo,
return offset;
}
-gint
-dissect_epl_sdo_command_write_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
+static gint
+dissect_epl_sdo_command_write_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
{
gint size, payload_length = 0;
- guint16 idx = 0x00, nosub = 0x00, sod_index = 0x00, error = 0xFF, entries = 0x00, sub_val = 0x00;
+ guint16 idx = 0x00, sod_index = 0xFF, error = 0xFF, sub_val = 0x00;
+ gboolean nosub = FALSE;
guint8 subindex = 0x00;
guint32 fragmentId = 0;
guint32 frame = 0;
gboolean end_segment = FALSE;
proto_item *psf_item, *cmd_payload;
- proto_tree *psf_tree, *payload_tree;
+ proto_tree *payload_tree;
const gchar *index_str, *sub_str, *sub_index_str;
fragment_head *frag_msg = NULL;
+ struct object *obj = NULL;
+ struct subobject *subobj = NULL;
/* get the current frame number */
frame = pinfo->num;
@@ -3080,31 +3898,46 @@ dissect_epl_sdo_command_write_by_index(proto_tree *epl_tree, tvbuff_t *tvb, pack
idx = tvb_get_letohs(tvb, offset);
/* add index item */
psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- /* value to string */
- index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
- /* get index string value */
- sod_index = str_to_val(index_str, sod_cmd_str_val, error);
- /* get subindex string */
- sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
- /* get subindex string value*/
- nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub,error);
+ /* look up index in registered profile */
+ obj = object_lookup(convo->profile, idx);
+ if (!obj)
+ {
+ /* value to string */
+ index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
+ /* get index string value */
+ sod_index = str_to_val(index_str, sod_cmd_str_val, error);
+ /* get subindex string */
+ sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
+ /* get subindex string value */
+ nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub, 0xFF) != 0xFF;
+ }
offset += 2;
+
/* get subindex offset */
subindex = tvb_get_guint8(tvb, offset);
+ subobj = subobject_lookup(obj, subindex);
+
+
/* get subindex string */
- sub_str = val_to_str_ext_const(idx, &sod_cmd_sub_str, "unknown");
+ sub_str = val_to_str_ext_const(subindex, &sod_cmd_sub_str, "unknown");
/* get string value */
- sub_val = str_to_val(sub_str, sod_cmd_sub_str_val,error);
+ sub_val = str_to_val(sub_str, sod_cmd_sub_str_val, error);
col_append_fstr(pinfo->cinfo, COL_INFO, "%s[%d]: (0x%04X/%d)",
val_to_str_ext(EPL_ASND_SDO_COMMAND_WRITE_BY_INDEX, &epl_sdo_asnd_commands_short_ext, "Command(%02X)"),
segment_size, idx, subindex);
- /* string is in list */
- if(sod_index != error)
+ if (obj || sod_index == error)
+ {
+ const char *name = obj ? obj->info.name : val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined");
+ proto_item_append_text(psf_item, " (%s)", name);
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%s", name);
+ if (obj) nosub = obj->info.type_class == OD_ENTRY_SCALAR;
+ }
+ else /* string is in list */
{
- /* add ondex string to index item */
+ /* add index string to index item */
proto_item_append_text(psf_item," (%s", val_to_str_ext_const(((guint32)(sod_index<<16)), &sod_index_names, "User Defined"));
proto_item_append_text(psf_item,"_%02Xh", (idx-sod_index));
if(sod_index == EPL_SOD_PDO_RX_MAPP || sod_index == EPL_SOD_PDO_TX_MAPP)
@@ -3128,19 +3961,20 @@ dissect_epl_sdo_command_write_by_index(proto_tree *epl_tree, tvbuff_t *tvb, pack
}
idx = sod_index;
}
- else
- {
- proto_item_append_text(psf_item," (%s)", val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined"));
- col_append_fstr(pinfo->cinfo, COL_INFO, " (%s", val_to_str_ext_const(((guint32) (idx << 16)), &sod_index_names, "User Defined"));
- }
if(sub_val != error)
idx = sub_val;
+ if (subobj)
+ {
+ psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(psf_item, " (%s)", subobj->info.name);
+ col_append_fstr(pinfo->cinfo, COL_INFO, "/%s)", subobj->info.name);
+ }
/* if the subindex is a EPL_SOD_STORE_PARAM */
/* if the subindex is a EPL_SOD_RESTORE_PARAM */
- if((idx == EPL_SOD_STORE_PARAM && subindex <= 0x7F && subindex >= 0x04) ||
- (idx == EPL_SOD_RESTORE_PARAM && subindex <= 0x7F && subindex >= 0x04))
+ else if((idx == EPL_SOD_STORE_PARAM && subindex <= 0x7F && subindex >= 0x04) ||
+ (idx == EPL_SOD_RESTORE_PARAM && subindex <= 0x7F && subindex >= 0x04))
{
psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (ManufacturerParam_%02Xh_U32)", subindex);
@@ -3149,19 +3983,18 @@ dissect_epl_sdo_command_write_by_index(proto_tree *epl_tree, tvbuff_t *tvb, pack
/* if the subindex is a EPL_SOD_PDO_RX_MAPP */
/* if the subindex is a EPL_SOD_PDO_TX_MAPP */
else if((idx == EPL_SOD_PDO_RX_MAPP && subindex >= 0x01 && subindex <= 0xfe) ||
- (idx == EPL_SOD_PDO_TX_MAPP && subindex >= 0x01 && subindex <= 0xfe))
+ (idx == EPL_SOD_PDO_TX_MAPP && subindex >= 0x01 && subindex <= 0xfe))
{
psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (ObjectMapping)");
col_append_fstr(pinfo->cinfo, COL_INFO, "/ObjectMapping)");
}
/* no subindex */
- else if(nosub != error)
+ else if(nosub)
{
col_append_fstr(pinfo->cinfo, COL_INFO, ")");
}
- /* if the subindex has the value 0x00 */
- else if(subindex == entries)
+ else if(subindex == 0x00)
{
psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (NumberOfEntries)");
@@ -3261,26 +4094,26 @@ dissect_epl_sdo_command_write_by_index(proto_tree *epl_tree, tvbuff_t *tvb, pack
}
size = tvb_reported_length_remaining(tvb, offset);
+
/* if the frame is a PDO Mapping and the subindex is bigger than 0x00 */
- if((idx == EPL_SOD_PDO_TX_MAPP && subindex > entries) ||(idx == EPL_SOD_PDO_RX_MAPP && subindex > entries))
+ if((idx == EPL_SOD_PDO_TX_MAPP && subindex > 0x00) || (idx == EPL_SOD_PDO_RX_MAPP && subindex > 0x00))
{
- psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_mapping, tvb, offset, 1, ENC_NA);
- psf_tree = proto_item_add_subtree(psf_item, ett_epl_asnd_sdo_cmd_data_mapping);
- idx = tvb_get_letohs(tvb, offset);
- proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_index, tvb, offset, 2, idx,"Index: 0x%04X", idx);
- offset += 2;
- idx = tvb_get_letohs(tvb, offset);
- proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_subindex, tvb, offset, 1, idx,"SubIndex: 0x%02X", idx);
- offset += 2;
- idx = tvb_get_letohs(tvb, offset);
- proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_offset, tvb, offset, 2, idx,"Offset: 0x%04X", idx);
- offset += 2;
- proto_tree_add_item(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- offset += 2;
+ wmem_array_t *mappings = NULL;
+ if (use_sdo_mappings)
+ mappings = idx == EPL_SOD_PDO_TX_MAPP ? convo->TPDO : convo->RPDO;
+
+ offset = dissect_object_mapping(convo->profile, mappings, epl_tree, tvb, pinfo->num, offset, idx, subindex);
}
else
{
- offset += dissect_epl_payload ( epl_tree, tvb, pinfo, offset, size, EPL_ASND );
+ /* dissect the payload */
+ const struct epl_datatype *type = NULL;
+ if (subobj)
+ type = subobj->info.type;
+ else if (obj)
+ type = obj->info.type;
+
+ offset = dissect_epl_payload(epl_tree, tvb, pinfo, offset, size, type, EPL_ASND);
}
}
else
@@ -3291,17 +4124,104 @@ dissect_epl_sdo_command_write_by_index(proto_tree *epl_tree, tvbuff_t *tvb, pack
return offset;
}
-gint
-dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
+/* epl_tree may be null here, when this function is called from the profile parser */
+static gint
+dissect_object_mapping(struct profile *profile, wmem_array_t *mappings, proto_tree *epl_tree, tvbuff_t *tvb, guint32 framenum, gint offset, guint16 idx, guint8 subindex)
+{
+ proto_item *ti_obj, *ti_subobj, *psf_item;
+ proto_tree *psf_tree;
+ struct object_mapping map = {0};
+ struct object *mapping_obj;
+ int *ett;
+ struct subobject *mapping_subobj;
+ gboolean nosub = FALSE;
+
+ /* If we don't populate the tree or record mappings, skip over it */
+ if (!epl_tree && !mappings)
+ return offset + EPL_OBJECT_MAPPING_SIZE;
+
+ map.param.idx = idx;
+ map.param.subindex = subindex;
+ map.frame.first = framenum;
+ map.frame.last = G_MAXUINT32;
+
+ psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_mapping, tvb, offset, 1, ENC_NA);
+ psf_tree = proto_item_add_subtree(psf_item, ett_epl_asnd_sdo_cmd_data_mapping);
+
+ map.pdo.idx = tvb_get_letohs(tvb, offset);
+ ti_obj = proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_index, tvb, offset, 2, map.pdo.idx,"Index: 0x%04X", map.pdo.idx);
+ offset += 2;
+
+ map.pdo.subindex = tvb_get_guint8(tvb, offset);
+ ti_subobj = proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_subindex, tvb, offset, 1, map.pdo.subindex, "SubIndex: 0x%02X", map.pdo.subindex);
+ offset += 2;
+
+ /* look up index in registered profiles */
+ if ((mapping_obj = object_lookup(profile, map.pdo.idx)))
+ {
+ if (!map.pdo.subindex && mapping_obj->info.type_class == OD_ENTRY_SCALAR)
+ nosub = TRUE;
+
+ map.info = &mapping_obj->info;
+ map.index_name = map.info->name;
+ proto_item_append_text (ti_obj, " (%s)", map.info->name);
+
+ mapping_subobj = subobject_lookup(mapping_obj, map.pdo.subindex);
+ if (mapping_subobj)
+ {
+ map.info = &mapping_subobj->info;
+ proto_item_append_text (ti_subobj, " (%s)", map.info->name);
+ }
+ else
+ {
+ PROTO_ITEM_SET_HIDDEN(ti_subobj);
+ }
+ }
+
+ map.bit_offset = tvb_get_letohs(tvb, offset);
+ proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_offset, tvb, offset, 2, map.bit_offset,"Offset: 0x%04X", map.bit_offset);
+ offset += 2;
+
+ map.no_of_bits = tvb_get_guint8(tvb, offset);
+ psf_item = proto_tree_add_item(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(psf_item, " bits");
+ offset += 2;
+
+ map.ett = -1;
+ ett = &map.ett;
+ /* We leak an ett entry every time we destruct a mapping
+ * Not sure what to do about that
+ */
+ proto_register_subtree_array(&ett, 1);
+
+ if (mappings)
+ {
+ /* TODO One could think of a better string here? */
+ if (nosub)
+ map.title = g_strdup_printf("PDO - %04X", map.pdo.idx);
+ else
+ map.title = g_strdup_printf("PDO - %04X:%02X", map.pdo.idx, map.pdo.subindex);
+
+ add_object_mapping(mappings, &map);
+ }
+
+ return offset;
+}
+
+static gint
+dissect_epl_sdo_command_write_multiple_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
{
gint dataoffset;
guint8 subindex = 0x00, padding = 0x00;
- guint16 idx = 0x00, sod_index = 0x00, nosub = 0x00 ,error = 0xFF, entries = 0x00, sub_val = 0x00;
+ guint16 idx = 0x00, error = 0xFF, sub_val = 0x00;
+ gboolean nosub = FALSE;
guint32 size, offsetincrement, datalength, remlength, objectcnt, abort_code = 0;
gboolean lastentry = FALSE, is_abort = FALSE;
const gchar *index_str, *sub_str, *sub_index_str;
proto_item *psf_item;
- proto_tree *psf_tree, *psf_od_tree;
+ proto_tree *psf_od_tree;
+ struct object *obj = NULL;
+ struct subobject *subobj = NULL;
/* Offset is calculated simply by only applying EPL payload offset, not packet offset.
@@ -3325,6 +4245,7 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
* we now use length_remaining, but it is kept to be on the safe side. */
while ( !lastentry && remlength > 0 )
{
+ guint16 sod_index = error;
offsetincrement = tvb_get_letohl(tvb, offset);
@@ -3382,16 +4303,27 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
idx = tvb_get_letohs(tvb, dataoffset);
/* add index item */
psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_index, tvb, offset+4, 2, ENC_LITTLE_ENDIAN);
- /* value to string */
- index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
- /* get index string value */
- sod_index = str_to_val(index_str, sod_cmd_str_val, error);
- /* get subindex string */
- sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
- /* get subindex string value*/
- nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub,error);
+ /* Check profile for name */
+ obj = object_lookup(convo->profile, idx);
+ if (!obj)
+ {
+ /* value to string */
+ index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
+ /* get index string value */
+ sod_index = str_to_val(index_str, sod_cmd_str_val, error);
+
+ /* get subindex string */
+ sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
+ /* get subindex string value*/
+ nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub, 0xFF) != 0xFF;
+ }
- if(sod_index != error)
+ if(sod_index == error)
+ {
+ const char *name = obj ? obj->info.name :val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined");
+ proto_item_append_text(psf_item," (%s)", name);
+ }
+ else
{
/* add index string */
proto_item_append_text(psf_item," (%s", val_to_str_ext_const(((guint32)(sod_index<<16)), &sod_index_names, "User Defined"));
@@ -3405,10 +4337,6 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
proto_item_append_text(psf_item,"_REC)");
}
}
- else
- {
- proto_item_append_text(psf_item," (%s)", val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined"));
- }
if (objectcnt < 8)
col_append_fstr(pinfo->cinfo, COL_INFO, " (0x%04X", idx);
@@ -3424,6 +4352,7 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
/* get subindex offset */
subindex = tvb_get_guint8(tvb, dataoffset);
+ subobj = subobject_lookup(obj, subindex);
proto_item_append_text(psf_od_tree, " SubIdx: 0x%02X", subindex);
/* get subindex string */
sub_str = val_to_str_ext_const(idx, &sod_cmd_sub_str, "unknown");
@@ -3433,8 +4362,13 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
if(sub_val != error)
idx = sub_val;
+ if (subobj)
+ {
+ psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, dataoffset, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(psf_item, " (%s)", subobj->info.name);
+ }
/* if the subindex is a EPL_SOD_STORE_PARAM */
- if(idx == EPL_SOD_STORE_PARAM && subindex <= 0x7F && subindex >= 0x04)
+ else if(idx == EPL_SOD_STORE_PARAM && subindex <= 0x7F && subindex >= 0x04)
{
psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, dataoffset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (ManufacturerParam_%02Xh_U32)", subindex);
@@ -3458,7 +4392,7 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
proto_item_append_text(psf_item, " (ObjectMapping)");
}
/* if the subindex has the value 0x00 */
- else if(subindex == entries)
+ else if(subindex == 0x00)
{
psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, dataoffset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (NumberOfEntries)");
@@ -3473,7 +4407,7 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
/* info text */
if (objectcnt < 8)
{
- if (nosub != error)
+ if (nosub)
/* no subindex */
col_append_fstr(pinfo->cinfo, COL_INFO, ")");
else
@@ -3492,26 +4426,22 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
PROTO_ITEM_SET_GENERATED(psf_item);
/* if the frame is a PDO Mapping and the subindex is bigger than 0x00 */
- if((idx == EPL_SOD_PDO_TX_MAPP && subindex > entries) ||(idx == EPL_SOD_PDO_RX_MAPP && subindex > entries))
+ if((idx == EPL_SOD_PDO_TX_MAPP && subindex > 0x00) ||(idx == EPL_SOD_PDO_RX_MAPP && subindex > 0x00))
{
- psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_mapping, tvb, dataoffset, 1, ENC_NA);
- psf_tree = proto_item_add_subtree(psf_item, ett_epl_asnd_sdo_cmd_data_mapping);
- idx = tvb_get_letohs(tvb, dataoffset);
- proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_index, tvb, dataoffset, 2, idx,"Index: 0x%04X", idx);
- dataoffset += 2;
- idx = tvb_get_letohs(tvb, dataoffset);
- proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_subindex, tvb, dataoffset, 1, idx,"SubIndex: 0x%02X", idx);
- dataoffset += 2;
- idx = tvb_get_letohs(tvb, dataoffset);
- proto_tree_add_uint_format(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_offset, tvb, dataoffset, 2, idx,"Offset: 0x%04X", idx);
- dataoffset += 2;
- proto_tree_add_item(psf_tree, hf_epl_asnd_sdo_cmd_data_mapping_length, tvb, dataoffset, 2, ENC_LITTLE_ENDIAN);
- /*dataoffset += 2;*/
+ wmem_array_t *mappings = NULL;
+ if (use_sdo_mappings)
+ mappings = idx == EPL_SOD_PDO_TX_MAPP ? convo->TPDO : convo->RPDO;
+ dissect_object_mapping(convo->profile, mappings, epl_tree, tvb, pinfo->num, dataoffset, idx, subindex);
}
- else
+ else /* dissect the payload */
{
- /* dissect the payload */
- dissect_epl_payload ( psf_od_tree, tvb, pinfo, dataoffset, size, EPL_ASND);
+ const struct epl_datatype *type = NULL;
+ if (subobj)
+ type = subobj->info.type;
+ else if (obj)
+ type = obj->info.type;
+
+ dissect_epl_payload(psf_od_tree, tvb, pinfo, dataoffset, size, type, EPL_ASND);
}
offset += datalength;
@@ -3538,6 +4468,7 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
* we start the loop. */
while ( remlength > 0 )
{
+ guint16 sod_index = error;
if ((tvb_get_guint8 ( tvb, offset + 3 ) & 0x80) == 0x80)
is_abort = TRUE;
@@ -3578,7 +4509,7 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
/* info text */
if (objectcnt < 8)
{
- if (nosub != error)
+ if (nosub)
/* no subindex */
col_append_fstr(pinfo->cinfo, COL_INFO, ")");
else
@@ -3617,19 +4548,21 @@ dissect_epl_sdo_command_write_multiple_by_index(proto_tree *epl_tree, tvbuff_t *
}
return offset;
}
-
-gint
-dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
+static gint
+dissect_epl_sdo_command_read_multiple_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
{
gint dataoffset;
guint8 subindex = 0x00, padding = 0x00;
- guint16 idx = 0x00, sod_index = 0x00, nosub = 0x00 ,error = 0xFF, entries = 0x00, sub_val = 0x00;
+ guint16 idx = 0x00, error = 0xFF, sub_val = 0x00;
+ gboolean nosub = FALSE;
guint32 size, offsetincrement, datalength, remlength, objectcnt, abort_code;
gboolean lastentry = FALSE, is_abort = FALSE;
const gchar *index_str, *sub_str, *sub_index_str;
proto_item *psf_item, *psf_od_item;
proto_tree *psf_tree, *psf_od_tree;
-
+ struct object *obj = NULL;
+ struct subobject *subobj = NULL;
+ const char *name;
/* Offset is calculated simply by only applying EPL payload offset, not packet offset.
* The packet offset is 16, as this is the number of bytes trailing the SDO payload.
@@ -3651,6 +4584,7 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
* we now use length_remaining, but it is kept to be on the safe side. */
while ( !lastentry && remlength > 0 )
{
+ guint16 sod_index = error;
offsetincrement = tvb_get_letohl(tvb, offset);
@@ -3709,18 +4643,31 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
{
/* get SDO index value */
idx = tvb_get_letohs(tvb, dataoffset);
+ obj = object_lookup(convo->profile, idx);
+ if (!obj)
+ {
+ /* value to string */
+ index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
+ /* get index string value */
+ sod_index = str_to_val(index_str, sod_cmd_str_val, error);
+ /* get subindex string */
+ sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
+ /* get subindex string value*/
+ nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub, 0xFF) != 0xFF;
+ }
/* add index item */
psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_index, tvb, offset+4, 2, ENC_LITTLE_ENDIAN);
- /* value to string */
- index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
- /* get index string value */
- sod_index = str_to_val(index_str, sod_cmd_str_val, error);
- /* get subindex string */
- sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
- /* get subindex string value*/
- nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub,error);
- if(sod_index != error)
+ if(obj)
+ {
+ proto_item_append_text(psf_item, " (%s)", obj->info.name);
+ nosub = obj->info.type_class == OD_ENTRY_SCALAR;
+ }
+ else if(sod_index == error)
+ {
+ proto_item_append_text(psf_item," (%s)", val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined"));
+ }
+ else
{
/* add index string */
proto_item_append_text(psf_item," (%s", val_to_str_ext_const(((guint32)(sod_index<<16)), &sod_index_names, "User Defined"));
@@ -3734,10 +4681,6 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
proto_item_append_text(psf_item,"_REC)");
}
}
- else
- {
- proto_item_append_text(psf_item," (%s)", val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined"));
- }
if (objectcnt < 8)
col_append_fstr(pinfo->cinfo, COL_INFO, " (0x%04X", idx);
@@ -3753,6 +4696,7 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
/* get subindex offset */
subindex = tvb_get_guint8(tvb, dataoffset);
+ subobj = subobject_lookup(obj, subindex);
proto_item_append_text(psf_od_tree, " SubIdx: 0x%02X", subindex);
/* get subindex string */
sub_str = val_to_str_ext_const(idx, &sod_cmd_sub_str, "unknown");
@@ -3762,8 +4706,13 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
if(sub_val != error)
idx = sub_val;
+ if (subobj)
+ {
+ psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, dataoffset, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(psf_item, " (%s)", subobj->info.name);
+ }
/* if the subindex is a EPL_SOD_STORE_PARAM */
- if(idx == EPL_SOD_STORE_PARAM && subindex <= 0x7F && subindex >= 0x04)
+ else if(idx == EPL_SOD_STORE_PARAM && subindex <= 0x7F && subindex >= 0x04)
{
psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, dataoffset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (ManufacturerParam_%02Xh_U32)", subindex);
@@ -3786,8 +4735,7 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, dataoffset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (ObjectMapping)");
}
- /* if the subindex has the value 0x00 */
- else if(subindex == entries)
+ else if(subindex == 0x00)
{
psf_item = proto_tree_add_item(psf_od_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, dataoffset, 1, ENC_LITTLE_ENDIAN);
proto_item_append_text(psf_item, " (NumberOfEntries)");
@@ -3802,7 +4750,7 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
/* info text */
if (objectcnt < 8)
{
- if (nosub != error)
+ if (nosub)
/* no subindex */
col_append_fstr(pinfo->cinfo, COL_INFO, ")");
else
@@ -3833,7 +4781,7 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
else
{
/* if the frame is a PDO Mapping and the subindex is bigger than 0x00 */
- if((idx == EPL_SOD_PDO_TX_MAPP && subindex > entries) ||(idx == EPL_SOD_PDO_RX_MAPP && subindex > entries))
+ if((idx == EPL_SOD_PDO_TX_MAPP && subindex > 0x00) ||(idx == EPL_SOD_PDO_RX_MAPP && subindex > 0x00))
{
psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_mapping, tvb, dataoffset, 1, ENC_NA);
psf_tree = proto_item_add_subtree(psf_item, ett_epl_asnd_sdo_cmd_data_mapping);
@@ -3851,7 +4799,13 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
else
{
/* dissect the payload */
- dissect_epl_payload ( psf_od_tree, tvb, pinfo, dataoffset, size, EPL_ASND);
+ const struct epl_datatype *type = NULL;
+ if (subobj)
+ type = subobj->info.type;
+ else if (obj)
+ type = obj->info.type;
+
+ dissect_epl_payload ( psf_od_tree, tvb, pinfo, dataoffset, size, type, EPL_ASND);
}
}
@@ -3879,6 +4833,8 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
* we start the loop. */
while ( remlength > 0 )
{
+ guint16 sod_index = error;
+ proto_tree *psf_entry;
/* add object subtree */
psf_od_item = proto_tree_add_subtree(epl_tree, tvb, offset, 4, 0, NULL, "OD");
@@ -3886,14 +4842,18 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
{
/* get SDO index value */
idx = tvb_get_letohs(tvb, dataoffset);
- /* value to string */
- index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
- /* get index string value */
- sod_index = str_to_val(index_str, sod_cmd_str_val, error);
- /* get subindex string */
- sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
- /* get subindex string value*/
- nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub,error);
+ obj = object_lookup(convo->profile, idx);
+ if (!obj)
+ {
+ /* value to string */
+ index_str = rval_to_str_const(idx, sod_cmd_str, "unknown");
+ /* get index string value */
+ sod_index = str_to_val(index_str, sod_cmd_str_val, error);
+ /* get subindex string */
+ sub_index_str = val_to_str_ext_const(idx, &sod_cmd_no_sub, "unknown");
+ /* get subindex string value*/
+ nosub = str_to_val(sub_index_str, sod_cmd_str_no_sub,0xFF) != 0xFF;
+ }
if (objectcnt < 8)
col_append_fstr(pinfo->cinfo, COL_INFO, " (0x%04X", idx);
@@ -3904,19 +4864,49 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
idx = sod_index;
proto_item_append_text(psf_od_item, " Idx: 0x%04X", idx);
- proto_tree_add_uint_format(psf_od_item, hf_epl_asnd_sdo_cmd_data_mapping_index, tvb, dataoffset, 2, idx,"Index: 0x%04X", idx);
+ psf_entry = proto_tree_add_uint_format(psf_od_item, hf_epl_asnd_sdo_cmd_data_mapping_index, tvb, dataoffset, 2, idx,"Index: 0x%04X", idx);
+
+ if(obj)
+ {
+ proto_item_append_text(psf_entry, " (%s)", obj->info.name);
+ nosub = obj->info.type_class == OD_ENTRY_SCALAR;
+ }
+ else if(sod_index == error)
+ {
+ name = obj ? obj->info.name :val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined");
+ proto_item_append_text(psf_entry," (%s)", name);
+ }
+ else
+ {
+ /* add index string */
+ proto_item_append_text(psf_entry," (%s", val_to_str_ext_const(((guint32)(sod_index<<16)), &sod_index_names, "User Defined"));
+ proto_item_append_text(psf_entry,"_%02Xh", (idx-sod_index));
+ if(sod_index == EPL_SOD_PDO_RX_MAPP || sod_index == EPL_SOD_PDO_TX_MAPP)
+ {
+ proto_item_append_text(psf_entry,"_AU64)");
+ }
+ else
+ {
+ proto_item_append_text(psf_entry,"_REC)");
+ }
+ }
+
dataoffset += 2;
/* get subindex offset */
subindex = tvb_get_guint8(tvb, dataoffset);
proto_item_append_text(psf_od_item, " SubIdx: 0x%02X", subindex);
- proto_tree_add_uint_format(psf_od_item, hf_epl_asnd_sdo_cmd_data_mapping_subindex, tvb, dataoffset, 1, subindex,"SubIndex: 0x%02X", subindex);
+ psf_item = proto_tree_add_uint_format(psf_od_item, hf_epl_asnd_sdo_cmd_data_mapping_subindex, tvb, dataoffset, 1, subindex,"SubIndex: 0x%02X", subindex);
+ subobj = subobject_lookup(obj, subindex);
+ name = subobj ? subobj->info.name
+ : val_to_str_ext_const((subindex|(idx<<16)), &sod_index_names, "User Defined");
+ proto_item_append_text(psf_item, " (%s)", name);
/* info text */
if (objectcnt < 8)
{
- if (nosub != error)
+ if (nosub)
/* no subindex */
col_append_fstr(pinfo->cinfo, COL_INFO, ")");
else
@@ -3940,8 +4930,8 @@ dissect_epl_sdo_command_read_multiple_by_index(proto_tree *epl_tree, tvbuff_t *t
return offset;
}
-gint
-dissect_epl_sdo_command_read_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
+static gint
+dissect_epl_sdo_command_read_by_index(struct epl_convo *convo, proto_tree *epl_tree, tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 segmented, gboolean response, guint16 segment_size)
{
gint size, payload_length;
guint16 idx = 0x00;
@@ -3951,20 +4941,33 @@ dissect_epl_sdo_command_read_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packe
proto_tree *payload_tree;
gboolean end_segment = FALSE;
fragment_head *frag_msg = NULL;
+ struct object *obj = NULL;
+ struct subobject *subobj = NULL;
+ struct read_req *req;
+ const struct epl_datatype *type = NULL;
/* get the current frame number */
frame = pinfo->num;
if (!response)
{ /* request */
+ const char *name;
idx = tvb_get_letohs(tvb, offset);
psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- proto_item_append_text(psf_item," (%s)", val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined" ));
+ obj = object_lookup(convo->profile, idx);
+
+ name = obj ? obj->info.name : val_to_str_ext_const(((guint32)(idx<<16)), &sod_index_names, "User Defined");
+ proto_item_append_text(psf_item," (%s)", name);
offset += 2;
+
subindex = tvb_get_guint8(tvb, offset);
psf_item = proto_tree_add_item(epl_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, offset, 1, ENC_LITTLE_ENDIAN);
- proto_item_append_text(psf_item, " (%s)", val_to_str_ext_const((subindex|(idx<<16)), &sod_index_names, "User Defined"));
+ subobj = subobject_lookup(obj, subindex);
+
+ name = subobj ? subobj->info.name
+ : val_to_str_ext_const((subindex|(idx<<16)), &sod_index_names, "User Defined");
+ proto_item_append_text(psf_item, " (%s)", name);
offset += 1;
@@ -3973,6 +4976,21 @@ dissect_epl_sdo_command_read_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packe
segment_size, idx, subindex);
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s", val_to_str_ext_const(((guint32) (idx << 16)), &sod_index_names, "User Defined"));
col_append_fstr(pinfo->cinfo, COL_INFO, "/%s)",val_to_str_ext_const((subindex|(idx<<16)), &sod_index_names, "User Defined"));
+
+ /* Cache object for read in next response */
+ req = convo_read_req_set(convo, convo->seq_send);
+ req->idx = idx;
+ req->subindex = subindex;
+ if (obj)
+ {
+ req->info = subobj ? &subobj->info : &obj->info;
+ req->index_name = obj->info.name;
+ }
+ else
+ {
+ req->info = NULL;
+ req->index_name = NULL;
+ }
}
else
{
@@ -4047,12 +5065,80 @@ dissect_epl_sdo_command_read_by_index(proto_tree *epl_tree, tvbuff_t *tvb, packe
col_append_str(pinfo->cinfo, COL_INFO, "Response");
size = tvb_reported_length_remaining(tvb, offset);
- offset = dissect_epl_payload ( epl_tree, tvb, pinfo, offset, size, EPL_ASND );
+
+ /* Did we register the read req? */
+
+ if ((req = convo_read_req_get(convo, pinfo, convo->seq_send)))
+ {
+ proto_item *ti;
+ ti = proto_tree_add_uint_format_value(epl_tree, hf_epl_asnd_sdo_cmd_data_index, tvb, 0, 0, req->idx, "%04X", req->idx);
+ PROTO_ITEM_SET_GENERATED(ti);
+ if (req->info)
+ {
+ proto_item_append_text (ti, " (%s)", req->index_name);
+ type = req->info->type;
+ }
+
+ ti = proto_tree_add_uint_format_value(epl_tree, hf_epl_asnd_sdo_cmd_data_subindex, tvb, 0, 0, req->subindex, "%02X", req->subindex);
+ PROTO_ITEM_SET_GENERATED(ti);
+
+ if (req->info && req->info->name != req->index_name)
+ proto_item_append_text (ti, " (%s)", req->info->name);
+
+ }
+
+ offset = dissect_epl_payload(epl_tree, tvb, pinfo, offset, size, type, EPL_ASND);
}
return offset;
}
+static gboolean epl_profile_uat_fld_fileopen_check_cb(void *, const char *path, guint len, const void *, const void *, char **err);
+
+struct profile *profile_load(wmem_allocator_t *allocator, const char *path)
+{
+ struct profile *profile = NULL;
+ char *err;
+ if (!epl_profile_uat_fld_fileopen_check_cb(NULL, path, (unsigned)strlen(path), NULL, NULL, &err))
+ {
+ report_failure("%s", err);
+ g_free(err);
+ return NULL;
+ }
+
+ if (g_str_has_suffix(path, ".eds"))
+ {
+ profile = profile_new(allocator);
+ if (!epl_eds_load(profile, path))
+ profile_del(profile);
+ }
+#if HAVE_LIBXML2
+ else if (g_str_has_suffix(path, ".xdd") || g_str_has_suffix(path, ".xdc"))
+ {
+ profile = profile_new(allocator);
+ if (!epl_xdd_load(profile, path))
+ profile_del(profile);
+ }
+#endif
+ if (!profile)
+ report_failure("Profile '%s' couldn't be parsed", path);
+
+ return profile;
+}
+
+static void apply_prefs(void)
+{
+ /* This gets called for all preferences, so we only load profile if path changes */
+ if (epl_default_profile_path != epl_default_profile_path_last
+ && epl_default_profile_path && *epl_default_profile_path)
+ {
+ profile_del(epl_default_profile);
+ epl_default_profile = profile_load(wmem_epan_scope(), epl_default_profile_path);
+
+ epl_default_profile_path_last = epl_default_profile_path;
+ /* TODO we could use something like UAT_AFFECTS_DISSECTION */
+ }
+}
/* Register the protocol with Wireshark */
@@ -4060,7 +5146,6 @@ void
proto_register_epl(void)
{
static hf_register_info hf[] = {
-
/* Common data fields (same for all message types) */
{ &hf_epl_mtyp,
{ "MessageType", "epl.mtyp",
@@ -4232,11 +5317,11 @@ proto_register_epl(void)
FT_BOOLEAN, 8, NULL, EPL_SOA_SYNC_PRES_FIRST, NULL, HFILL }
},
{ &hf_epl_soa_pre_set ,
- { "PResModeSet", "epl.soa.prsc",
+ { "PResModeSet", "epl.soa.prmst",
FT_BOOLEAN, 8, NULL, EPL_SOA_SYNC_PRES_SET, NULL, HFILL }
},
{ &hf_epl_soa_pre_res,
- { "PResModeReset", "epl.soa.prft",
+ { "PResModeReset", "epl.soa.prmrst",
FT_BOOLEAN, 8, NULL, EPL_SOA_SYNC_PRES_RESET, NULL, HFILL }
},
{ &hf_epl_soa_mac_end,
@@ -4422,12 +5507,16 @@ proto_register_epl(void)
},
{ &hf_epl_asnd_identresponse_dt,
{ "DeviceType", "epl.asnd.ires.devicetype",
- FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL }
},
- { &hf_epl_asnd_identresponse_profile,
- { "Profile", "epl.asnd.ires.profile",
+ { &hf_epl_asnd_identresponse_dt_add,
+ { "DeviceType additional info", "epl.asnd.ires.devicetype.add",
FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
+ { &hf_epl_asnd_identresponse_profile_path,
+ { "Profile Path", "epl.asnd.ires.profilepath",
+ FT_STRING, STR_UNICODE, NULL, 0x00, NULL, HFILL }
+ },
{ &hf_epl_asnd_identresponse_vid,
{ "VendorId", "epl.asnd.ires.vendorid",
FT_UINT32, BASE_DEC_HEX, NULL, 0x00, NULL, HFILL }
@@ -4536,7 +5625,7 @@ proto_register_epl(void)
FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_epl_asnd_syncResponse_delay,
- { "SyncDelay", "epl.asnd.syncresponse.delay.station",
+ { "SyncDelay", "epl.asnd.syncresponse.delay",
FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_epl_asnd_syncResponse_pre_fst,
@@ -4748,10 +5837,6 @@ proto_register_epl(void)
FT_UINT8, BASE_DEC, VALS(epl_sdo_send_con_vals),
0x03, NULL, HFILL }
},
- { &hf_epl_asnd_sdo_cmd,
- { "Command Layer", "epl.asnd.sdo.cmd",
- FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }
- },
{ &hf_epl_asnd_sdo_cmd_transaction_id,
{ "SDO Transaction ID", "epl.asnd.sdo.cmd.transaction.id",
FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL }
@@ -4761,6 +5846,30 @@ proto_register_epl(void)
FT_UINT8, BASE_DEC,
VALS(epl_sdo_asnd_cmd_response), 0x80, NULL, HFILL }
},
+#if 0
+ { &hf_epl_asnd_sdo_resp_in,
+ { "Response frame", "epl.asnd.sdo.resp_in",
+ FT_FRAMENUM, BASE_NONE,
+ FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
+ "The frame number of the corresponding response", HFILL }
+ },
+ { &hf_epl_asnd_sdo_no_resp,
+ { "No response seen", "epl.asnd.sdo.no_resp",
+ FT_NONE, BASE_NONE,
+ NULL, 0x0,
+ "No corresponding response frame was seen", HFILL }
+ },
+ { &hf_epl_asnd_sdo_resp_to,
+ { "Request frame", "epl.asnd.sdo.resp_to",
+ FT_FRAMENUM, BASE_NONE,
+ FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
+ "The frame number of the corresponding request", HFILL }
+ },
+#endif
+ { &hf_epl_asnd_sdo_cmd,
+ { "Command Layer", "epl.asnd.sdo.cmd",
+ FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
{ &hf_epl_asnd_sdo_cmd_abort,
{ "SDO Abort", "epl.asnd.sdo.cmd.abort",
FT_UINT8, BASE_DEC,
@@ -4879,6 +5988,97 @@ proto_register_epl(void)
{ "Sub Abort Code", "epl.asnd.sdo.od.multiparam.abort",
FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL }
},
+
+ /* EPL Data types */
+ { &hf_epl_pdo,
+ { "PDO", "epl.pdo",
+ FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_pdo_index,
+ { "Index", "epl.pdo.index",
+ FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_pdo_subindex,
+ { "SubIndex", "epl.pdo.subindex",
+ FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_meta,
+ { "PDO meta info", "epl.od.meta",
+ FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_meta_mapping_index,
+ { "Mapped by index", "epl.od.meta.index",
+ FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_meta_mapping_subindex,
+ { "Mapped by subindex", "epl.od.meta.subindex",
+ FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_meta_lifetime_start,
+ { "Lifetime start", "epl.od.meta.lifetime.start",
+ FT_FRAMENUM, FT_NONE, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_meta_lifetime_end,
+ { "Lifetime end", "epl.od.meta.lifetime.end",
+ FT_FRAMENUM, FT_NONE, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_meta_offset,
+ { "Offset", "epl.od.meta.offset",
+ FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_meta_length,
+ { "Length", "epl.od.meta.length",
+ FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_boolean,
+ { "Data", "epl.od.data.boolean",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_int,
+ { "Data", "epl.od.data.int",
+ FT_INT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_uint,
+ { "Data", "epl.od.data.uint",
+ /* We can't use BASE_DEC_HEX directly, because a FT_UINT8
+ * would then have 15 leading zeroes */
+ FT_UINT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_real,
+ { "Data", "epl.od.data.real",
+ FT_FLOAT, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_string,
+ { "Data", "epl.od.data.string",
+ FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_octet_string,
+ { "Data", "epl.od.data.bytestring",
+ FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_mac,
+ { "Data", "epl.od.data.ethaddr",
+ FT_ETHER, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
+ { &hf_epl_od_ipv4,
+ { "Data", "epl.od.data.ipv4",
+ FT_IPv4, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
+#if 0
+ { &hf_epl_od_domain,
+ { "Data", "epl.od.data.domain",
+ FT_BYTES, BASE_ALLOW_ZERO, NULL, 0x00, NULL, HFILL }
+ },
+
+ { &hf_epl_od_time_difference, /* not 1:1 */
+ { "Data", "epl.od.data.time",
+ FT_RELATIVE_TIME, BASE_NONE, NULL, 0x00, NULL, HFILL }
+ },
+#endif
+ { &hf_epl_od_time,
+ { "Data", "epl.od.data.time",
+ FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL }
+ },
};
/* Setup protocol subtree array */
@@ -4904,6 +6104,7 @@ proto_register_epl(void)
&ett_epl_fragments,
&ett_epl_asnd_sdo_data_reassembled,
&ett_epl_asnd_nmt_dna,
+ &ett_epl_pdo_meta
};
static ei_register_info ei[] = {
@@ -4943,13 +6144,14 @@ proto_register_epl(void)
heur_epl_subdissector_list = register_heur_dissector_list("epl", proto_epl);
heur_epl_data_subdissector_list = register_heur_dissector_list("epl_data", proto_epl);
epl_asnd_dissector_table = register_dissector_table("epl.asnd",
- "Manufacturer specific ASND service", proto_epl, FT_UINT8, BASE_DEC);
+ "Manufacturer specific ASND service", proto_epl, FT_UINT8, BASE_DEC /*, DISSECTOR_TABLE_NOT_ALLOW_DUPLICATE*/);
/* Registering protocol to be called by another dissector */
epl_handle = register_dissector("epl", dissect_epl, proto_epl);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_epl, hf, array_length(hf));
+
proto_register_subtree_array(ett, array_length(ett));
/* Register expert information field */
@@ -4957,7 +6159,7 @@ proto_register_epl(void)
expert_register_field_array ( expert_epl, ei, array_length (ei ) );
/* register preferences */
- epl_module = prefs_register_protocol(proto_epl, NULL);
+ epl_module = prefs_register_protocol(proto_epl, apply_prefs);
prefs_register_bool_preference(epl_module, "show_soc_flags", "Show flags of SoC frame in Info column",
"If you are capturing in networks with multiplexed or slow nodes, this can be useful", &show_soc_flags);
@@ -4965,16 +6167,87 @@ proto_register_epl(void)
prefs_register_bool_preference(epl_module, "show_duplicated_command_layer", "Show command-layer in duplicated frames",
"For analysis purposes one might want to show the command layer even if the dissectore assumes a duplicated frame", &show_cmd_layer_for_duplicated);
+ prefs_register_bool_preference(epl_module, "show_pdo_meta_info", "Show life times and origin PDO Tx/Rx params for PDO entries",
+ "For analysis purposes one might want to see how long the current mapping has been active for and what OD write caused it", &show_pdo_meta_info);
+
+ prefs_register_bool_preference(epl_module, "use_sdo_mappings", "Use SDO ObjectMappings for PDO dissection",
+ "Partition PDOs according to ObjectMappings sent via SDO", &use_sdo_mappings);
+
+#ifdef HAVE_LIBXML2
+ prefs_register_bool_preference(epl_module, "use_xdc_mappings", "Use XDC ObjectMappings for PDO dissection",
+ "If you want to parse the defaultValue (XDD) and actualValue (XDC) attributes for ObjectMappings in order to detect default PDO mappings, which may not be sent over SDO ", &use_xdc_mappings);
+#endif
+
+ prefs_register_bool_preference(epl_module, "interpret_untyped_as_le", "Interpret short (<64bit) data as little endian integers",
+ "If a data field has untyped data under 8 byte long, interpret it as unsigned little endian integer and show decimal and hexadecimal representation thereof. Otherwise use stock data dissector", &interpret_untyped_as_le);
+
+ /* init device profiles support */
+ epl_profiles_by_device = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
+ epl_profiles_by_nodeid = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
+ epl_profiles_by_address = wmem_map_new(wmem_epan_scope(), epl_address_hash, epl_address_equal);
+
+ set_address(&epl_placeholder_mac, AT_ETHER, 6, "\xFF\xFF\xFF\xFF\xFF\xFF");
+
+#ifdef HAVE_LIBXML2
+ epl_xdd_init();
+#endif
+ epl_eds_init();
+
+ prefs_register_filename_preference(epl_module, "default_profile", "Default Profile to use if no specific profiles exist",
+ "If you have a capture without IdentResponse and many nodes, it's easier to set a default profile here than to add entries for all MAC address or Node IDs",
+ &epl_default_profile_path, FALSE);
+
+ device_profile_uat = uat_new("Device-Specific Profiles",
+ sizeof (struct device_profile_uat_assoc),
+ "device_profiles", /* filename */
+ TRUE, /* from_profile */
+ &device_profile_list_uats, /* data_ptr */
+ &ndevice_profile_uat, /* numitems_ptr */
+ UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */
+ NULL, /* Help section (currently a wiki page) */
+ device_profile_uat_copy_cb,
+ device_profile_uat_update_record,
+ device_profile_uat_free_cb,
+ device_profile_parse_uat,
+ NULL,
+ device_profile_list_uats_flds);
+
+ prefs_register_uat_preference(epl_module, "device_profiles",
+ "Device-Specific Profiles",
+ "Add vendor-provided EDS" IF_LIBXML("/XDD") " profiles here",
+ device_profile_uat
+ );
+
+
+ nodeid_profile_uat = uat_new("NodeID-Specific Profiles",
+ sizeof (struct nodeid_profile_uat_assoc),
+ "nodeid_profiles", /* filename */
+ TRUE, /* from_profile */
+ &nodeid_profile_list_uats, /* data_ptr */
+ &nnodeid_profile_uat, /* numitems_ptr */
+ UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */
+ NULL, /* Help section (currently a wiki page) */
+ nodeid_profile_uat_copy_cb,
+ nodeid_profile_uat_update_record,
+ nodeid_profile_uat_free_cb,
+ nodeid_profile_parse_uat,
+ NULL,
+ nodeid_profile_list_uats_flds);
+
+ prefs_register_uat_preference(epl_module, "nodeid_profiles",
+ "Node-Specific Profiles",
+ "Assign vendor-provided EDS" IF_LIBXML("/XDD") " profiles to CN IDs here",
+ nodeid_profile_uat
+ );
+
/* tap-registration */
/* epl_tap = register_tap("epl");*/
}
-
-
void
proto_reg_handoff_epl(void)
{
- dissector_handle_t epl_udp_handle = create_dissector_handle( dissect_epludp, proto_epl );
+ dissector_handle_t epl_udp_handle = create_dissector_handle(dissect_epludp, proto_epl);
dissector_add_uint("ethertype", ETHERTYPE_EPL_V2, epl_handle);
dissector_add_uint_with_preference("udp.port", UDP_PORT_EPL, epl_udp_handle);
@@ -4984,9 +6257,326 @@ proto_reg_handoff_epl(void)
register_cleanup_routine( cleanup_dissector );
/* register reassembly table */
reassembly_table_register(&epl_reassembly_table, &addresses_reassembly_table_functions);
+}
+
+
+static gboolean
+epl_uat_fld_uint16dec_check_cb(void *_record _U_, const char *str, guint len _U_, const void *chk_data _U_, const void *fld_data _U_, char **err)
+{
+ guint16 val;
+ if (!ws_strtou16(str, NULL, &val))
+ {
+ *err = g_strdup("Invalid argument. Expected a decimal between [0-65535]");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+epl_uat_fld_uint32hex_check_cb(void *_record _U_, const char *str, guint len _U_, const void *chk_data _U_, const void *fld_data _U_, char **err)
+{
+ guint32 val;
+ if (!ws_hexstrtou32(str, NULL, &val))
+ {
+ *err = g_strdup("Invalid argument. Expected a hexadecimal between [0-ffffffff]");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+epl_profile_uat_fld_fileopen_check_cb(void *record _U_, const char *path, guint len, const void *chk_data _U_, const void *fld_data _U_, char **err)
+{
+ const char *supported = "Only" IF_LIBXML(" *.xdd, *.xdc and") " *.eds profiles supported.";
+ ws_statb64 st;
+
+
+ if (!path || !len)
+ {
+ *err = g_strdup("No filename given.");
+ return FALSE;
+ }
+
+ if (ws_stat64(path, &st) != 0)
+ {
+ *err = g_strdup_printf("File '%s' does not exist or access was denied.", path);
+ return FALSE;
+ }
+
+
+ if (g_str_has_suffix(path, ".eds"))
+ {
+ *err = NULL;
+ return TRUE;
+ }
+
+ if (g_str_has_suffix(path, ".xdd") || g_str_has_suffix(path, ".xdc"))
+ {
+#ifdef HAVE_LIBXML2
+ *err = NULL;
+ return TRUE;
+#else
+ *err = g_strdup_printf("*.xdd and *.xdc support not compiled in. %s", supported);
+ return FALSE;
+#endif
+ }
+
+ *err = g_strdup(supported);
+ return FALSE;
+}
+
+
+static void
+drop_profiles(void *key _U_, void *value, void *user_data _U_)
+{
+ struct profile *head = (struct profile*)value, *curr;
+ while ((curr = head))
+ {
+ head = head->next;
+ profile_del(curr);
+ }
+}
+
+static void
+device_profile_parse_uat(void)
+{
+ guint i;
+ struct profile *profile = NULL;
+ wmem_map_foreach(epl_profiles_by_device, drop_profiles, NULL);
+
+ /* PDO Mappings will have stale pointers after a profile change
+ * so we reset the memory pool. As PDO Mappings are refereneced
+ * via Conversations, we need to fixup those too to avoid a use
+ * after free, preferably by clearing them.
+ * This generation++ is a temporary workaround
+ */
+
+
+ if (pdo_mapping_scope)
+ {
+ wmem_free_all(pdo_mapping_scope);
+ current_convo_generation++; /* FIXME remove */
+ }
+
+ for (i = 0; i < ndevice_profile_uat; i++)
+ {
+ struct device_profile_uat_assoc *uat = &(device_profile_list_uats[i]);
+
+ profile = (struct profile*)wmem_map_lookup(epl_profiles_by_device, GUINT_TO_POINTER(uat->device_type));
+
+ /* do a shallow copy, we can't use the original because we need different
+ * ->next pointer for each. May be we should've used Glib's non-intrusive
+ * linked list to begin with
+ */
+ if (profile)
+ {
+ struct profile *clone = wmem_new0(profile->scope, struct profile);
+ *clone = *profile;
+ profile = clone;
+ }
+
+ if (!profile)
+ profile = profile_load(wmem_epan_scope(), uat->path);
+
+ if (!profile)
+ continue;
+ struct profile *profile_head;
+ if ((profile_head = (struct profile*)wmem_map_lookup(epl_profiles_by_device, GUINT_TO_POINTER(profile->id))))
+ {
+ wmem_map_remove(epl_profiles_by_device, GUINT_TO_POINTER(profile_head->id));
+ profile->next = profile_head;
+ }
+
+ profile->id = uat->device_type;
+ profile->data = GUINT_TO_POINTER(profile->id);
+ profile->vendor_id = uat->vendor_id;
+ profile->product_code = uat->product_code;
+
+ wmem_map_insert(epl_profiles_by_device, GUINT_TO_POINTER(profile->id), profile);
+ profile->parent_map = epl_profiles_by_device;
+
+ g_log(NULL, G_LOG_LEVEL_INFO, "Loading %s\n", profile->path);
+ }
+}
+
+static gboolean
+device_profile_uat_update_record(void *_record _U_, char **err _U_)
+{
+ return TRUE;
+}
+
+static void
+device_profile_uat_free_cb(void *_r)
+{
+ struct device_profile_uat_assoc *r = (struct device_profile_uat_assoc *)_r;
+ g_free(r->path);
+}
+
+static void*
+device_profile_uat_copy_cb(void *dst_, const void *src_, size_t len _U_)
+{
+ const struct device_profile_uat_assoc *src = (const struct device_profile_uat_assoc *)src_;
+ struct device_profile_uat_assoc *dst = (struct device_profile_uat_assoc *)dst_;
+
+ dst->path = g_strdup(src->path);
+ dst->device_type = src->device_type;
+ dst->vendor_id = src->vendor_id;
+ dst->product_code = src->product_code;
+
+ return dst;
+}
+
+static void
+nodeid_profile_parse_uat(void)
+{
+ guint i;
+ struct profile *profile = NULL;
+ wmem_map_foreach(epl_profiles_by_nodeid, drop_profiles, NULL);
+ wmem_map_foreach(epl_profiles_by_address, drop_profiles, NULL);
+
+
+ /* PDO Mappings will have stale pointers after a profile change
+ * so we reset the memory pool. As PDO Mappings are refereneced
+ * via Conversations, we need to fixup those too to avoid a use
+ * after free, preferably by clearing them.
+ * This generation++ is a temporary workaround
+ */
+
+ if (pdo_mapping_scope)
+ {
+ wmem_free_all(pdo_mapping_scope);
+ current_convo_generation++; /* FIXME remove */
+ }
+
+ for (i = 0; i < nnodeid_profile_uat; i++)
+ {
+ struct nodeid_profile_uat_assoc *uat = &(nodeid_profile_list_uats[i]);
+
+ profile = uat->is_nodeid ? (struct profile*)wmem_map_lookup(epl_profiles_by_nodeid, GUINT_TO_POINTER(uat->node.id))
+ : (struct profile*)wmem_map_lookup(epl_profiles_by_address, &uat->node.addr);
+
+ if (!profile)
+ profile = profile_load(wmem_epan_scope(), uat->path);
+
+ if (!profile)
+ continue;
+
+ if (uat->is_nodeid)
+ {
+ profile->nodeid = uat->node.id;
+ profile->data = GUINT_TO_POINTER(profile->nodeid);
+
+ wmem_map_insert(epl_profiles_by_nodeid, GUINT_TO_POINTER(profile->nodeid), profile);
+ profile->parent_map = epl_profiles_by_nodeid;
+
+ }
+ else
+ {
+ copy_address_wmem(profile->scope, &profile->node_addr, &uat->node.addr);
+ profile->data = &profile->node_addr;
+
+ wmem_map_insert(epl_profiles_by_address, &profile->node_addr, profile);
+ profile->parent_map = epl_profiles_by_address;
+ }
+ g_log(NULL, G_LOG_LEVEL_INFO, "Loading %s\n", profile->path);
+ }
+}
+
+
+static gboolean
+nodeid_profile_uat_update_record(void *_record _U_, char **err _U_)
+{
+ return TRUE;
+}
+
+static void
+nodeid_profile_uat_free_cb(void *_r)
+{
+ struct nodeid_profile_uat_assoc *r = (struct nodeid_profile_uat_assoc *)_r;
+ if (!r->is_nodeid)
+ free_address(&r->node.addr);
+ g_free(r->path);
+}
+
+static void*
+nodeid_profile_uat_copy_cb(void *dst_, const void *src_, size_t len _U_)
+{
+ const struct nodeid_profile_uat_assoc *src = (const struct nodeid_profile_uat_assoc *)src_;
+ struct nodeid_profile_uat_assoc *dst = (struct nodeid_profile_uat_assoc *)dst_;
+
+ dst->path = g_strdup(src->path);
+ dst->id_str = g_strdup(src->id_str);
+ if ((dst->is_nodeid = src->is_nodeid))
+ dst->node.id = src->node.id;
+ else
+ copy_address(&dst->node.addr, &src->node.addr);
+
+ return dst;
+}
+
+static void
+nodeid_profile_list_uats_nodeid_tostr_cb(void *_rec, char **out_ptr, unsigned *out_len, const void *u1 _U_, const void *u2 _U_)
+{
+ struct nodeid_profile_uat_assoc *rec = (struct nodeid_profile_uat_assoc*)_rec;
+ if (rec->id_str)
+ {
+ *out_ptr = g_strdup(rec->id_str);
+ *out_len = (unsigned)strlen(rec->id_str);
+ }
+ else
+ {
+ *out_ptr = g_strdup("");
+ *out_len = 0;
+ }
+}
+
+static gboolean
+epl_uat_fld_cn_check_cb(void *record _U_, const char *str, guint len _U_, const void *u1 _U_, const void *u2 _U_, char **err)
+{
+ unsigned int c;
+ guint8 nodeid;
+
+ if (ws_strtou8(str, NULL, &nodeid) && EPL_IS_CN_NODEID(nodeid))
+ return TRUE;
+
+ if (sscanf(str, "%*02x%*c%*02x%*c%*02x%*c%*02x%*c%*02x%*c%02x", &c) > 0)
+ return TRUE;
+
+ *err = g_strdup("Invalid argument. Expected either a CN ID [1-239] or a MAC address");
+ return FALSE;
+}
+
+static void
+nodeid_profile_list_uats_nodeid_set_cb(void *_rec, const char *str, unsigned len, const void *set_data _U_, const void *fld_data _U_)
+{
+ struct nodeid_profile_uat_assoc *rec = (struct nodeid_profile_uat_assoc*)_rec;
+ guint8 addr[6];
+
+ if (ws_strtou8(str, NULL, &addr[0]))
+ {
+ rec->is_nodeid = TRUE;
+ rec->node.id = addr[0];
+ }
+ else
+ {
+ unsigned i;
+ const char *endptr = str;
+ for (i = 0; i < 6; i++)
+ {
+ ws_hexstrtou8(endptr, &endptr, &addr[i]);
+ endptr++;
+ }
+
+ alloc_address_wmem(NULL, &rec->node.addr, AT_ETHER, 6, addr);
+ rec->is_nodeid = FALSE;
+ }
+
+ g_free(rec->id_str);
+ rec->id_str = g_strndup(str, len);
}
+
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
diff --git a/epan/dissectors/packet-epl.h b/epan/dissectors/packet-epl.h
index c94e6ddd98..7db6187fa7 100644
--- a/epan/dissectors/packet-epl.h
+++ b/epan/dissectors/packet-epl.h
@@ -30,6 +30,102 @@
#include <epan/wmem/wmem.h>
#include <epan/range.h>
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#pragma GCC diagnostic ignored "-Wmissing-braces"
+#endif
+
+struct epl_datatype;
+
+struct profile {
+ guint16 id;
+ guint8 nodeid;
+ address node_addr;
+
+ guint32 vendor_id;
+ guint32 product_code;
+
+ wmem_map_t *objects;
+ wmem_allocator_t *scope, *parent_scope;
+ wmem_map_t *parent_map;
+
+ char *name;
+ char *path;
+ void *data;
+ guint cb_id;
+ wmem_array_t *TPDO; /* CN->MN */
+ wmem_array_t *RPDO; /* MN->CN */
+
+ struct profile *next;
+};
+
+enum { OD_ENTRY_SCALAR = 7, OD_ENTRY_ARRAY = 8, OD_ENTRY_RECORD = 9 };
+struct od_entry {
+ guint16 idx;
+ /* This is called the ObjectType in the standard,
+ * but this is too easy to be mistaken with the
+ * DataType.
+ * ObjectType specifies whether it's a scalar or
+ * an aggregate
+ */
+ guint16 type_class;
+ char name[64];
+ /* Called DataType by the standard,
+ * Can be e.g. Unsigned32
+ */
+ const struct epl_datatype *type;
+ guint64 value;
+};
+
+struct subobject {
+ range_admin_t range;
+ struct od_entry info;
+};
+
typedef struct epl_wmem_iarray epl_wmem_iarray_t;
+struct object {
+ struct od_entry info;
+ epl_wmem_iarray_t *subindices;
+};
+
+struct profile;
+
+const struct epl_datatype *epl_type_to_hf(const char *name);
+
+static inline gboolean
+subobject_equal(gconstpointer _a, gconstpointer _b)
+{
+ const struct od_entry *a = &((const struct subobject*)_a)->info;
+ const struct od_entry *b = &((const struct subobject*)_b)->info;
+
+ return a->type_class == b->type_class
+ && a->type == b->type
+ && g_str_equal(a->name, b->name);
+}
+
+void epl_xdd_init(void);
+struct profile *epl_xdd_load(struct profile *profile, const char *xml_file);
+
+void epl_eds_init(void);
+struct profile *epl_eds_load(struct profile *profile, const char *eds_file);
+
+
+struct object *epl_profile_object_add(struct profile *profile, guint16 idx);
+struct object *epl_profile_object_lookup_or_add(struct profile *profile, guint16 idx);
+
+gboolean epl_profile_object_mapping_add(struct profile *profile, guint16 idx, guint8 subindex, guint64 mapping);
+gboolean epl_profile_object_mappings_update(struct profile *profile);
+
+range_admin_t * epl_wmem_iarray_find(epl_wmem_iarray_t *arr, guint32 value);
+gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr);
+gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr);
+
+#define EPL_OBJECT_MAPPING_SIZE sizeof (guint64)
+
+#define CHECK_OVERLAP_ENDS(x1, x2, y1, y2) ((x1) < (y2) && (y1) < (x2))
+#define CHECK_OVERLAP_LENGTH(x, x_len, y, y_len) \
+ CHECK_OVERLAP_ENDS((x), (x) + (x_len), (y), (y) + (y_len))
+
+
#endif