|
|
@@ -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;
|
|
|
}
|