@@ -86,6 +86,9 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL);
8686#define NRF5_BROADCAST_ADDRESS 0xffff
8787#define NRF5_NO_SHORT_ADDRESS_ASSIGNED 0xfffe
8888
89+ /* IE constants */
90+ #define IE_VENDOR_SIZE_MIN 3 /* Minimum vendor OUI size in bytes */
91+
8992#if defined(CONFIG_COOP_ENABLED )
9093#define OT_WORKER_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)
9194#else
@@ -98,6 +101,9 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL);
98101#define PSDU_LENGTH (psdu ) ((psdu)[0])
99102#define PSDU_DATA (psdu ) ((psdu) + 1)
100103
104+ #define CSL_IE_SIZE (6) /* Buffer for CSL IE: 2 bytes header + 4 bytes content */
105+ #define LM_IE_SIZE (32) /* Buffer for LM IE: 2 bytes header + 30 bytes content */
106+
101107enum nrf5_pending_events {
102108 PENDING_EVENT_FRAME_RECEIVED , /* Radio has received new frame */
103109 PENDING_EVENT_RX_FAILED , /* The RX failed */
@@ -125,6 +131,7 @@ struct nrf5_header_ie_csl_reduced {
125131
126132struct nrf5_header_ie_link_metrics {
127133 uint8_t vendor_oui [IE_VENDOR_SIZE_MIN ];
134+ uint8_t subtype ; /* Vendor-specific subtype/probing ID */
128135 uint8_t lqi_token ;
129136 uint8_t link_margin_token ;
130137 uint8_t rssi_token ;
@@ -348,9 +355,20 @@ static int nrf5_set_tx_power(uint16_t channel)
348355}
349356
350357#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER ) || defined(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT )
358+ /**
359+ * @brief Set ACK data for both short and extended addresses
360+ *
361+ * This function handles the address conversion and calls the nRF driver
362+ * to set ACK data for both address types.
363+ *
364+ * @param short_addr Short address
365+ * @param ext_addr Extended address
366+ * @param header_ie_buf Buffer containing the IE data
367+ * @param ie_length Length of the IE data in bytes
368+ * @return 0 on success, negative error code on failure
369+ */
351370static int nrf5_ack_data_set (uint16_t short_addr , const otExtAddress * ext_addr ,
352- const struct nrf5_header_ie * header_ie )
353-
371+ const uint8_t * header_ie_buf , size_t ie_length )
354372{
355373 uint8_t ext_addr_le [EXTENDED_ADDRESS_SIZE ];
356374 uint8_t short_addr_le [SHORT_ADDRESS_SIZE ];
@@ -363,17 +381,15 @@ static int nrf5_ack_data_set(uint16_t short_addr, const otExtAddress *ext_addr,
363381 sys_memcpy_swap (ext_addr_le , ext_addr -> m8 , EXTENDED_ADDRESS_SIZE );
364382
365383 if (short_addr != NRF5_NO_SHORT_ADDRESS_ASSIGNED ) {
366- nrf_802154_ack_data_set (short_addr_le , false, header_ie ,
367- header_ie -> length + IE_HEADER_SIZE , NRF_802154_ACK_DATA_IE );
384+ nrf_802154_ack_data_set (short_addr_le , false, header_ie_buf , ie_length ,
385+ NRF_802154_ACK_DATA_IE );
368386 }
369- nrf_802154_ack_data_set (ext_addr_le , true, header_ie , header_ie -> length + IE_HEADER_SIZE ,
387+ nrf_802154_ack_data_set (ext_addr_le , true, header_ie_buf , ie_length ,
370388 NRF_802154_ACK_DATA_IE );
371389
372390 return 0 ;
373391}
374- #endif
375392
376- #if defined(CONFIG_OPENTHREAD_CSL_RECEIVER )
377393static int nrf5_ack_data_clear (uint16_t short_addr , const otExtAddress * ext_addr )
378394{
379395 uint8_t ext_addr_le [EXTENDED_ADDRESS_SIZE ];
@@ -395,6 +411,42 @@ static int nrf5_ack_data_clear(uint16_t short_addr, const otExtAddress *ext_addr
395411}
396412#endif
397413
414+ #if defined(CONFIG_OPENTHREAD_CSL_RECEIVER )
415+ /**
416+ * @brief Create CSL IE using structured format with bit fields
417+ *
418+ * Creates a CSL Information Element using the defined structs with proper IEEE 802.15.4 format
419+ *
420+ * @param csl_period CSL period value
421+ * @param ie_buffer Buffer to store the IE (must be at least sizeof(struct nrf5_header_ie) bytes)
422+ * @return Length of the created IE in bytes
423+ */
424+ static size_t create_csl_ie (uint32_t csl_period , uint8_t * ie_buffer )
425+ {
426+ if (ie_buffer == NULL ) {
427+ return 0 ;
428+ }
429+
430+ struct nrf5_header_ie csl_ie ;
431+ const uint8_t csl_element_id = NRF5_HEADER_IE_ELEMENT_ID_CSL_IE ;
432+
433+ memset (& csl_ie , 0 , sizeof (csl_ie ));
434+
435+ csl_ie .length = sizeof (struct nrf5_header_ie_csl_reduced );
436+ csl_ie .element_id_low = csl_element_id & 0x01 ;
437+ csl_ie .element_id_high = (csl_element_id >> 1 ) & 0x7f ;
438+ csl_ie .type = NRF5_IE_TYPE_HEADER ;
439+ csl_ie .content .csl_reduced .csl_phase = sys_cpu_to_le16 (0 );
440+ csl_ie .content .csl_reduced .csl_period = sys_cpu_to_le16 (csl_period );
441+
442+ size_t csl_ie_size = sizeof (uint16_t ) + sizeof (struct nrf5_header_ie_csl_reduced );
443+
444+ memcpy (ie_buffer , & csl_ie , csl_ie_size );
445+
446+ return csl_ie_size ;
447+ }
448+ #endif
449+
398450static void nrf5_get_eui64 (uint8_t * mac )
399451{
400452 __ASSERT (mac != NULL , "nrf5_get_eui64: mac is NULL" );
@@ -644,7 +696,6 @@ static bool nrf5_tx_csma_ca(otRadioFrame *frame, uint8_t *payload)
644696 .use_metadata_value = true,
645697 .power = get_transmit_power_for_channel (frame -> mChannel ),
646698 },
647-
648699 };
649700
650701 nrf_802154_csma_ca_max_backoffs_set (frame -> mInfo .mTxInfo .mMaxCsmaBackoffs );
@@ -1440,17 +1491,6 @@ otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShort
14401491
14411492 ARG_UNUSED (aInstance );
14421493
1443- const struct nrf5_header_ie header_ie = {
1444- .length = sizeof (struct nrf5_header_ie_csl_reduced ),
1445- .element_id_high = (NRF5_HEADER_IE_ELEMENT_ID_CSL_IE ) >> 1U ,
1446- .element_id_low = (NRF5_HEADER_IE_ELEMENT_ID_CSL_IE ) & 0x01 ,
1447- .type = NRF5_IE_TYPE_HEADER ,
1448- .content .csl_reduced = {
1449- .csl_phase = 0 ,
1450- .csl_period = aCslPeriod ,
1451- },
1452- };
1453-
14541494 nrf_802154_csl_writer_period_set (aCslPeriod );
14551495#if defined(CONFIG_NRF_802154_SER_HOST )
14561496 nrf5_data .csl .period = aCslPeriod ;
@@ -1459,7 +1499,10 @@ otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShort
14591499 if (aCslPeriod == 0 ) {
14601500 result = nrf5_ack_data_clear (aShortAddr , aExtAddr );
14611501 } else {
1462- result = nrf5_ack_data_set (aShortAddr , aExtAddr , & header_ie );
1502+ uint8_t csl_ie_buf [CSL_IE_SIZE ];
1503+ size_t ie_length = create_csl_ie (aCslPeriod , csl_ie_buf );
1504+
1505+ result = nrf5_ack_data_set (aShortAddr , aExtAddr , csl_ie_buf , ie_length );
14631506 }
14641507
14651508 return result ? OT_ERROR_FAILED : OT_ERROR_NONE ;
@@ -1542,34 +1585,123 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh
15421585}
15431586
15441587#if defined(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT )
1588+
1589+ /**
1590+ * @brief Set vendor IE header for link metrics using structured format
1591+ *
1592+ * This function creates a properly formatted IEEE 802.15.4 vendor-specific header IE
1593+ * for OpenThread link metrics probing
1594+ *
1595+ * +---------------------------------+----------------------+
1596+ * | Length | Element ID | Type=0 | Vendor OUI |
1597+ * +-----------+------------+--------+----------------------+
1598+ * | Bytes: 0-1 | 2-4 |
1599+ * +-----------+---------------------+----------------------+
1600+ * | Bits: 0-6 | 7-14 | 15 | IE_VENDOR_THREAD_OUI |
1601+ * +-----------+------------+--------+----------------------|
1602+ *
1603+ * Thread v1.2.1 Spec., 4.11.3.4.4.6
1604+ * +---------------------------------+-------------------+------------------+
1605+ * | Vendor Specific Information |
1606+ * +---------------------------------+-------------------+------------------+
1607+ * | 5 | 6 | 7 (optional) |
1608+ * +---------------------------------+-------------------+------------------+
1609+ * | IE_VENDOR_THREAD_ACK_PROBING_ID | LINK_METRIC_TOKEN | LINK_METRIC_TOKEN|
1610+ * |---------------------------------|-------------------|------------------|
1611+ *
1612+ * @param lqi Include LQI metric
1613+ * @param link_margin Include link margin metric
1614+ * @param rssi Include RSSI metric
1615+ * @param ie_header Buffer to store the IE header
1616+ */
1617+ static void set_vendor_ie_header_lm (bool lqi , bool link_margin , bool rssi , uint8_t * ie_header )
1618+ {
1619+ /* OpenThread vendor-specific constants */
1620+ const uint8_t ie_vendor_id = NRF5_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE ;
1621+ const uint8_t ie_vendor_thread_ack_probing_id = 0x00 ;
1622+ const uint32_t ie_vendor_thread_oui = 0xeab89b ;
1623+ const uint8_t ie_vendor_thread_rssi_token = 0x01 ;
1624+ const uint8_t ie_vendor_thread_margin_token = 0x02 ;
1625+ const uint8_t ie_vendor_thread_lqi_token = 0x03 ;
1626+
1627+ struct nrf5_header_ie vendor_ie ;
1628+ uint8_t link_metrics_data_len = (uint8_t )lqi + (uint8_t )link_margin + (uint8_t )rssi ;
1629+ uint8_t token_offset ;
1630+
1631+ __ASSERT (link_metrics_data_len <= 2 , "Thread limits to 2 metrics at most" );
1632+ __ASSERT (ie_header , "Invalid argument" );
1633+
1634+ if (link_metrics_data_len == 0 ) {
1635+ ie_header [0 ] = 0 ;
1636+ return ;
1637+ }
1638+
1639+ /* Clear the structure */
1640+ memset (& vendor_ie , 0 , sizeof (vendor_ie ));
1641+
1642+ vendor_ie .length = 4 + link_metrics_data_len ;
1643+ vendor_ie .element_id_low = ie_vendor_id & 0x01 ;
1644+ vendor_ie .element_id_high = (ie_vendor_id >> 1 ) & 0x7f ;
1645+ vendor_ie .type = NRF5_IE_TYPE_HEADER ;
1646+ vendor_ie .content .link_metrics .vendor_oui [0 ] = (ie_vendor_thread_oui ) & 0xff ;
1647+ vendor_ie .content .link_metrics .vendor_oui [1 ] = (ie_vendor_thread_oui >> 8 ) & 0xff ;
1648+ vendor_ie .content .link_metrics .vendor_oui [2 ] = (ie_vendor_thread_oui >> 16 ) & 0xff ;
1649+ vendor_ie .content .link_metrics .subtype = ie_vendor_thread_ack_probing_id ;
1650+
1651+ memcpy (ie_header , & vendor_ie , 2 );
1652+
1653+ uint8_t * content_ptr = ie_header + 2 ;
1654+
1655+ content_ptr [0 ] = (ie_vendor_thread_oui ) & 0xff ;
1656+ content_ptr [1 ] = (ie_vendor_thread_oui >> 8 ) & 0xff ;
1657+ content_ptr [2 ] = (ie_vendor_thread_oui >> 16 ) & 0xff ;
1658+ content_ptr [3 ] = ie_vendor_thread_ack_probing_id ;
1659+
1660+ token_offset = 4 ;
1661+
1662+ if (lqi ) {
1663+ content_ptr [token_offset ++ ] = ie_vendor_thread_lqi_token ;
1664+ }
1665+
1666+ if (link_margin ) {
1667+ content_ptr [token_offset ++ ] = ie_vendor_thread_margin_token ;
1668+ }
1669+
1670+ if (rssi ) {
1671+ content_ptr [token_offset ++ ] = ie_vendor_thread_rssi_token ;
1672+ }
1673+ }
1674+
15451675otError otPlatRadioConfigureEnhAckProbing (otInstance * aInstance , otLinkMetrics aLinkMetrics ,
1546- otShortAddress aShortAddress ,
1676+ const otShortAddress aShortAddress ,
15471677 const otExtAddress * aExtAddress )
15481678{
1549- int result ;
1550-
15511679 ARG_UNUSED (aInstance );
15521680
1553- const struct nrf5_header_ie header_ie = {
1554- .length = sizeof (struct nrf5_header_ie_link_metrics ),
1555- .element_id_high = (NRF5_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE ) >> 1U ,
1556- .element_id_low = (NRF5_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE ) & 0x01 ,
1557- .type = NRF5_IE_TYPE_HEADER ,
1558- .content .link_metrics = {
1559- .vendor_oui [0 ] = (IE_VENDOR_THREAD_OUI >> 0 ) & 0xff ,
1560- .vendor_oui [1 ] = (IE_VENDOR_THREAD_OUI >> 8 ) & 0xff ,
1561- .vendor_oui [2 ] = (IE_VENDOR_THREAD_OUI >> 16 ) & 0xff ,
1562- .lqi_token = aLinkMetrics .mLqi ? IE_VENDOR_THREAD_LQI_TOKEN : 0 ,
1563- .link_margin_token = aLinkMetrics .mLinkMargin
1564- ? IE_VENDOR_THREAD_MARGIN_TOKEN
1565- : 0 ,
1566- .rssi_token = aLinkMetrics .mRssi ? IE_VENDOR_THREAD_RSSI_TOKEN : 0 ,
1567- },
1568- };
1681+ /* Use the proper IEEE 802.15.4 driver configure interface */
1682+ uint8_t header_ie_buf [LM_IE_SIZE ];
15691683
1570- result = nrf5_ack_data_set (aShortAddress , aExtAddress , & header_ie );
1684+ /* Validate addresses */
1685+ if (aShortAddress == NRF5_BROADCAST_ADDRESS || aExtAddress == NULL ) {
1686+ return OT_ERROR_INVALID_ARGS ;
1687+ }
15711688
1572- return result ? OT_ERROR_FAILED : OT_ERROR_NONE ;
1689+ /* Create the vendor-specific IE header */
1690+ set_vendor_ie_header_lm (aLinkMetrics .mLqi , aLinkMetrics .mLinkMargin , aLinkMetrics .mRssi ,
1691+ header_ie_buf );
1692+
1693+ /* If no metrics requested, clear the ACK data using the helper function */
1694+ if (header_ie_buf [0 ] == 0 ) {
1695+ return nrf5_ack_data_clear (aShortAddress , aExtAddress ) ? OT_ERROR_FAILED
1696+ : OT_ERROR_NONE ;
1697+ }
1698+
1699+ /* Calculate IE length: first byte contains length field */
1700+ uint8_t ie_length = (header_ie_buf [0 ] & 0x7f ) + 2 ; /* +2 for the header itself */
1701+
1702+ return nrf5_ack_data_set (aShortAddress , aExtAddress , header_ie_buf , ie_length )
1703+ ? OT_ERROR_FAILED
1704+ : OT_ERROR_NONE ;
15731705}
15741706#endif
15751707
0 commit comments