Ver Fonte

Change Ethernet Link object's handling of capabilities and interface label

The interface label is now part of the global g_ethernet_link object data.

Also a meta structure CipEthernetLinkMetaInterfaceCapability for the interface capabilities is added to the Ethernet Link object structure. This
meta attribute uses a bit mask to mark which entries of type CipEthernetLinkSpeedDuplexArrayEntry of the "speed_duplex_table" array are
valid and should be returned for a GetAttribute request to the interface
capability attribute (#11).

Signed-off-by: Stefan Mätje <stefan.maetje@esd.eu>
Stefan Mätje há 6 anos atrás
pai
commit
01ffdbd2b6
2 ficheiros alterados com 180 adições e 78 exclusões
  1. 125 63
      source/src/cip/cipethernetlink.c
  2. 55 15
      source/src/cip/cipethernetlink.h

+ 125 - 63
source/src/cip/cipethernetlink.c

@@ -3,10 +3,24 @@
  * All rights reserved.
  *
  ******************************************************************************/
-#include <string.h>
+/*
+ * CIP Ethernet Link Object
+ * ========================
+ *
+ * Implemented Attributes
+ * ----------------------
+ * - Attribute  1: Interface Speed
+ * - Attribute  2: Interface Flags
+ * - Attribute  3: Physical Address (Ethernet MAC)
+ * - Attribute 10: Interface Label
+ * - Attribute 11: Interface Capabilities
+ *
+ */
 
 #include "cipethernetlink.h"
 
+#include <string.h>
+
 #include "cipcommon.h"
 #include "cipmessagerouter.h"
 #include "ciperror.h"
@@ -14,26 +28,34 @@
 #include "opener_api.h"
 #include "trace.h"
 
+/** @brief Type definition of the Interface Control attribute (#6)
+ *
+ *  This is used only internally at the moment.
+ */
 typedef struct {
   CipWord control_bits;
   CipUint forced_interface_speed;
 } CipEthernetLinkInterfaceControl;
 
+/** @brief Type definition of one entry in the speed / duplex array
+ */
 typedef struct speed_duplex_array_entry {
-  CipUint interface_speed;
-  CipUint interface_duplex_mode;
+  CipUint interface_speed;  /**< the interface speed in Mbit/s */
+  CipUsint interface_duplex_mode;  /**< the interface's duplex mode: 0 = half duplex, 1 = full duplex, 2-255 = reserved */
 } CipEthernetLinkSpeedDuplexArrayEntry;
 
-typedef struct speed_duplex_options {
-  CipUsint speed_duplex_array_count;
-  struct speed_duplex_array_entry *speed_duplex_array;
-} CipEthernetLinkSpeedDuplexOptions;
 
-typedef struct {
-  CipDword capability_bits;
-  struct speed_duplex_options speed_duplex_options;
-} CipEthernetLinkInterfaceCapability;
+/* forward declaration of functions to encode certain attribute objects */
+static int EncodeInterfaceCounters(EipUint8 **pa_acMsg);
+
+static int EncodeMediaCounters(EipUint8 **pa_acMsg);
+
+static int EncodeInterfaceControl(EipUint8 **pa_acMsg);
+
+static int EncodeInterfaceCapability(EipUint8 **pa_acMsg);
 
+
+/* forward declaration for the GetAttributeSingle service handler function */
 EipStatus GetAttributeSingleEthernetLink(
   CipInstance *RESTRICT const instance,
   CipMessageRouterRequest *const message_router_request,
@@ -41,30 +63,59 @@ EipStatus GetAttributeSingleEthernetLink(
   const struct sockaddr *originator_address,
   const int encapsulation_session);
 
-/** @brief Configures the MAC address of the Ethernet Link object*
+
+/** @brief This is the internal table of possible speed / duplex combinations
  *
- *  @param mac_address The MAC address of the Ethernet Link
+ *  This table contains all possible speed / duplex combinations of today.
+ *  Which entries of this table are transmitted during the GetService
+ *  is controlled by the
+ *  CipEthernetLinkMetaInterfaceCapability::speed_duplex_selector bit mask.
+ *  Therefore you need to keep this array in sync with the bit masks of
+ *  CipEthLinkSpeedDpxSelect.
  */
+static const CipEthernetLinkSpeedDuplexArrayEntry speed_duplex_table[] =
+{
+  { /* Index 0: 10Mbit/s half duplex*/
+    .interface_speed = 10,
+    .interface_duplex_mode = 0
+  },
+  { /* Index 1: 10Mbit/s full duplex*/
+    .interface_speed = 10,
+    .interface_duplex_mode = 1
+  },
+  { /* Index 2: 100Mbit/s half duplex*/
+    .interface_speed = 100,
+    .interface_duplex_mode = 0
+  },
+  { /* Index 3: 100Mbit/s full duplex*/
+    .interface_speed = 100,
+    .interface_duplex_mode = 1
+  },
+  { /* Index 4: 1000Mbit/s half duplex*/
+    .interface_speed = 1000,
+    .interface_duplex_mode = 0
+  },
+  { /* Index 5: 1000Mbit/s full duplex*/
+    .interface_speed = 1000,
+    .interface_duplex_mode = 1
+  },
+};
+
 
-CipUsint dummy_attribute_usint = 0;
-CipUdint dummy_attribute_udint = 0;
-
-CipShortString interface_label = { .length = 0, .string = NULL };
-CipEthernetLinkInterfaceControl interface_control = { .control_bits = 0,
-                                                      .forced_interface_speed =
-                                                        0 };
-
-CipEthernetLinkSpeedDuplexArrayEntry speed_duplex_object = { .interface_speed =
-                                                               100, .
-                                                             interface_duplex_mode
-                                                               = 1 };
-CipEthernetLinkInterfaceCapability interface_capability = {
-  .capability_bits = 1, .speed_duplex_options = { .speed_duplex_array_count =
-                                                    1,
-                                                  .speed_duplex_array =
-                                                    &speed_duplex_object }
+/* Two dummy variables to provide fill data for the GetAttributeAll service. */
+static CipUsint dummy_attribute_usint = 0;
+static CipUdint dummy_attribute_udint = 0;
+
+/* Constant dummy data for attribute #6 */
+static CipEthernetLinkInterfaceControl interface_control =
+{
+  .control_bits = 0,
+  .forced_interface_speed = 0,
 };
 
+/** @brief Definition of the Ethernet Link object instance(s) */
+CipEthernetLinkObject g_ethernet_link;
+
 EipStatus CipEthernetLinkInit() {
   CipClass *ethernet_link_class = CreateCipClass(kCipEthernetLinkClassCode,
                                                  0, /* # class attributes*/
@@ -74,18 +125,29 @@ EipStatus CipEthernetLinkInit() {
                                                  11, /* # highest instance attribute number*/
                                                  2, /* # instance services*/
                                                  1, /* # instances*/
-                                                 "Ethernet Link", 4, /* # class revision*/
+                                                 "Ethernet Link", /* # class name */
+                                                 4, /* # class revision*/
                                                  NULL); /* # function pointer for initialization*/
 
   /* set attributes to initial values */
   g_ethernet_link.interface_speed = 100;
   g_ethernet_link.interface_flags = 0xF; /* successful speed and duplex neg, full duplex active link, TODO in future it should be checked if link is active */
+  g_ethernet_link.interface_caps.capability_bits = kEthLinkCapAutoNeg;
+  g_ethernet_link.interface_caps.speed_duplex_selector =
+    kEthLinkSpeedDpx_100_FD;
 
   if (ethernet_link_class != NULL) {
     CipInstance *ethernet_link_instance = GetCipInstance(ethernet_link_class,
                                                          1);
+    /* add services to the class */
+    InsertService(ethernet_link_class, kGetAttributeSingle,
+                  &GetAttributeSingleEthernetLink, "GetAttributeSingle");
+    InsertService(ethernet_link_class, kGetAttributeAll, &GetAttributeAll,
+                  "GetAttributeAll");
+
+    /* bind attributes to the instance*/
     InsertAttribute(ethernet_link_instance, 1, kCipUdint,
-                    &g_ethernet_link.interface_speed, kGetableSingleAndAll); /* bind attributes to the instance*/
+                    &g_ethernet_link.interface_speed, kGetableSingleAndAll);
     InsertAttribute(ethernet_link_instance, 2, kCipDword,
                     &g_ethernet_link.interface_flags, kGetableSingleAndAll);
     InsertAttribute(ethernet_link_instance, 3, kCip6Usint,
@@ -103,14 +165,9 @@ EipStatus CipEthernetLinkInit() {
     InsertAttribute(ethernet_link_instance, 9, kCipUsint,
                     &dummy_attribute_usint, kGetableAll);
     InsertAttribute(ethernet_link_instance, 10, kCipShortString,
-                    &interface_label, kGetableAll);
-    InsertAttribute(ethernet_link_instance, 11, kCipAny, &interface_capability,
-                    kGetableSingleAndAll);
-
-    InsertService(ethernet_link_class, kGetAttributeSingle,
-                  &GetAttributeSingleEthernetLink, "GetAttributeSingle");
-    InsertService(ethernet_link_class, kGetAttributeAll, &GetAttributeAll,
-                  "GetAttributeAll");
+                    &g_ethernet_link.interface_label, kGetableAll);
+    InsertAttribute(ethernet_link_instance, 11, kCipAny,
+                    &g_ethernet_link.interface_caps, kGetableSingleAndAll);
   } else {
     return kEipStatusError;
   }
@@ -118,7 +175,7 @@ EipStatus CipEthernetLinkInit() {
   return kEipStatusOk;
 }
 
-int EncodeInterfaceCounters(EipUint8 **pa_acMsg) {
+static int EncodeInterfaceCounters(EipUint8 **pa_acMsg) {
 // Returns default value 0
   int return_value = 0;
   for (int i = 0; i < 11; i++) {
@@ -127,7 +184,7 @@ int EncodeInterfaceCounters(EipUint8 **pa_acMsg) {
   return return_value;
 }
 
-int EncodeMediaCounters(EipUint8 **pa_acMsg) {
+static int EncodeMediaCounters(EipUint8 **pa_acMsg) {
 // Returns default value 0
   int return_value = 0;
   for (int i = 0; i < 12; i++) {
@@ -136,7 +193,7 @@ int EncodeMediaCounters(EipUint8 **pa_acMsg) {
   return return_value;
 }
 
-int EncodeInterfaceControl(EipUint8 **pa_acMsg) {
+static int EncodeInterfaceControl(EipUint8 **pa_acMsg) {
 // Returns default value 0
   int return_value = 0;
   return_value += EncodeData(kCipWord, &interface_control.control_bits,
@@ -147,28 +204,33 @@ int EncodeInterfaceControl(EipUint8 **pa_acMsg) {
   return return_value;
 }
 
-int EncodeInterfaceCapability(EipUint8 **pa_acMsg) {
+#define NELEMENTS(x)  ((sizeof(x)/sizeof(x[0])))
+static int EncodeInterfaceCapability(EipUint8 **pa_acMsg)
+{
   int return_value = 0;
-  return_value += EncodeData(kCipDword, &interface_capability.capability_bits,
+  return_value += EncodeData(kCipDword,
+                             &g_ethernet_link.interface_caps.capability_bits,
                              pa_acMsg);
-  return_value += EncodeData(
-    kCipUsint,
-    &interface_capability.speed_duplex_options.speed_duplex_array_count,
-    pa_acMsg);
-
-  for (CipUsint i = 0;
-       i < interface_capability.speed_duplex_options.speed_duplex_array_count;
-       i++) {
-    return_value += EncodeData(
-      kCipUint,
-      &(interface_capability.speed_duplex_options.speed_duplex_array[i]
-        .interface_speed),
-      pa_acMsg);
-    return_value += EncodeData(
-      kCipUsint,
-      &(interface_capability.speed_duplex_options.speed_duplex_array[i]
-        .interface_duplex_mode),
-      pa_acMsg);
+  {
+    unsigned selected = g_ethernet_link.interface_caps.speed_duplex_selector;
+    CipUsint count;
+    for (count = 0; selected; count++) { /* count # of bits set */
+      selected &= selected - 1u;  /* clear the least significant bit set */
+    }
+    return_value += EncodeData(kCipUsint, &count, pa_acMsg);
+  }
+
+  for (unsigned u = 0; u < NELEMENTS(speed_duplex_table); u++) {
+    if (g_ethernet_link.interface_caps.speed_duplex_selector & (1u << u)) {
+      return_value += EncodeData(
+                        kCipUint,
+                        &speed_duplex_table[u].interface_speed,
+                        pa_acMsg);
+      return_value += EncodeData(
+                        kCipUsint,
+                        &speed_duplex_table[u].interface_duplex_mode,
+                        pa_acMsg);
+    }
   }
   return return_value;
 }

+ 55 - 15
source/src/cip/cipethernetlink.h

@@ -15,30 +15,70 @@
 /** @brief Ethernet Link class code */
 static const CipUint kCipEthernetLinkClassCode = CIP_ETHERNETLINK_CLASS_CODE;
 
-/* public functions */
-/** @brief Initialize the Ethernet Link Objects data
- *
- *  @return kEipStatusOk if initialization was successful, otherwise kEipStatusError
- */
-EipStatus CipEthernetLinkInit(void);
+/* public type definitions */
 
-int EncodeInterfaceCounters(EipUint8 **pa_acMsg);
+/** @brief Provide bit masks for the Interface Capability (#11) attribute's Capability Bits */
+typedef enum {
+  /** Interface needs reset to activate attribute #6 */
+  kEthLinkCapManualReset = 0x01,
+  /** Interface supports link auto-negotiation */
+  kEthLinkCapAutoNeg = 0x02,
+  /** Interface supports link auto-crossover */
+  kEthLinkCapAutoMDX = 0x04,
+  /** Interface supports setting of Interface Control attribute(#6) */
+  kEthLinkCapManualSpeed = 0x08,
+} CipEthLinkCapabilityBits;
 
-int EncodeMediaCounters(EipUint8 **pa_acMsg);
 
-int EncodeInterfaceControl(EipUint8 **pa_acMsg);
+/** @brief Provide bit masks to select available speed / duplex combinations
+ *
+ *  Keep the bit index of these bit masks in sync with the array index of the
+ *  matching speed / duplex structure in the internal @p speed_duplex_table
+ *  of cipethernetlink.c
+ */
+typedef enum {
+  kEthLinkSpeedDpx_10_HD = 0x01,
+  kEthLinkSpeedDpx_10_FD = 0x02,
+  kEthLinkSpeedDpx_100_HD = 0x04,
+  kEthLinkSpeedDpx_100_FD = 0x08,
+  kEthLinkSpeedDpx_1000_HD = 0x10,
+  kEthLinkSpeedDpx_1000_FD = 0x20,
+} CipEthLinkSpeedDpxSelect;
 
-int EncodeInterfaceCapability(EipUint8 **pa_acMsg);
+
+/** @brief Type definition to describe the Interface Capability
+ *
+ *  This structure is not a direct representation of the Interface Capability
+ *  attribute (#11) but replaces the needed array of speed / duplex list entries
+ *  by @ref speed_duplex_selector to create the needed array on the fly.
+ */
+typedef struct {
+  /** Capability flags of CipEthLinkCapabilityBits group */
+  CipDword capability_bits;
+  /** Speed / duplex selector bit map of CipEthLinkSpeedDpxSelect */
+  uint16_t speed_duplex_selector;
+} CipEthernetLinkMetaInterfaceCapability;
 
 /** @brief Data of an CIP Ethernet Link object */
 typedef struct {
-  EipUint32 interface_speed; /**< 10/100/1000 Mbit/sec */
-  EipUint32 interface_flags; /**< Interface flags as defined in the CIP specification */
-  EipUint8 physical_address[6]; /**< MAC address of the Ethernet link */
+  EipUint32 interface_speed; /**< Attribute #1: 10/100/1000 Mbit/sec */
+  EipUint32 interface_flags; /**< Attribute #2: Interface flags as defined in the CIP specification */
+  EipUint8 physical_address[6]; /**< Attribute #3: MAC address of the Ethernet link */
+  CipShortString interface_label; /**< Attribute #10: Interface label */
+  CipEthernetLinkMetaInterfaceCapability interface_caps; /**< Attribute #11: Interface capabilities */
 } CipEthernetLinkObject;
 
-/* global private variables */
 
-CipEthernetLinkObject g_ethernet_link;
+/* public functions */
+/** @brief Initialize the Ethernet Link Objects data
+ *
+ *  @return kEipStatusOk if initialization was successful, otherwise kEipStatusError
+ */
+EipStatus CipEthernetLinkInit(void);
+
+
+/* global object instance(s) */
+
+extern CipEthernetLinkObject g_ethernet_link;
 
 #endif /* OPENER_CIPETHERNETLINK_H_*/