/* Cesar project {{{ * * Copyright (C) 2008 Spidcom * * <<>> * * }}} */ /** * \file cp/beacon/src/beacon.c * \brief Beacon Module. * \ingroup cp_beacon * * The beacon module is responsible for : * - When the station acts as CCo: * - Create the central beacon. * - Get the discover info from the others stations. */ /* Public headers. */ #include "common/std.h" #include "lib/fixed.h" #include "lib/slist.h" #include "lib/stats.h" #include "string.h" #include "hal/arch/arch.h" #include "cp/defs.h" #include "cp/cp.h" #include "cp/fsm/fsm.h" #include "cp/beacon/beacon.h" #include "cp/cco/bw/bw.h" #include "mac/common/timings.h" #include "mac/common/ntb.h" #include "hal/gpio/gpio.h" #include "bsu/beacon/beacon.h" #include "bsu/bsu.h" #include "mac/ca/ca.h" /* Private headers. */ #include "cp/beacon/inc/beacon.h" #include "cp/inc/context.h" #include "cp/inc/trace.h" /* Config headers. */ #include "config/beacon.h" /** Default interval between SPOC updates. */ #define CP_BEACON_SPOC_UPDATE_INTERVAL_DEFAULT_MS \ CONFIG_BEACON_SPOC_UPDATE_INTERVAL_DEFAULT_MS /* In CSMA_ONLY mode, the beacon periods are always "merged", so this margin * value is always applied and have for consequence to send the central beacon * after this CA_ACCESS_MERGE_MARGIN_TCK. * Knowing this, when the plug acts as a STA, it should program the timer * after this delay plus the necessary time to receive and process the central * beacon. This warning verifies those two values are compatible. */ #if (CA_ACCESS_MERGE_MARGIN_TCK >= CP_TIMER_OFFSET_STA) # warning "CP_TIMER_OFFSET_STA too small" #endif /** Timer expires. * \param ctx the CP context. */ static void cp_beacon_timer_expires (cp_t *ctx) { dbg_assert (ctx); /* Post an event in the FSM beacon timer expires. */ cp_fsm_post_urgent_new_event (ctx, bare, BEACON_TIMER_EXPIRES); } void cp_beacon_fill_discover_info ( cp_t *ctx, bsu_beacon_bmi_discover_info_t *discover) { cp_sta_own_data_t *sta = cp_sta_mgr_get_sta_own_data (ctx); bitstream_t stream; bitstream_write_init (&stream, &discover->info_data, 4); bitstream_write (&stream, 0, 1); bitstream_write (&stream, CP_CCO_LEVEL, 2); bitstream_write (&stream, CP_PCO_CAP, 1); bitstream_write (&stream, CP_BACKUP_CCO_CAP, 1); bitstream_write (&stream, cp_sta_own_data_get_cco_status (ctx), 1); bitstream_write (&stream, cp_sta_own_data_get_pco_status (ctx), 1); bitstream_write (&stream, false, 1); bitstream_write (&stream, cp_sta_mgr_get_num_discovered_stas (ctx), 8); bitstream_write (&stream, cp_sta_mgr_get_num_discovered_net (ctx), 8); bitstream_write (&stream, cp_sta_own_data_get_authenticated_status (ctx), 1); bitstream_write (&stream, sta->cco_prefered, 1); bitstream_write (&stream, 0, 6); bitstream_finalise (&stream); u32 last = bitstream_direct_read (&ctx->beacon.discover_info_last, 1, 31); u32 new = bitstream_direct_read (&discover->info_data, 1, 31); /* Updated ? */ bitstream_direct_write (&discover->info_data, 0, new != last, 1); ctx->beacon.discover_info_last = discover->info_data; discover->present = true; } void cp_beacon_fill (cp_t *ctx, bsu_beacon_t *beacon) { cp_sta_own_data_t *sta; cp_net_t *net = NULL; uint nb, ns; dbg_assert (beacon); beacon->beacon_period_start_date = bsu_aclf_beacon_period_start_date_next (ctx->bsu_aclf); sta = cp_sta_mgr_get_sta_own_data (ctx); if (cp_sta_own_data_get_tei (ctx)) net = cp_sta_mgr_get_our_avln (ctx); beacon->vf.stei = cp_sta_own_data_get_tei (ctx); beacon->vf.nid = cp_sta_own_data_get_nid (ctx); beacon->vf.hm = sta->hybrid_mode; beacon->vf.ncnr = CP_BEACON_NON_COORDINATED_NETWORK; beacon->vf.npsm = CP_BEACON_NPSM_NOT_ACTIVE; beacon->vf.numslots = 0; beacon->vf.slotusage = cp_sta_mgr_get_slot_usage (ctx); beacon->vf.slotid = net == NULL ? 0 : cp_net_get_slot_id (ctx, net); beacon->vf.aclsss = 0; beacon->vf.hoip = ctx->beacon.hoip.hoip_flag; beacon->vf.rtsbf = false; beacon->vf.nm = net == NULL ? MAC_NM_CSMA_ONLY : cp_net_get_nm (ctx, net); beacon->vf.ccocap = CP_CCO_LEVEL; /* regions. */ cp_cco_region_alloc_t *region; for (nb = 0, region = cp_cco_region_alloc_get_first ( ctx, &ctx->region.region_list); region; region = cp_cco_region_alloc_get_next ( ctx, &ctx->region.region_list, region), nb++) { beacon->bmis.region.region[nb].rt = region->type; beacon->bmis.region.region[nb].end_time_atu = region->end_time_atu; } beacon->bmis.region.nb = nb; /* Store the persistent schedules. */ uint sched_id = 0; cp_cco_bw_alloc_t *sched; for (nb = 0; nb < 8; nb++) { for (ns = 0, sched = cp_cco_bw_alloc_get_first_persistent ( ctx, &ctx->bw.alloc_list, nb); sched; sched = cp_cco_bw_alloc_get_next_persistent ( ctx, &ctx->bw.alloc_list, sched), ns++) { dbg_assert (sched_id < BSU_BEACON_BMI_PERSISTENT_SCHEDULE_MAX); beacon->bmis.ps.ps[sched_id].sais[ns].stpf = sched->stpf; beacon->bmis.ps.ps[sched_id].sais[ns].glid = sched->glid; beacon->bmis.ps.ps[sched_id].sais[ns].start_time_atu = sched->start_time_atu; beacon->bmis.ps.ps[sched_id].sais[ns].end_time_atu = sched->end_time_atu; if (ns == 0) { beacon->bmis.ps.ps[sched_id].pscd = sched->pscd; beacon->bmis.ps.ps[sched_id].cscd = sched->cscd; } } if (ns) { beacon->bmis.ps.ps[sched_id].ns = ns; sched_id++; } } beacon->bmis.ps.nb = sched_id; /* Store the not persistent schedules. */ for (ns = 0, sched = cp_cco_bw_alloc_get_first_with_persistence ( ctx, &ctx->bw.alloc_list, CP_CCO_BW_ALLOC_PERSISTENCE_NOT_PERSISTENT); sched; sched = cp_cco_bw_alloc_get_next_with_persistence ( ctx, &ctx->bw.alloc_list, sched), ns++) { dbg_assert (ns < BSU_BEACON_BMIS_SCHEDULES_SAI_MAX); beacon->bmis.nps.sais[ns].stpf = sched->stpf; beacon->bmis.nps.sais[ns].glid = sched->glid; beacon->bmis.nps.sais[ns].start_time_atu = sched->start_time_atu; beacon->bmis.nps.sais[ns].end_time_atu = sched->end_time_atu; } beacon->bmis.nps.ns = ns; cp_beacon_fill_discovery_bentries (ctx, beacon); /* If the EKS countdown is not 0. */ if (ctx->beacon.eks.kccd) { beacon->bmis.eks.present = true; beacon->bmis.eks.kccd = ctx->beacon.eks.kccd; beacon->bmis.eks.kbc = ctx->beacon.eks.kbc; beacon->bmis.eks.new_eks = ctx->beacon.eks.new_eks; } else beacon->bmis.eks.present = false; /* If the handover countdown is not 0. */ if (ctx->beacon.hoip.hoipcd) { beacon->bmis.handover.present = true; beacon->bmis.handover.hcd = ctx->beacon.hoip.hoipcd; beacon->bmis.handover.tei = ctx->beacon.hoip.cco; } else beacon->bmis.handover.present = false; /* If the hybrid mode countdown is not 0. */ if (ctx->beacon.hm.hmcd) { beacon->bmis.change_hm.present = true; beacon->bmis.change_hm.hmccd = ctx->beacon.hm.hmcd; beacon->bmis.change_hm.newhm = ctx->beacon.hm.hm; } else beacon->bmis.change_hm.present = false; /* If the snid countdown is not 0. */ if (ctx->beacon.snids.snidcd) { beacon->bmis.change_snid.present = true; beacon->bmis.change_snid.snidccd = ctx->beacon.snids.snidcd; beacon->bmis.change_snid.new_snid = ctx->beacon.snids.snid; } else beacon->bmis.change_snid.present = false; beacon->bmis.bpsto.present = (beacon->vf.nm == MAC_NM_CSMA_ONLY); beacon->bmis.relocation.present = false; beacon->bmis.aclsc.present = false; beacon->bmis.cns.present = false; beacon->bmis.mac_address.present = true; beacon->bmis.mac_address.mac_address = cp_sta_own_data_get_mac_address (ctx); } /** * The beacon received from the AVLN. * \param ctx the module context. * \param beacon The beacon received. * * Add the beacon to the list of received beacon, and raise the flag * beacon received in STA_CORE */ void cp_beacon_receive (void *ul, bsu_beacon_t *beacon) { cp_t *ctx = (cp_t *) ul; dbg_assert (ul); dbg_assert (beacon); interface_beacon (ctx->interface, beacon); if (beacon->params.direction == BSU_BEACON_DIRECTION_FROM_PLC || beacon->params.direction == BSU_BEACON_DIRECTION_FROM_BSU) { beacon->next = NULL; slist_push_back (ctx->beacon.list., beacon, bare); cp_sta_core_signal_recv_beacon_event (ctx); } else { /* The station is CCo, release the beacon. */ blk_release (beacon); } } /** * Set the sai in the schedule. */ static inline void cp_beacon_set_sai_in_sched (cp_cco_bw_alloc_t *sched, bsu_beacon_sai_t* sai) { sched->end_time_atu = sai->end_time_atu; sched->glid = sai->glid | 0x80; sched->stpf = sai->stpf; } void cp_beacon_sta_compute_schedules (cp_t *ctx, bsu_beacon_t *beacon) { dbg_assert (ctx); dbg_assert (beacon); uint i, j; /** Organize the schedules contained in beacon_data as a set. */ set_t set_schedules; cp_cco_bw_alloc_t *sched; set_init (&set_schedules, CALLBACK (cp_cco_bw_alloc_less)); for (i = 0; i < beacon->bmis.nps.ns; i++) { sched = cp_cco_bw_alloc_init(ctx); cp_beacon_set_sai_in_sched (sched, &beacon->bmis.nps.sais[i]); sched->persistence = CP_CCO_BW_ALLOC_PERSISTENCE_NOT_PERSISTENT; if (sched->stpf) sched->start_time_atu = beacon->bmis.nps.sais[i].start_time_atu; cp_cco_bw_alloc_add (ctx, &set_schedules, sched); slab_release (sched); } for (i = 0; i < beacon->bmis.ps.nb; i++) { for (j = 0; j < beacon->bmis.ps.ps[i].ns; j++) { sched = cp_cco_bw_alloc_init(ctx); sched->cscd = beacon->bmis.ps.ps[i].cscd; sched->pscd = beacon->bmis.ps.ps[i].pscd; sched->persistence = CP_CCO_BW_ALLOC_PERSISTENCE_PERSISTENT; cp_beacon_set_sai_in_sched ( sched, &beacon->bmis.ps.ps[i].sais[j]); if (sched->stpf) sched->start_time_atu = beacon->bmis.ps.ps[i].sais[j].start_time_atu; cp_cco_bw_alloc_add (ctx, &set_schedules, sched); slab_release (sched); } } /** Organize the regions contained in beacon_data as a set. */ cp_cco_region_alloc_t *region; set_t set_regions; set_init (&set_regions, CALLBACK (cp_cco_region_alloc_less)); for (i = 0; i < beacon->bmis.region.nb; i++) { region = cp_cco_region_alloc_init (ctx); region->type = beacon->bmis.region.region[i].rt; region->end_time_atu = beacon->bmis.region.region[i].end_time_atu; cp_cco_region_alloc_add (ctx, &set_regions, region); slab_release (region); } cp_cco_bw_allocation_list_swap (ctx, &set_schedules); cp_cco_region_allocation_list_swap (ctx, &set_regions); } cp_sta_t * cp_beacon_update_sta_peer (cp_t *ctx, bsu_beacon_t *beacon, cp_net_t *net, mac_t mac) { cp_sta_t *sta; dbg_assert (net); mac = mac == MAC_ZERO ? MAC_BROADCAST : mac; sta = cp_sta_mgr_sta_add (ctx, net, beacon->vf.stei, mac); dbg_assert (sta); if (beacon->vf.bt == BSU_BEACON_TYPE_CENTRAL) { cp_net_set_cco (ctx, net, beacon->vf.stei); cp_sta_set_authenticated (ctx, sta, true); } else if (beacon->vf.bt == BSU_BEACON_TYPE_PROXY) { cp_net_set_pco (ctx, net, beacon->vf.stei); cp_sta_set_authenticated (ctx, sta, true); } if (beacon->bmis.discover_info.present) { cp_beacon_discover_info_t info_data = *(cp_beacon_discover_info_t*) &beacon->bmis.discover_info.info_data; sta->is_backup_cco = info_data.backup_cco_status; sta->cco_cap = info_data.cco_cap; sta->pco_cap = info_data.proxy_net_cap; sta->backup_cco_cap = info_data.backup_cco_cap; sta->numDisSta = info_data.num_dis_sta; sta->numDisNet = info_data.num_dis_net; if (info_data.cco_status) cp_net_set_cco (ctx, net, beacon->vf.stei); if (info_data.pco_status) cp_net_set_pco (ctx, net, beacon->vf.stei); } return sta; } void cp_beacon_countdowns (cp_t *ctx) { dbg_assert (ctx); if (ctx->beacon.last_countdown_call_date_active && less_mod2p32 (ctx->beacon.last_countdown_call_date, phy_date ())) { uint bp_nb = 1 + (phy_date () - ctx->beacon.last_countdown_call_date) / ctx->bsu_aclf->beacon_period_theo_tck; /* Snid... */ if (ctx->beacon.snids.snidcd) { if (ctx->beacon.snids.snidcd <= bp_nb) { ctx->beacon.snids.snidcd = 0; cp_sta_own_data_set_snid (ctx, ctx->beacon.snids.snid); } else ctx->beacon.snids.snidcd -= bp_nb; } /* Hybrid mode... */ if (ctx->beacon.hm.hmcd) { if (ctx->beacon.hm.hmcd <= bp_nb) { cp_sta_own_data_t *own = cp_sta_mgr_get_sta_own_data (ctx); ctx->beacon.hm.hmcd = 0; own->hybrid_mode = ctx->beacon.hm.hm; } else ctx->beacon.hm.hmcd -= bp_nb; } /* Handover in progress... */ if (ctx->beacon.hoip.hoipcd) { if (ctx->beacon.hoip.hoipcd <= bp_nb) { cp_sta_t *sta; cp_net_t *net; ctx->beacon.hoip.hoipcd = 0; net = cp_sta_mgr_get_our_avln (ctx); if (cp_sta_own_data_get_tei (ctx) == ctx->beacon.hoip.cco) { cp_fsm_post_new_event (ctx, bare, HOIP_EXPIRED); } else { sta = cp_sta_mgr_sta_get_assoc (ctx, net, ctx->beacon.hoip.cco); cp_net_set_cco (ctx, net, ctx->beacon.hoip.cco); cp_fsm_post_new_event (ctx, sta, HOIP_EXPIRED, net, sta); } } else ctx->beacon.hoip.hoipcd -= bp_nb; } } else { /* Activated at the end of the function. */ ctx->beacon.last_countdown_call_date_active = true; } /** Update the countdown limit for next time. */ ctx->beacon.last_countdown_call_date = bsu_aclf_beacon_period_start_date_next (ctx->bsu_aclf); } /** * Initialise the beacon module. * \param ctx the module context. */ void cp_beacon_init (cp_t *ctx) { dbg_assert (ctx); // Initialise the beacon module. memset (&ctx->beacon, 0, sizeof (cp_beacon_t)); slist_init (ctx->beacon.list., bare); // Initialise the instance of the leon timer. hal_timer_instance_init ( ctx->hal_timer, &ctx->beacon.leon_timer, ctx, (hal_timer_instance_cb_t) CALLBACK (cp_beacon_timer_expires)); /* Setup beacon indicator. */ GPIO_SETUP (LED_BEACON_TX_RX, GPIO_DIRECTION_OUT); GPIO_SET (LED_BEACON_TX_RX, 0); bsu_init_beacon_cb (ctx->bsu, CALLBACK (cp_beacon_receive), ctx); /* Store the current date for SPOC update coefficients. */ ctx->beacon.spoc_update_date = phy_date (); /* Initialise tunable parameters. */ ctx->beacon.spoc_update_interval_ms = CP_BEACON_SPOC_UPDATE_INTERVAL_DEFAULT_MS; lib_stats_set_stat_value_notype ("SPOC_UPDATE_INTERVAL_MS", &ctx->beacon.spoc_update_interval_ms, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); } /** * Uninitialise the beacon module. * \param ctx the module context. */ void cp_beacon_uninit (cp_t *ctx) { cp_beacon_deactivate (ctx); hal_timer_instance_uninit (ctx->hal_timer, &ctx->beacon.leon_timer); } void cp_beacon_cco_update_beacon_data (cp_t *ctx) { bsu_beacon_t beacon; dbg_assert (ctx); /* Be aware that an handover can be in progress. If the it is the case and * the current countdown is 1, then after the cp_beacon_update_beacon_data * function, the handover is ended. The emission of the beacon MUST be * aborted. */ bool hoip_ended = ctx->beacon.hoip.hoipcd == 1; cp_beacon_countdowns (ctx); /** Generate the beacon and send. */ cp_beacon_fill (ctx, &beacon); /* Either: * - There is no key change. * or * - The bsu should start a key change countdown. The cp informs the bsu by * setting kccd once. */ dbg_assert (!beacon.bmis.eks.present || (beacon.bmis.eks.kccd == CP_BEACON_COUNTDOWN_EKS)); /** Program the timer to awake and send another central beacon. */ cp_beacon_reconfigure_timer (ctx, true /* CCo */); GPIO_TOGGLE (LED_BEACON_TX_RX); if (!hoip_ended) { bsu_update (ctx->bsu, &beacon, BSU_UPDATE_STA_TYPE_CCO); if (ctx->beacon.eks.kccd) { /* Reset KCCD, because we only want to set it once. * Setting it (once) tells the bsu to start the countdown. * The bsu will take care of decrementing it. */ ctx->beacon.eks.kccd = 0; ctx->beacon.eks.kbc = BSU_BEACON_EKS_KBC_NB; ctx->beacon.eks.new_eks = MAC_EKS_NB; } } } void cp_beacon_poweron_init (cp_t *ctx) { bsu_beacon_t beacon; dbg_assert (ctx); ctx->beacon.last_countdown_call_date_active = false; /* Generate the beacon and send. */ cp_beacon_fill (ctx, &beacon); bsu_update (ctx->bsu, &beacon, BSU_UPDATE_STA_TYPE_STA); GPIO_TOGGLE (LED_BEACON_TX_RX); } /** * Process the key change part of the beacon. * \param ctx cp context. * \param beacon The beacon to process. */ static void cp_beacon_process_bmi_eks (cp_t *ctx, const bsu_beacon_t *beacon) { /* This only runs on a Sta. The CCo, being the origin of the change, doesn't * need to process a beacon to know about it and react. */ dbg_assert (!cp_sta_own_data_get_cco_status (ctx)); if (beacon->bmis.eks.present) { if ((beacon->bmis.eks.kbc == BSU_BEACON_EKS_KBC_NEK)) { if ((ctx->mac_config->nek_mgr.next_nek == NULL) || (ctx->mac_config->nek_mgr.next_nek->eks != beacon->bmis.eks.new_eks)) { /* We don't have the new NEK. We need to ask for it. * But we don't want to ask many times. After all, this already * is a fall-back mechanism in case we missed the CM_SET_KEY_REQ * MME sent by the CCo before the start of a NEK change. It * doesn't seem useful to insist that much. So, only ask once. * * If ctx->beacon.eks.kccd != 0, it means we are already aware * of the countdown. So we already asked for the new missing * NEK. */ if (ctx->beacon.eks.kccd == 0) cp_fsm_post_new_event (ctx, bare, nek_request); } } ctx->beacon.eks.kccd = beacon->bmis.eks.kccd; ctx->beacon.eks.kbc = beacon->bmis.eks.kbc; ctx->beacon.eks.new_eks = beacon->bmis.eks.new_eks; } else { if (ctx->beacon.eks.kccd == 1) { if (ctx->beacon.eks.kbc == BSU_BEACON_EKS_KBC_NEK) { mac_nek_t *nek = bsu_nek_get_current (ctx->bsu); if (nek->eks != ctx->beacon.eks.new_eks) { /* The countdown elapsed and we are not using the new NEK. */ cp_fsm_trigger_new_event (ctx, bare, to_leave); } } } ctx->beacon.eks.kccd = 0; } } /** * Handle the non-reception of a beacon, as reported by the bsu which sends a * fake one. * \param ctx cp context. * \param fake_beacon A fake beacon. */ static void cp_beacon__bsu_inform__beacon_not_received (cp_t *ctx, const bsu_beacon_t *fake_beacon) { dbg_assert (!cp_sta_own_data_get_cco_status (ctx)); cp_beacon_process_bmi_eks (ctx, fake_beacon); } /** * Process the first beacon in the received list. * \param ctx the control plane context. */ void cp_beacon_get_and_process_beacon (cp_t *ctx) { dbg_assert (ctx); bsu_beacon_t *beacon; /** check the countdowns. */ cp_beacon_countdowns (ctx); /* While beacon list is not empty and like beacons are urgent, it process * all the beacons received. */ while (ctx->beacon.list.head) { arch_dsr_lock (); beacon = slist_pop_front (ctx->beacon.list., bare); arch_dsr_unlock (); if (beacon->params.direction == BSU_BEACON_DIRECTION_FROM_BSU) { cp_beacon__bsu_inform__beacon_not_received (ctx, beacon); } else { /** Beacon received ok. */ GPIO_TOGGLE (LED_BEACON_TX_RX); CP_TRACE_VERBOSE ( BEACON_BEACON_PROCESS, phy_date(), beacon->vf.nid, beacon->params.rx_parameters.snid, beacon->vf.stei, beacon->vf.bt, beacon->params.rx_parameters.bts); cp_beacon_process_beacon (ctx, beacon); } /** Release the beacon. */ blk_release (beacon); } } cp_sta_t* cp_beacon_process_beacon_central ( cp_t *ctx, bsu_beacon_t *beacon, cp_net_t *net) { cp_sta_t *sta = NULL; net->beacon_recv_date = phy_date (); mac_t cco_mac = beacon->bmis.mac_address.present ? beacon->bmis.mac_address.mac_address : MAC_ZERO; cp_sta_own_data_t *own = cp_sta_mgr_get_sta_own_data (ctx); /* I Should follow instruction from the beacon only if I track it. */ if (own->nid_track == beacon->vf.nid && own->tei_track == beacon->vf.stei && beacon->params.rx_parameters.snid == cp_sta_own_data_get_snid (ctx) && own->cco_mac_addr_track == cco_mac) { net->hm = beacon->vf.hm; net->avln_num_slots = beacon->vf.numslots; cp_net_set_access (ctx, net, beacon->params.rx_parameters.access); cp_net_set_slot_id_and_usage (ctx, net, beacon->vf.slotid, beacon->vf.slotusage); sta = cp_beacon_update_sta_peer (ctx, beacon, net, cco_mac); if (beacon->params.frequency_error_valid && ctx->beacon.spoc_update_interval_ms && less_mod2p32 ( ctx->beacon.spoc_update_date, phy_date ())) { pbproc_spoc_update (ctx->pbproc, true, beacon->params.frequency_error); ctx->beacon.spoc_update_date = phy_date () + MAC_MS_TO_TCK (ctx->beacon.spoc_update_interval_ms); ctx->beacon.spoc_updated = true; CP_TRACE_VERBOSE (SPOC, beacon->params.frequency_error); } /* I'm at least associated. */ if (cp_sta_own_data_get_tei (ctx)) { /* Update the countdowns. */ if (beacon->bmis.change_snid.present) { ctx->beacon.snids.snidcd = beacon->bmis.change_snid.snidccd; ctx->beacon.snids.snid = beacon->bmis.change_snid.new_snid; } if (beacon->bmis.change_hm.present) { ctx->beacon.hm.hmcd = beacon->bmis.change_hm.hmccd; ctx->beacon.hm.hm = beacon->bmis.change_hm.newhm; } if (beacon->bmis.handover.present) { ctx->beacon.hoip.hoipcd = beacon->bmis.handover.hcd; ctx->beacon.hoip.cco = beacon->bmis.handover.tei; } cp_beacon_process_bmi_eks (ctx, beacon); /** Update the schedules. */ cp_beacon_sta_compute_schedules (ctx, beacon); if (CONFIG_CP_AV) { bsu_beacon_bmi_discover_info_t discover; cp_beacon_fill_discover_info (ctx, &discover); bsu_update_discover_info (ctx->bsu, &discover); } /** Program the timer. */ cp_beacon_reconfigure_timer (ctx, false); } } /* I'm not associated or the beacon comes from another Network. */ else if (!cp_sta_own_data_get_tei (ctx) || (cp_sta_own_data_get_nid (ctx) != beacon->vf.nid || (cp_sta_own_data_get_nid (ctx) == beacon->vf.nid && cp_sta_own_data_get_snid (ctx) != beacon->params.rx_parameters.snid))) { net->hm = beacon->vf.hm; net->avln_num_slots = beacon->vf.numslots; cp_net_set_access (ctx, net, beacon->params.rx_parameters.access); cp_net_set_slot_id_and_usage (ctx, net, beacon->vf.slotid, beacon->vf.slotusage); /* Update station information. */ sta = cp_beacon_update_sta_peer (ctx, beacon, net, cco_mac); } return sta; } /** * Process the discover beacon received. * \param ctx the module context. * \param beacon the beacon to process. * \param net the network associated with the beacon. * \return the station from the sta_mgr if the beacon has been processed. */ static cp_sta_t* cp_beacon_process_beacon_discover ( cp_t *ctx, bsu_beacon_t *beacon, cp_net_t *net) { cp_sta_t *sta; mac_t peer_mac = beacon->bmis.mac_address.present ? beacon->bmis.mac_address.mac_address : MAC_ZERO; /* Beacon from a neighbor AVLN. */ if (beacon->vf.nid != cp_sta_own_data_get_nid (ctx) || (beacon->vf.nid == cp_sta_own_data_get_nid (ctx) && beacon->params.rx_parameters.snid != cp_sta_own_data_get_snid (ctx))) sta = cp_beacon_update_sta_peer (ctx, beacon, net, peer_mac); /* Beacon from our AVLN. */ else { sta = cp_sta_mgr_sta_get_from_mac (ctx, peer_mac); if (sta && cp_sta_get_tei (sta) == beacon->vf.stei) { slab_release (sta); sta = cp_beacon_update_sta_peer (ctx, beacon, net, peer_mac); /* Discover beacons from our AVLN. */ if (cp_sta_own_data_get_tei (ctx) && cp_sta_mgr_get_our_avln (ctx) == net) { bool updated = bitstream_direct_read ( &beacon->bmis.discover_info.info_data, 0, 1); if (updated) { cp_fsm_event_t *event = cp_fsm_event_sta_new ( ctx, CP_FSM_EVENT_TYPE_discover_info_updated, net, sta); cp_fsm_post (ctx, event); } } } /* Station is not from our AVLN but another one with the same NID, * SNID. */ else if (sta) { slab_release (sta); sta = NULL; } } return sta; } void cp_beacon_process_beacon (cp_t *ctx, bsu_beacon_t *beacon) { /** Add network and station info to local data. */ cp_net_t *net = cp_sta_mgr_add_avln ( ctx, beacon->params.rx_parameters.snid, beacon->vf.nid); /** If no NET object available, do not process this beacon. * This can happen on SNID conflict when some AVLN are powered on at * the time, the SNID conflict will cause the CP to create a net * object for each new tuple of {nid, snid}. STA mgr garbage should * resolve the problem by removing the old AVLN but it takes its * time. */ if (net && MAC_TEI_IS_STA (beacon->vf.stei)) { cp_sta_t *sta = NULL; if (beacon->vf.bt == BSU_BEACON_TYPE_CENTRAL) sta = cp_beacon_process_beacon_central (ctx, beacon, net); else if (beacon->vf.bt == BSU_BEACON_TYPE_DISCOVER) sta = cp_beacon_process_beacon_discover (ctx, beacon, net); /* Is the SNID in conflict. */ if (cp_sta_own_data_get_cco_status (ctx)) { cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); if (our_net != net && cp_net_get_snid (ctx, our_net) == cp_net_get_snid (ctx, net) && ctx->beacon.snids.snidcd == 0) cp_fsm_post_new_event (ctx, bare, cco__snid_conflict); } /* Raise the events in the FSM for station action. */ if (beacon->vf.bt == BSU_BEACON_TYPE_CENTRAL || beacon->vf.bt == BSU_BEACON_TYPE_PROXY) { /* If I have a sta the beacon has been processed. */ if (sta) cp_fsm_post_new_event ( ctx, beacon, BEACON, beacon, net, sta); if (cp_sta_own_data_get_cco_status (ctx) && (cp_sta_own_data_get_nid (ctx) == beacon->vf.nid)) cp_fsm_post_new_event ( ctx, beacon, BEACON_WITH_SAME_NID, beacon, net, NULL); } /* Release my reference on the STA. */ if (sta) slab_release (sta); } } void cp_beacon_process_tracked_avln (cp_t *ctx, bsu_beacon_t *beacon, cp_net_t *net) { cp_sta_own_data_t *own; dbg_assert (ctx); dbg_assert (beacon); dbg_assert (net); own = cp_sta_mgr_get_sta_own_data (ctx); own->nid_track = beacon->vf.nid; own->tei_track = beacon->vf.stei; own->cco_mac_addr_track = beacon->bmis.mac_address.present ? beacon->bmis.mac_address.mac_address : MAC_ZERO; own->hybrid_mode = beacon->vf.hm; /* SPOC. */ ctx->beacon.spoc_update_date = phy_date (); ctx->beacon.spoc_updated = false; /** Update the schedules. */ cp_beacon_sta_compute_schedules (ctx, beacon); bsu_beacon_bmi_discover_info_t discover; cp_beacon_fill_discover_info (ctx, &discover); bsu_update_discover_info (ctx->bsu, &discover); bsu_track_avln (ctx->bsu, beacon->vf.nid, beacon->params.rx_parameters.snid, beacon->vf.stei, beacon->bmis.mac_address.present ? beacon->bmis.mac_address.mac_address : MAC_ZERO); // Program the timer. cp_beacon_reconfigure_timer (ctx, false); } void cp_beacon_process_untracked_avln (cp_t *ctx) { cp_sta_own_data_t *own = cp_sta_mgr_get_sta_own_data (ctx); own->nid_track = 0; own->tei_track = 0; own->cco_mac_addr_track = MAC_ZERO; /* Reset SPOC. */ ctx->beacon.spoc_updated = false; if (ctx->beacon.spoc_update_interval_ms != 0) pbproc_spoc_update (ctx->pbproc, false, 0); /* Inform BSU. */ bsu_untrack_avln (ctx->bsu); } void cp_beacon_beacon_not_received (cp_t *ctx) { dbg_assert (ctx); cp_beacon_countdowns (ctx); cp_fsm_post_new_event (ctx, bare, BEACON_NOT_RECEIVED); /* Update the discover info for the BSU. */ bsu_beacon_bmi_discover_info_t discover; cp_beacon_fill_discover_info (ctx, &discover); bsu_update_discover_info (ctx->bsu, &discover); // Program the timer. cp_beacon_reconfigure_timer (ctx, false); } /** * Store a NEK. * \param nek_mgr the NEK management structure. * \param eks The eks of the NEK to store. * \param nek_enc The encryption part of the NEK to store. * \param nek_dec The decryption part of the NEK to store. * \return A pointer to the stored NEK, or NULL if the storage failed. */ PRIVATE mac_nek_t * cp_beacon_store_nek (mac_nek_mgr_t *nek_mgr, u8 eks, const u32 *nek_enc, const u32 *nek_dec) { /* Find a free slot. i.e. A slot that is neither in use, nor containing the * next key. */ /* arch_dsr_lock() is not necessary for the search, because even if this * function is interrupted, a free slot will remain free, because only the * cp fills slots, and this function is called by the cp, and the cp doesn't * interrupt the cp. * */ mac_nek_t *nek = NULL; uint i; for (i = 0; i < MAC_NEK_STORE_NB; i++) { if ((nek_mgr->store[i].in_use == false) && (&nek_mgr->store[i] != nek_mgr->next_nek)) { nek = &nek_mgr->store[i]; break; } } if (nek == NULL) return NULL; /* Put the key in the free slot. */ nek->eks = eks; for (i = 0; i < COUNT (nek->nek_enc); i++) { nek->nek_enc[i] = nek_enc[i]; nek->nek_dec[i] = nek_dec[i]; } /* Ensure the storage of the NEK is over, before returning. * Probably not necessary before returning from a function, but seems safer * in case this is inlined, for example (?) */ arch_reorder_barrier (); return nek; } void cp_beacon_change_nek (cp_t *ctx, uint eks, cp_key_t nek_enc, bool now) { cp_key_t nek_dec; dbg_assert (ctx); dbg_assert (ctx->mac_config); cp_secu_pbb_dec_gen (nek_enc, &nek_dec); mac_nek_t *nek = cp_beacon_store_nek (&ctx->mac_config->nek_mgr, eks, nek_enc.key, nek_dec.key); /* Failing to store the NEK should not happen (See MAC_NEK_STORE_NB). */ dbg_assert (nek); if (now) { bsu_nek_use (ctx->bsu, nek); } else { bsu_nek_set_next (ctx->bsu, nek); if (cp_sta_own_data_get_cco_status (ctx)) { /* Initiate key change countdown. */ ctx->beacon.eks.kccd = CP_BEACON_COUNTDOWN_EKS; ctx->beacon.eks.kbc = BSU_BEACON_EKS_KBC_NEK; ctx->beacon.eks.new_eks = eks; } } } /** * Deactivate the Beacon module. * \param ctx the CP context. */ void cp_beacon_deactivate (cp_t *ctx) { bsu_beacon_t *beacon; dbg_assert (ctx); /* Stop the hal timer. */ hal_timer_instance_cancel (ctx->hal_timer, &ctx->beacon.leon_timer); /* Release pending beacons. */ while (!slist_empty (ctx->beacon.list., bare)) { beacon = slist_pop_front (ctx->beacon.list., bare); blk_release (beacon); } } void cp_beacon_change_snid (cp_t *ctx, cp_snid_t snid) { dbg_assert (ctx); dbg_assert (cp_sta_own_data_get_cco_status (ctx)); if (!ctx->beacon.snids.snidcd) { ctx->beacon.snids.snid = snid; ctx->beacon.snids.snidcd = CP_BEACON_COUNTDOWN_SNID; } } void cp_beacon_reconfigure_timer (cp_t *ctx, bool cco) { dbg_assert (ctx); u32 date; if (cco) date = bsu_aclf_beacon_period_start_date_next (ctx->bsu_aclf) + bsu_aclf_beacon_period_tck (ctx->bsu_aclf) - CP_TIMER_OFFSET_CCO; else date = bsu_aclf_beacon_period_start_date_next (ctx->bsu_aclf) + CP_TIMER_OFFSET_STA; // Program the timer. hal_timer_instance_cancel (ctx->hal_timer, &ctx->beacon.leon_timer); hal_timer_instance_program (ctx->hal_timer, &ctx->beacon.leon_timer, date); } /** * Check if another countdown is currently active. * \param ctx the module context. * \return true if another countdown is active, false otherwise. */ bool cp_beacon_any_countdown_active (cp_t *ctx) { dbg_assert (ctx); return ((ctx->beacon.snids.snidcd) || (ctx->beacon.hm.hmcd) || (ctx->beacon.hoip.hoipcd)); } bool cp_beacon_synchronised (cp_t *ctx) { dbg_assert (ctx); return ctx->beacon.spoc_updated || ctx->beacon.spoc_update_interval_ms == 0; } void cp_beacon_update_tracking (cp_t *ctx, cp_sta_t *sta) { dbg_assert (ctx); cp_sta_own_data_t *own = cp_sta_mgr_get_sta_own_data (ctx); own->tei_track = cp_sta_get_tei (sta); own->cco_mac_addr_track = cp_sta_get_mac_address (sta); bsu_update_tracking (ctx->bsu, own->tei_track, own->cco_mac_addr_track); }