cipethernetlink.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. /*******************************************************************************
  2. * Copyright (c) 2009, Rockwell Automation, Inc.
  3. * All rights reserved.
  4. *
  5. ******************************************************************************/
  6. /** @file
  7. * @brief Implement the CIP Ethernet Link Object
  8. *
  9. * CIP Ethernet Link Object
  10. * ========================
  11. *
  12. * Implemented Attributes
  13. * ----------------------
  14. * Conditional attributes are indented and marked with the condition it
  15. * depends on like "(0 != OPENER_ETHLINK_CNTRS_ENABLE)"
  16. *
  17. * - Attribute 1: Interface Speed
  18. * - Attribute 2: Interface Flags
  19. * - Attribute 3: Physical Address (Ethernet MAC)
  20. * - Attribute 4: Interface Counters (32-bit) (0 != OPENER_ETHLINK_CNTRS_ENABLE)
  21. * - Attribute 5: Media Counters (32-bit) (0 != OPENER_ETHLINK_CNTRS_ENABLE)
  22. * - Attribute 6: Interface Control (0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE)
  23. * - Attribute 7: Interface Type
  24. * See Vol. 2 Section 6-3.4 regarding an example for a device with internal
  25. * switch port where the implementation of this attribute is recommended.
  26. * - Attribute 10: Interface Label (0 != OPENER_ETHLINK_LABEL_ENABLE)
  27. * If the define OPENER_ETHLINK_LABEL_ENABLE is set then this attribute
  28. * has a string content ("PORT 1" by default on instance 1) otherwise it
  29. * is empty.
  30. * - Attribute 11: Interface Capabilities
  31. *
  32. * Implemented Services
  33. * --------------------
  34. * Conditional services are indented and marked with the condition it
  35. * depends on like "(0 != OPENER_ETHLINK_CNTRS_ENABLE)"
  36. *
  37. * - GetAttributesAll
  38. * - GetAttributeSingle
  39. * - GetAndClearAttribute (0 != OPENER_ETHLINK_CNTRS_ENABLE)
  40. * This service should only implemented for the attributes 4, 5, 12,
  41. * 13 and 15.
  42. * - SetAttributeSingle (0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE)
  43. * This service should only be implemented if attribute 6 is enabled.
  44. *
  45. */
  46. #include "cipethernetlink.h"
  47. #include <string.h>
  48. #include "cipcommon.h"
  49. #include "opener_api.h"
  50. #include "trace.h"
  51. #include "opener_user_conf.h"
  52. #include "endianconv.h"
  53. #if OPENER_ETHLINK_INSTANCE_CNT > 1
  54. /* If we have more than 1 Ethernet Link instance then the interface label
  55. * attribute is mandatory. We need then OPENER_ETHLINK_LABEL_ENABLE. */
  56. #define OPENER_ETHLINK_LABEL_ENABLE 1
  57. #endif
  58. #ifndef OPENER_ETHLINK_LABEL_ENABLE
  59. #define OPENER_ETHLINK_LABEL_ENABLE 0
  60. #endif
  61. #if defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE
  62. #define IFACE_LABEL_ACCESS_MODE kGetableSingleAndAll
  63. #define IFACE_LABEL "PORT 1"
  64. #define IFACE_LABEL_1 "PORT 2"
  65. #define IFACE_LABEL_2 "PORT internal"
  66. #else
  67. #define IFACE_LABEL_ACCESS_MODE kGetableAll
  68. #endif
  69. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  70. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  71. #define IFACE_CTRL_ACCESS_MODE (kSetAndGetAble | kNvDataFunc)
  72. #else
  73. #define IFACE_CTRL_ACCESS_MODE kGetableAll
  74. #endif
  75. /** @brief Type definition of one entry in the speed / duplex array
  76. */
  77. typedef struct speed_duplex_array_entry {
  78. CipUint interface_speed; /**< the interface speed in Mbit/s */
  79. CipUsint interface_duplex_mode; /**< the interface's duplex mode: 0 = half duplex, 1 = full duplex, 2-255 = reserved */
  80. } CipEthernetLinkSpeedDuplexArrayEntry;
  81. /* forward declaration of functions to encode certain attribute objects */
  82. static void EncodeCipEthernetLinkInterfaceCounters(const void *const data,
  83. ENIPMessage *const outgoing_message);
  84. static void EncodeCipEthernetLinkMediaCounters(const void *const data,
  85. ENIPMessage *const outgoing_message);
  86. static void EncodeCipEthernetLinkInterfaceControl(const void *const data,
  87. ENIPMessage *const outgoing_message);
  88. static void EncodeCipEthernetLinkInterfaceCaps(const void *const data,
  89. ENIPMessage *const outgoing_message);
  90. #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
  91. /* forward declaration for the GetAndClear service handler function */
  92. EipStatus GetAndClearEthernetLink(
  93. CipInstance *RESTRICT const instance,
  94. CipMessageRouterRequest *const message_router_request,
  95. CipMessageRouterResponse *const message_router_response,
  96. const struct sockaddr *originator_address,
  97. const CipSessionHandle encapsulation_session);
  98. #endif /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
  99. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  100. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  101. /** @brief Modify the attribute values for Attribute6: Interface Control
  102. *
  103. * @param CipEthernetLinkInterfaceControl pointer to attribute data.
  104. * @param message_router_request pointer to request.
  105. * @param message_router_response pointer to response.
  106. * @return length of taken bytes
  107. * -1 .. error
  108. */
  109. int DecodeCipEthernetLinkInterfaceControl(
  110. CipEthernetLinkInterfaceControl *const data,
  111. const CipMessageRouterRequest *const message_router_request,
  112. CipMessageRouterResponse *const message_router_response);
  113. #endif
  114. /** @brief This is the internal table of possible speed / duplex combinations
  115. *
  116. * This table contains all possible speed / duplex combinations of today.
  117. * Which entries of this table are transmitted during the GetService
  118. * is controlled by the
  119. * CipEthernetLinkMetaInterfaceCapability::speed_duplex_selector bit mask.
  120. * Therefore you need to keep this array in sync with the bit masks of
  121. * CipEthLinkSpeedDpxSelect.
  122. */
  123. static const CipEthernetLinkSpeedDuplexArrayEntry speed_duplex_table[] =
  124. {
  125. { /* Index 0: 10Mbit/s half duplex*/
  126. .interface_speed = 10,
  127. .interface_duplex_mode = 0
  128. },
  129. { /* Index 1: 10Mbit/s full duplex*/
  130. .interface_speed = 10,
  131. .interface_duplex_mode = 1
  132. },
  133. { /* Index 2: 100Mbit/s half duplex*/
  134. .interface_speed = 100,
  135. .interface_duplex_mode = 0
  136. },
  137. { /* Index 3: 100Mbit/s full duplex*/
  138. .interface_speed = 100,
  139. .interface_duplex_mode = 1
  140. },
  141. { /* Index 4: 1000Mbit/s half duplex*/
  142. .interface_speed = 1000,
  143. .interface_duplex_mode = 0
  144. },
  145. { /* Index 5: 1000Mbit/s full duplex*/
  146. .interface_speed = 1000,
  147. .interface_duplex_mode = 1
  148. },
  149. };
  150. #if defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE
  151. static const CipShortString iface_label_table[OPENER_ETHLINK_INSTANCE_CNT] =
  152. {
  153. {
  154. .length = sizeof IFACE_LABEL - 1,
  155. .string = (EipByte *)IFACE_LABEL
  156. },
  157. #if OPENER_ETHLINK_INSTANCE_CNT > 1
  158. {
  159. .length = sizeof IFACE_LABEL_1 - 1,
  160. .string = (EipByte *)IFACE_LABEL_1
  161. },
  162. #endif
  163. #if OPENER_ETHLINK_INSTANCE_CNT > 2
  164. {
  165. .length = sizeof IFACE_LABEL_2 - 1,
  166. .string = (EipByte *)IFACE_LABEL_2
  167. },
  168. #endif
  169. };
  170. #endif /* defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE */
  171. /* Two dummy variables to provide fill data for the GetAttributeAll service. */
  172. static CipUsint dummy_attribute_usint = 0;
  173. #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
  174. #else
  175. static CipUdint dummy_attribute_udint = 0;
  176. #endif
  177. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  178. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  179. #else
  180. /* Constant dummy data for attribute #6 */
  181. static CipEthernetLinkInterfaceControl s_interface_control =
  182. {
  183. .control_bits = 0,
  184. .forced_interface_speed = 0,
  185. };
  186. #endif
  187. /** @brief Definition of the Ethernet Link object instance(s) */
  188. CipEthernetLinkObject g_ethernet_link[OPENER_ETHLINK_INSTANCE_CNT];
  189. EipStatus CipEthernetLinkInit(void) {
  190. CipClass *ethernet_link_class = CreateCipClass(kCipEthernetLinkClassCode,
  191. 0,
  192. /* # class attributes*/
  193. 7,
  194. /* # highest class attribute number*/
  195. 2,
  196. /* # class services*/
  197. 11,
  198. /* # instance attributes*/
  199. 11,
  200. /* # highest instance attribute number*/
  201. /* # instance services follow */
  202. 2 + OPENER_ETHLINK_CNTRS_ENABLE + OPENER_ETHLINK_IFACE_CTRL_ENABLE,
  203. OPENER_ETHLINK_INSTANCE_CNT,
  204. /* # instances*/
  205. "Ethernet Link",
  206. /* # class name */
  207. 4,
  208. /* # class revision*/
  209. NULL); /* # function pointer for initialization*/
  210. /* set attributes to initial values */
  211. for (size_t idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx) {
  212. g_ethernet_link[idx].interface_speed = 100;
  213. /* successful speed and duplex neg, full duplex active link.
  214. * TODO: in future it should be checked if link is active */
  215. g_ethernet_link[idx].interface_flags = 0xF;
  216. g_ethernet_link[idx].interface_type = kEthLinkIfTypeTwistedPair;
  217. if (2 == idx) {
  218. g_ethernet_link[idx].interface_type = kEthLinkIfTypeInternal;
  219. }
  220. #if defined(OPENER_ETHLINK_LABEL_ENABLE) && 0 != OPENER_ETHLINK_LABEL_ENABLE
  221. g_ethernet_link[idx].interface_label = iface_label_table[idx];
  222. #endif
  223. g_ethernet_link[idx].interface_caps.capability_bits = kEthLinkCapAutoNeg;
  224. g_ethernet_link[idx].interface_caps.speed_duplex_selector =
  225. kEthLinkSpeedDpx_100_FD;
  226. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  227. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  228. g_ethernet_link[idx].interface_control.control_bits =
  229. kEthLinkIfCntrlAutonegotiate;
  230. g_ethernet_link[idx].interface_control.forced_interface_speed = 0U;
  231. #endif
  232. }
  233. if (ethernet_link_class != NULL) {
  234. /* add services to the class */
  235. InsertService(ethernet_link_class, kGetAttributeSingle,
  236. &GetAttributeSingle,
  237. "GetAttributeSingle");
  238. InsertService(ethernet_link_class, kGetAttributeAll, &GetAttributeAll,
  239. "GetAttributeAll");
  240. #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
  241. InsertService(ethernet_link_class, kEthLinkGetAndClear,
  242. &GetAndClearEthernetLink, "GetAndClear");
  243. #endif
  244. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  245. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  246. InsertService(ethernet_link_class, kSetAttributeSingle,
  247. &SetAttributeSingle,
  248. "SetAttributeSingle");
  249. #endif
  250. /* bind attributes to the instance */
  251. for (CipInstanceNum idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx) {
  252. CipInstance *ethernet_link_instance =
  253. GetCipInstance( ethernet_link_class, (CipInstanceNum)(idx + 1) );
  254. InsertAttribute(ethernet_link_instance,
  255. 1,
  256. kCipUdint,
  257. EncodeCipUdint,
  258. NULL,
  259. &g_ethernet_link[idx].interface_speed,
  260. kGetableSingleAndAll);
  261. InsertAttribute(ethernet_link_instance,
  262. 2,
  263. kCipDword,
  264. EncodeCipDword,
  265. NULL,
  266. &g_ethernet_link[idx].interface_flags,
  267. kGetableSingleAndAll);
  268. InsertAttribute(ethernet_link_instance,
  269. 3,
  270. kCip6Usint,
  271. EncodeCipEthernetLinkPhyisicalAddress,
  272. NULL,
  273. &g_ethernet_link[idx].physical_address,
  274. kGetableSingleAndAll);
  275. #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
  276. InsertAttribute(ethernet_link_instance,
  277. 4,
  278. kCipUsint,
  279. EncodeCipUsint,
  280. NULL,
  281. &g_ethernet_link[idx].interface_cntrs,
  282. kGetableSingleAndAll);
  283. InsertAttribute(ethernet_link_instance,
  284. 5,
  285. kCipUsint,
  286. EncodeCipUsint,
  287. NULL,
  288. &g_ethernet_link[idx].media_cntrs,
  289. kGetableSingleAndAll);
  290. #else
  291. InsertAttribute(ethernet_link_instance,
  292. 4,
  293. kCipAny,
  294. EncodeCipEthernetLinkInterfaceCounters,
  295. NULL,
  296. &dummy_attribute_udint,
  297. kGetableAllDummy);
  298. InsertAttribute(ethernet_link_instance,
  299. 5,
  300. kCipAny,
  301. EncodeCipEthernetLinkMediaCounters,
  302. NULL,
  303. &dummy_attribute_udint,
  304. kGetableAllDummy);
  305. #endif /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
  306. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  307. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  308. if (2 == idx) {
  309. /* Interface control of internal switch port is never settable. */
  310. InsertAttribute(ethernet_link_instance,
  311. 6,
  312. kCipAny,
  313. EncodeCipEthernetLinkInterfaceControl,
  314. DecodeCipEthernetLinkInterfaceControl,
  315. &g_ethernet_link[idx].interface_control,
  316. IFACE_CTRL_ACCESS_MODE & ~kSetable);
  317. } else {
  318. InsertAttribute(ethernet_link_instance,
  319. 6,
  320. kCipAny,
  321. EncodeCipEthernetLinkInterfaceControl,
  322. DecodeCipEthernetLinkInterfaceControl,
  323. &g_ethernet_link[idx].interface_control,
  324. IFACE_CTRL_ACCESS_MODE);
  325. }
  326. #else
  327. InsertAttribute(ethernet_link_instance,
  328. 6,
  329. kCipAny,
  330. EncodeCipEthernetLinkInterfaceControl,
  331. NULL,
  332. &s_interface_control,
  333. kGetableAll);
  334. #endif
  335. InsertAttribute(ethernet_link_instance,
  336. 7,
  337. kCipUsint,
  338. EncodeCipUsint,
  339. NULL,
  340. &g_ethernet_link[idx].interface_type,
  341. kGetableSingleAndAll);
  342. InsertAttribute(ethernet_link_instance,
  343. 8,
  344. kCipUsint,
  345. EncodeCipUsint,
  346. NULL,
  347. &dummy_attribute_usint,
  348. kGetableAllDummy);
  349. InsertAttribute(ethernet_link_instance,
  350. 9,
  351. kCipUsint,
  352. EncodeCipUsint,
  353. NULL,
  354. &dummy_attribute_usint, kGetableAllDummy);
  355. InsertAttribute(ethernet_link_instance,
  356. 10,
  357. kCipShortString,
  358. EncodeCipShortString,
  359. NULL,
  360. &g_ethernet_link[idx].interface_label,
  361. IFACE_LABEL_ACCESS_MODE);
  362. InsertAttribute(ethernet_link_instance,
  363. 11,
  364. kCipAny,
  365. EncodeCipEthernetLinkInterfaceCaps,
  366. NULL,
  367. &g_ethernet_link[idx].interface_caps,
  368. kGetableSingleAndAll);
  369. }
  370. } else {
  371. return kEipStatusError;
  372. }
  373. return kEipStatusOk;
  374. }
  375. void CipEthernetLinkSetMac(EipUint8 *p_physical_address) {
  376. for (size_t idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx) {
  377. memcpy(g_ethernet_link[idx].physical_address,
  378. p_physical_address,
  379. sizeof(g_ethernet_link[0].physical_address)
  380. );
  381. }
  382. return;
  383. }
  384. static void EncodeCipEthernetLinkInterfaceCounters(const void *const data,
  385. ENIPMessage *const outgoing_message)
  386. {
  387. /* Suppress unused parameter compiler warning. */
  388. (void)data;
  389. #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
  390. for (size_t i = 0; i < 11; i++) {
  391. /* Encode real values using the access through the array of the
  392. * interface_cntrs union. */
  393. EncodeData(kCipUdint,
  394. g_ethernet_link[instance_id - 1].interface_cntrs.cntr32 + i,
  395. message_router_response);
  396. }
  397. #else
  398. /* Encode the default counter value of 0 */
  399. FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
  400. 11 * sizeof(CipUdint),
  401. outgoing_message);
  402. #endif
  403. }
  404. static void EncodeCipEthernetLinkMediaCounters(const void *const data,
  405. ENIPMessage *const outgoing_message)
  406. {
  407. /* Suppress unused parameter compiler warning. */
  408. (void)data;
  409. #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
  410. for (size_t i = 0; i < 12; i++) {
  411. /* Encode real values using the access through the array of the
  412. * media_cntrs union. */
  413. EncodeData(kCipUdint,
  414. g_ethernet_link[instance_id - 1].media_cntrs.cntr32 + i,
  415. message_router_response);
  416. }
  417. #else
  418. /* Encode the default counter value of 0 */
  419. FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
  420. 12 * sizeof(CipUdint),
  421. outgoing_message);
  422. #endif
  423. }
  424. static void EncodeCipEthernetLinkInterfaceControl(const void *const data,
  425. ENIPMessage *const outgoing_message)
  426. {
  427. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  428. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  429. const CipEthernetLinkInterfaceControl *const interface_control =
  430. data;
  431. #else
  432. /* Suppress unused parameter compiler warning. */
  433. (void)data;
  434. CipEthernetLinkInterfaceControl *interface_control = &s_interface_control;
  435. #endif
  436. EncodeCipWord(&interface_control->control_bits, outgoing_message);
  437. EncodeCipUint(&interface_control->forced_interface_speed, outgoing_message);
  438. }
  439. #define NELEMENTS(x) ( (sizeof(x) / sizeof(x[0]) ) )
  440. static void EncodeCipEthernetLinkInterfaceCaps(const void *const data,
  441. ENIPMessage *const outgoing_message)
  442. {
  443. const CipEthernetLinkMetaInterfaceCapability *const interface_caps = data;
  444. EncodeCipDword(&interface_caps->capability_bits, outgoing_message);
  445. uint16_t selected = interface_caps->speed_duplex_selector;
  446. CipUsint count = 0;
  447. while(selected) { /* count # of bits set */
  448. selected &= selected - 1U; /* clear the least significant bit set */
  449. count++;
  450. }
  451. EncodeCipUsint(&count, outgoing_message);
  452. for (size_t i = 0; i < NELEMENTS(speed_duplex_table); i++) {
  453. if (interface_caps->speed_duplex_selector &
  454. (1U << i) ) {
  455. EncodeCipUint(&speed_duplex_table[i].interface_speed, outgoing_message);
  456. EncodeCipUsint(&speed_duplex_table[i].interface_duplex_mode,
  457. outgoing_message);
  458. }
  459. }
  460. }
  461. #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
  462. EipStatus GetAndClearEthernetLink(
  463. CipInstance *RESTRICT const instance,
  464. CipMessageRouterRequest *const message_router_request,
  465. CipMessageRouterResponse *const message_router_response,
  466. const struct sockaddr *originator_address,
  467. const CipSessionHandle encapsulation_session) {
  468. CipAttributeStruct *attribute = GetCipAttribute(
  469. instance, message_router_request->request_path.attribute_number);
  470. message_router_response->data_length = 0;
  471. message_router_response->reply_service = (0x80
  472. | message_router_request->service);
  473. message_router_response->general_status = kCipErrorAttributeNotSupported;
  474. message_router_response->size_of_additional_status = 0;
  475. EipUint16 attribute_number = message_router_request->request_path
  476. .attribute_number;
  477. if ( (NULL != attribute) && (NULL != attribute->data) ) {
  478. OPENER_TRACE_INFO("GetAndClear attribute %" PRIu16 "\n", attribute_number);
  479. /* The PreGetCallback is not executed here.
  480. * The GetAttributeSingle{EthernetLink}() will do it on our behalf. */
  481. switch (attribute_number) {
  482. case 4: /* fall through */
  483. case 5:
  484. GetAttributeSingleEthernetLink(
  485. instance, message_router_request,
  486. message_router_response,
  487. originator_address,
  488. encapsulation_session);
  489. break;
  490. default:
  491. message_router_response->general_status =
  492. kCipErrorServiceNotSupportedForSpecifiedPath;
  493. break;
  494. }
  495. /* Clearing the counters must be done in the PostGetCallback because it
  496. * is often hardware specific and can't be handled in generic code. */
  497. /* The PostGetCallback is not executed here. The
  498. * GetAttributeSingle{EthernetLink}() should have done it on our
  499. * behalf already.
  500. */
  501. }
  502. return kEipStatusOkSend;
  503. }
  504. #endif /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
  505. #if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && \
  506. 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
  507. static bool IsIfaceControlAllowed(CipInstanceNum instance_id,
  508. CipEthernetLinkInterfaceControl const *iface_cntrl)
  509. {
  510. const CipUsint duplex_mode =
  511. (iface_cntrl->control_bits & kEthLinkIfCntrlForceDuplexFD) ? 1 : 0;
  512. for (size_t i = 0; i < NELEMENTS(speed_duplex_table); i++) {
  513. if (g_ethernet_link[instance_id - 1].interface_caps.speed_duplex_selector &
  514. (1U << i) ) {
  515. if (duplex_mode == speed_duplex_table[i].interface_duplex_mode &&
  516. iface_cntrl->forced_interface_speed ==
  517. speed_duplex_table[i].interface_speed) {
  518. return true;
  519. }
  520. }
  521. }
  522. return false;
  523. }
  524. int DecodeCipEthernetLinkInterfaceControl(
  525. CipEthernetLinkInterfaceControl *const data,
  526. CipMessageRouterRequest *const message_router_request,
  527. CipMessageRouterResponse *const message_router_response) {
  528. CipInstance *const instance = GetCipInstance(
  529. GetCipClass(message_router_request->request_path.class_id),
  530. message_router_request->request_path.instance_number);
  531. int number_of_decoded_bytes = -1;
  532. CipEthernetLinkInterfaceControl if_cntrl;
  533. DecodeCipWord(&if_cntrl.control_bits, message_router_request,
  534. message_router_response);
  535. DecodeCipUint(&if_cntrl.forced_interface_speed, message_router_request,
  536. message_router_response);
  537. if (if_cntrl.control_bits > kEthLinkIfCntrlMaxValid) {
  538. message_router_response->general_status =
  539. kCipErrorInvalidAttributeValue;
  540. return number_of_decoded_bytes;
  541. } else {
  542. if ((0 != (if_cntrl.control_bits & kEthLinkIfCntrlAutonegotiate))
  543. && ((0 != (if_cntrl.control_bits & kEthLinkIfCntrlForceDuplexFD))
  544. || (0 != if_cntrl.forced_interface_speed))) {
  545. message_router_response->general_status =
  546. kCipErrorObjectStateConflict;
  547. return number_of_decoded_bytes;
  548. } else {
  549. if (0 == (if_cntrl.control_bits & kEthLinkIfCntrlAutonegotiate)) {
  550. /* Need to check if a supported mode is forced. */
  551. if (!IsIfaceControlAllowed(instance->instance_number,
  552. &if_cntrl)) {
  553. message_router_response->general_status =
  554. kCipErrorInvalidAttributeValue;
  555. return number_of_decoded_bytes;
  556. }
  557. }
  558. *data = if_cntrl; //write data to attribute
  559. message_router_response->general_status = kCipErrorSuccess;
  560. number_of_decoded_bytes = 4;
  561. }
  562. }
  563. return number_of_decoded_bytes;
  564. }
  565. #endif