@@ -1108,11 +1108,11 @@ static byte_t rx_parse(const uint32_t can_id,
11081108 const bool svc = (can_id & (UINT32_C (1 ) << 25U )) != 0U ;
11091109 const bool bit_23 = (can_id & (UINT32_C (1 ) << 23U )) != 0U ;
11101110 if (svc ) {
1111- is_v1 = is_v1 && !bit_23 ;
11121111 out_v1 -> dst = (byte_t )((can_id >> 7U ) & CANARD_NODE_ID_MAX );
11131112 out_v1 -> port_id = (can_id >> 14U ) & CANARD_SERVICE_ID_MAX ;
11141113 const bool req = (can_id & (UINT32_C (1 ) << 24U )) != 0U ;
11151114 out_v1 -> kind = req ? canard_kind_1v0_request : canard_kind_1v0_response ;
1115+ is_v1 = is_v1 && !bit_23 && (out_v1 -> src != out_v1 -> dst ); // self-addressing not allowed
11161116 } else {
11171117 out_v1 -> dst = CANARD_NODE_ID_ANONYMOUS ;
11181118 const bool is_1v1 = (can_id & (UINT32_C (1 ) << 7U )) != 0U ;
@@ -1141,11 +1141,12 @@ static byte_t rx_parse(const uint32_t can_id,
11411141 const bool svc = (can_id & (UINT32_C (1 ) << 7U )) != 0U ;
11421142 if (svc ) {
11431143 const byte_t dst = (byte_t )((can_id >> 8U ) & CANARD_NODE_ID_MAX );
1144- is_v0 = is_v0 && (dst != 0 ) && (src != 0 ); // 0 reserved for anonymous/broadcast, not for svc.
11451144 out_v0 -> dst = dst ;
11461145 out_v0 -> port_id = (can_id >> 16U ) & 0xFFU ;
11471146 const bool req = (can_id & (UINT32_C (1 ) << 15U )) != 0U ;
11481147 out_v0 -> kind = req ? canard_kind_0v1_request : canard_kind_0v1_response ;
1148+ // Node-ID 0 reserved for anonymous/broadcast, invalid for services. Self-addressing not allowed.
1149+ is_v0 = is_v0 && (dst != 0 ) && (src != 0 ) && (src != dst );
11491150 } else {
11501151 out_v0 -> dst = CANARD_NODE_ID_ANONYMOUS ;
11511152 out_v0 -> port_id = (can_id >> 8U ) & 0xFFFFU ;
@@ -1449,10 +1450,9 @@ static bool rx_session_solve_admission(const rx_session_t* const ses,
14491450 return (fresh && affine ) || (affine && stale ) || (stale && fresh );
14501451}
14511452
1452- // Returns false on OOM, no other failure modes.
14531453// The caller must ensure the frame is of the correct version that matches the subscription (v0/v1).
14541454// The caller must ensure the frame is directed to the local node (broadcast or unicast to the local node-ID).
1455- static bool rx_session_update (canard_subscription_t * const sub ,
1455+ static void rx_session_update (canard_subscription_t * const sub ,
14561456 const canard_us_t ts ,
14571457 const frame_t * const frame ,
14581458 const byte_t iface_index )
@@ -1477,14 +1477,14 @@ static bool rx_session_update(canard_subscription_t* const sub,
14771477 index );
14781478 if (ses == NULL ) {
14791479 sub -> owner -> err .oom += frame -> start ;
1480- return ! frame -> start ;
1480+ return ;
14811481 }
14821482
14831483 // Decide admit or drop.
14841484 const bool admit = rx_session_solve_admission (
14851485 ses , ts , frame -> priority , frame -> start , frame -> toggle , frame -> transfer_id , iface_index );
14861486 if (!admit ) {
1487- return true; // Rejection is not a failure.
1487+ return ;
14881488 }
14891489
14901490 // The frame must be accepted. If this is the start of a new transfer, we must update state.
@@ -1502,7 +1502,7 @@ static bool rx_session_update(canard_subscription_t* const sub,
15021502 ses -> slots [frame -> priority ] = rx_slot_new (sub , ts , frame -> transfer_id , iface_index );
15031503 if (ses -> slots [frame -> priority ] == NULL ) {
15041504 sub -> owner -> err .oom ++ ;
1505- return false ;
1505+ return ;
15061506 }
15071507 CANARD_ASSERT (ses -> slots [frame -> priority ]-> transfer_id == frame -> transfer_id );
15081508 CANARD_ASSERT (ses -> slots [frame -> priority ]-> expected_toggle == frame -> toggle );
@@ -1514,35 +1514,94 @@ static bool rx_session_update(canard_subscription_t* const sub,
15141514 // Accept the frame.
15151515 rx_session_accept (ses , ts , frame );
15161516 CANARD_ASSERT (!frame -> end || (ses -> slots [frame -> priority ] == NULL ));
1517- return true;
1517+ }
1518+
1519+ static int32_t rx_subscription_cavl_compare (const void * const user , const canard_tree_t * const node )
1520+ {
1521+ return ((int32_t )(* (uint32_t * )user )) - ((int32_t )((canard_subscription_t * )node )-> port_id );
1522+ }
1523+
1524+ // Locates the appropriate subscription if the destination is matching and there is a subscription.
1525+ static canard_subscription_t * rx_route (const canard_t * const self , const frame_t * const fr )
1526+ {
1527+ CANARD_ASSERT ((self != NULL ) && (fr != NULL ));
1528+ if ((fr -> dst != CANARD_NODE_ID_ANONYMOUS ) && (fr -> dst != self -> node_id )) {
1529+ return NULL ; // misfiltered
1530+ }
1531+ return (canard_subscription_t * )cavl2_find (
1532+ self -> rx .subscriptions [fr -> kind ], & fr -> port_id , rx_subscription_cavl_compare );
1533+ }
1534+
1535+ // Recompute the filter configuration and apply.
1536+ // Must be invoked after modification of the subscription set and after the local node-ID is changed.
1537+ static void rx_filter_configure (const canard_t * const self )
1538+ {
1539+ if (self -> vtable -> filter == NULL ) {
1540+ return ; // No filtering support, nothing to do.
1541+ }
1542+ (void )self ;
1543+ // TODO not implemented.
15181544}
15191545
15201546// --------------------------------------------- MISC ---------------------------------------------
15211547
1548+ // The splitmix64 PRNG algorithm. Original work by Sebastiano Vigna, released under CC0-1.0 (public domain dedication).
1549+ // Source http://xoshiro.di.unimi.it/splitmix64.c.
1550+ static uint64_t splitmix64 (uint64_t * const state )
1551+ {
1552+ uint64_t z = (* state += 0x9E3779B97F4A7C15ULL );
1553+ z = (z ^ (z >> 30U )) * 0xBF58476D1CE4E5B9ULL ;
1554+ z = (z ^ (z >> 27U )) * 0x94D049BB133111EBULL ;
1555+ return z ^ (z >> 31U );
1556+ }
1557+
1558+ // Obtains a decent-quality pseudo-random number in [0, cardinality).
1559+ static uint64_t random (canard_t * const self , const uint64_t cardinality )
1560+ {
1561+ CANARD_ASSERT (cardinality > 0 );
1562+ return splitmix64 (& self -> prng_state ) % cardinality ;
1563+ }
1564+
1565+ static void node_id_occupancy_reset (canard_t * const self )
1566+ {
1567+ self -> node_id_occupancy_bitmap [0 ] = 1 ; // Reserve 0 for compatibility with v0
1568+ self -> node_id_occupancy_bitmap [1 ] = 0 ;
1569+ }
1570+
1571+ // Records the seen node-ID and reallocates the local node if a collision is found.
1572+ static void node_id_occupancy_update (canard_t * const self , const byte_t src , const byte_t dst )
1573+ {
1574+ (void )self ;
1575+ (void )src ;
1576+ (void )dst ;
1577+ // TODO not implemented.
1578+ // TODO filtering.
1579+ rx_filter_configure (self );
1580+ }
1581+
15221582bool canard_new (canard_t * const self ,
15231583 const canard_vtable_t * const vtable ,
15241584 const canard_mem_set_t memory ,
15251585 const size_t tx_queue_capacity ,
1526- const uint_least8_t node_id ,
15271586 const uint64_t prng_seed ,
15281587 const size_t filter_count ,
15291588 canard_filter_t * const filter_storage )
15301589{
15311590 const bool ok = (self != NULL ) && (vtable != NULL ) && (vtable -> now != NULL ) && (vtable -> tx != NULL ) &&
1532- (vtable -> filter != NULL ) && mem_valid (memory .tx_transfer ) && mem_valid (memory .tx_frame ) &&
1533- mem_valid (memory .rx_session ) && mem_valid (memory .rx_payload ) &&
1534- ((filter_count == 0U ) || (filter_storage != NULL )) && (node_id <= CANARD_NODE_ID_MAX );
1591+ mem_valid (memory .tx_transfer ) && mem_valid (memory .tx_frame ) && mem_valid (memory .rx_session ) &&
1592+ mem_valid (memory .rx_payload ) && ((filter_count == 0U ) || (filter_storage != NULL ));
15351593 if (ok ) {
15361594 (void )memset (self , 0 , sizeof (* self ));
1537- self -> node_id = (node_id <= CANARD_NODE_ID_MAX ) ? node_id : CANARD_NODE_ID_ANONYMOUS ;
15381595 self -> tx .fd = true;
15391596 self -> tx .queue_capacity = tx_queue_capacity ;
15401597 self -> rx .filter_count = filter_count ;
15411598 self -> rx .filters = filter_storage ;
15421599 self -> mem = memory ;
1543- self -> prng_state = prng_seed ;
1600+ self -> prng_state = prng_seed ^ ( uintptr_t ) self ;
15441601 self -> vtable = vtable ;
15451602 self -> unicast_sub .index_port_id = TREE_NULL ;
1603+ self -> node_id = (byte_t )(random (self , CANARD_NODE_ID_MAX ) + 1U ); // [1, 127]
1604+ node_id_occupancy_reset (self );
15461605 }
15471606 return ok ;
15481607}
@@ -1563,11 +1622,21 @@ void canard_destroy(canard_t* const self)
15631622 (void )memset (self , 0 , sizeof (* self )); // UAF safety
15641623}
15651624
1625+ bool canard_set_node_id (canard_t * const self , const uint_least8_t node_id )
1626+ {
1627+ const bool ok = (self != NULL ) && (node_id <= CANARD_NODE_ID_MAX );
1628+ if (ok && (node_id != self -> node_id )) {
1629+ self -> node_id = node_id ;
1630+ node_id_occupancy_reset (self );
1631+ rx_filter_configure (self );
1632+ }
1633+ return ok ;
1634+ }
1635+
15661636void canard_poll (canard_t * const self , const uint_least8_t tx_ready_iface_bitmap )
15671637{
15681638 if (self != NULL ) {
15691639 const canard_us_t now = self -> vtable -> now (self );
1570-
15711640 // Drop stale sessions to reclaim memory. This happens when remote peers cease sending data.
15721641 // The oldest is held alive until its session timeout has expired, but notice that it may be different
15731642 // depending on the subscription instance if large transfer-ID values are used.
@@ -1583,7 +1652,6 @@ void canard_poll(canard_t* const self, const uint_least8_t tx_ready_iface_bitmap
15831652 rx_session_destroy (ses );
15841653 }
15851654 }
1586-
15871655 // Process the TX pipeline.
15881656 tx_expire (self , now ); // deadline maintenance first to keep queue pressure bounded
15891657 FOREACH_IFACE (i ) { // submit queued frames through all currently writable interfaces
@@ -1594,19 +1662,50 @@ void canard_poll(canard_t* const self, const uint_least8_t tx_ready_iface_bitmap
15941662 }
15951663}
15961664
1597- // bool canard_ingest_frame(canard_t* const self,
1598- // const canard_us_t timestamp,
1599- // const uint_least8_t iface_index,
1600- // const uint32_t extended_can_id,
1601- // const canard_bytes_t can_data)
1602- // {
1603- // bool ok = (self != NULL) && (timestamp >= 0) && (iface_index < CANARD_IFACE_COUNT) &&
1604- // (extended_can_id < (UINT32_C(1) << 29U)) && ((can_data.size == 0) || (can_data.data != NULL));
1605- // if (ok) {
1606- // ok = false; // TODO
1607- // }
1608- // return ok;
1609- // }
1665+ static void ingest_frame (canard_t * const self ,
1666+ const canard_us_t timestamp ,
1667+ const uint_least8_t iface_index ,
1668+ const frame_t frame )
1669+ {
1670+ // Update the node-ID occupancy/collision monitoring states before routing the message.
1671+ // We do this only on start frames because non-start frames have the version detection ambiguity,
1672+ // which is not a problem for the routing logic (new states can only be created on start frames),
1673+ // but it may cause phantom occupancy detection. Also, doing it only on start frames reduces the load.
1674+ if (frame .start ) {
1675+ node_id_occupancy_update (self , frame .src , frame .dst );
1676+ }
1677+ // Route the frame to the appropriate destination internally.
1678+ canard_subscription_t * const sub = rx_route (self , & frame );
1679+ if (sub != NULL ) {
1680+ rx_session_update (sub , timestamp , & frame , iface_index );
1681+ }
1682+ }
1683+
1684+ bool canard_ingest_frame (canard_t * const self ,
1685+ const canard_us_t timestamp ,
1686+ const uint_least8_t iface_index ,
1687+ const uint32_t extended_can_id ,
1688+ const canard_bytes_t can_data )
1689+ {
1690+ const bool ok = (self != NULL ) && (timestamp >= 0 ) && (iface_index < CANARD_IFACE_COUNT ) &&
1691+ (extended_can_id < (UINT32_C (1 ) << 29U )) && ((can_data .size == 0 ) || (can_data .data != NULL ));
1692+ if (ok ) {
1693+ frame_t frs [2 ] = { { 0 }, { 0 } };
1694+ const byte_t parsed = rx_parse (extended_can_id , can_data , & frs [0 ], & frs [1 ]);
1695+ if (parsed == 0 ) {
1696+ self -> err .rx_frame ++ ;
1697+ }
1698+ if ((parsed & 1U ) != 0 ) {
1699+ CANARD_ASSERT (canard_kind_version (frs [0 ].kind ) == 0 );
1700+ ingest_frame (self , timestamp , iface_index , frs [0 ]);
1701+ }
1702+ if ((parsed & 2U ) != 0 ) {
1703+ CANARD_ASSERT (canard_kind_version (frs [1 ].kind ) == 1 );
1704+ ingest_frame (self , timestamp , iface_index , frs [1 ]);
1705+ }
1706+ }
1707+ return ok ;
1708+ }
16101709
16111710uint16_t canard_0v1_crc_seed_from_data_type_signature (const uint64_t data_type_signature )
16121711{
0 commit comments