/* 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. */ #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 "cp/beacon/inc/beacon.h" #include "cp/beacon/discover.h" #include "cp/inc/context.h" #include "cp/inc/trace.h" #include "hal/gpio/gpio.h" #include "bsu/beacon/beacon.h" #include "bsu/bsu.h" #include "config/beacon.h" /** Default interval between SPOC updates. */ #define CP_BEACON_SPOC_UPDATE_INTERVAL_DEFAULT_MS \ CONFIG_BEACON_SPOC_UPDATE_INTERVAL_DEFAULT_MS /** 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); } /** * Fill the discover info beacon entry. * \param ctx the module context. * \param beacon the beacon to fill. */ static 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; } static void cp_beacon_clk_sync_call_spoc (cp_t *ctx, u32 fe) { dbg_assert (ctx); phy_spoc_coeff_t *coeff = blk_alloc (); coeff->part2 = blk_alloc(); phy_spoc_compute_all (fe, coeff); pbproc_spoc_coeff_set (ctx->pbproc, coeff); } 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 = 1; 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 ? BSU_BEACON_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; uint tei; if (cp_sta_own_data_get_cco_status (ctx) && cp_beacon_discover_need_to_request (ctx, &tei)) { beacon->bmis.discover.present = true; beacon->bmis.discover.tei = tei; } else beacon->bmis.discover.present = false; cp_beacon_fill_discover_info (ctx, &beacon->bmis.discover_info); /* 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 == BSU_BEACON_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->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; } /** * Add the allocation of the beacon to the bandwidth manager. * \param ctx the Module context. * \param beacon the beacon received. */ 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, 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, 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); } /** * Update the station in the station manager with the data contained in the * beacon. * \param ctx the module context. * \param beacon_data the beacon data. * \param net the network corresponding to the beacon. * return the control plane station. */ static cp_sta_t * cp_beacon_update_sta_peer (cp_t *ctx, bsu_beacon_t *beacon, cp_net_t *net) { cp_sta_t *sta; mac_t mac; dbg_assert (net); /** Handle the absence of Mac address bentry in the Central beacon. */ if (beacon->bmis.mac_address.present) mac = beacon->bmis.mac_address.mac_address; else mac = MAC_BROADCAST; 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); cp_sta_set_authenticated (ctx, sta, info_data.authentication); } return sta; } /** * Countdown decrement and process the necessary data. * \param ctx the CP context. */ static void cp_beacon_countdowns (cp_t *ctx) { dbg_assert (ctx); // TODO DM: is this test really usefull? if (less_mod2p32 (ctx->beacon.countdown_limit_date, phy_date ())) { u32 intervals; /** Check the interval: * We want to know how many beacon period elapsed. */ u32 bp = bsu_aclf_beacon_period_tck (ctx->bsu_aclf); dbg_assert (bp); bsu_aclf_beacon_period_start_date (ctx->bsu_aclf, &intervals, 1); intervals = (intervals + bp/2 - ctx->beacon.last_countdown_date) / bp; /** We missed too many beacon period we shouldn't be associated * anymore. */ if (intervals > CP_MAX_NO_BEACON + 1) { return; } ctx->beacon.last_countdown_date = ctx->beacon.countdown_limit_date; /** Update the countdown limit for next time. */ ctx->beacon.countdown_limit_date = bsu_aclf_beacon_period_start_date_next (ctx->bsu_aclf); /* Snid... */ if (ctx->beacon.snids.snidcd) { if (ctx->beacon.snids.snidcd <= intervals) { ctx->beacon.snids.snidcd = 0; cp_sta_own_data_set_snid (ctx, ctx->beacon.snids.snid); } else ctx->beacon.snids.snidcd -= intervals; } /* Hybrid mode... */ if (ctx->beacon.hm.hmcd) { if (ctx->beacon.hm.hmcd <= intervals) { 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 -= intervals; } /* Handover in progress... */ if (ctx->beacon.hoip.hoipcd) { if (ctx->beacon.hoip.hoipcd <= intervals) { 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 -= intervals; } /* EKS ... */ if (ctx->beacon.eks.kccd) { if (ctx->beacon.eks.kccd <= intervals) ctx->beacon.eks.kccd = 0; else ctx->beacon.eks.kccd -= intervals; } } } /** * 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)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 (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); } /** * Prepare the beacon and reconfigure the timer of the beacon module. * \param ctx the module context. * \param beacon the bsu_beacon_t structure to fill. * \param cco the cco status. */ static void cp_beacon_update_beacon_data (cp_t *ctx, bsu_beacon_t *beacon, bool cco) { cp_beacon_countdowns (ctx); /** Generate the beacon and send. */ cp_beacon_fill (ctx, beacon); /** Program the timer to awake and send another central beacon. */ cp_beacon_reconfigure_timer (ctx, cco); GPIO_TOGGLE (LED_BEACON_TX_RX); } void cp_beacon_sta_update_beacon_data (cp_t *ctx) { dbg_assert (ctx); bsu_beacon_bmi_discover_info_t discover; cp_beacon_fill_discover_info (ctx, &discover); bsu_update_discover_info (&discover); } void cp_beacon_cco_update_beacon_data (cp_t *ctx) { bsu_beacon_t beacon; dbg_assert (ctx); cp_beacon_update_beacon_data (ctx, &beacon, true); bsu_update (&beacon, BSU_UPDATE_STA_TYPE_CCO); } void cp_beacon_poweron_init (cp_t *ctx) { bsu_beacon_t beacon; dbg_assert (ctx); ctx->beacon.countdown_limit_date = ctx->beacon.last_countdown_date = phy_date (); /* Generate the beacon and send. */ cp_beacon_fill (ctx, &beacon); bsu_update (&beacon, BSU_UPDATE_STA_TYPE_STA); GPIO_TOGGLE (LED_BEACON_TX_RX); } /** * Process the central 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_central ( cp_t *ctx, bsu_beacon_t *beacon, cp_net_t *net) { cp_sta_t *sta = NULL; 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); if (beacon->params.frequency_error_valid && ctx->beacon.spoc_update_interval_ms && less_mod2p32 ( ctx->beacon.spoc_update_date, phy_date ())) { cp_beacon_clk_sync_call_spoc ( ctx, 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; } if (beacon->bmis.eks.present) { 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; } /** 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 (&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); sta = cp_beacon_update_sta_peer (ctx, beacon, net); } 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) { mac_t peer_mac = beacon->bmis.mac_address.present ? beacon->bmis.mac_address.mac_address : MAC_ZERO; cp_sta_t *sta = cp_sta_mgr_sta_get_from_mac (ctx, peer_mac); /* The station exists in the station manager. */ if (sta && cp_sta_get_tei (sta) == beacon->vf.stei) { sta = cp_beacon_update_sta_peer (ctx, beacon, net); /* 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); } } /* This reference is return by the station update peer. */ slab_release (sta); } /* Station does not exits, only the station from another AVLN should be * added in the station manager. */ else 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); /* This reference is return by the station update peer. */ slab_release (sta); } return sta; } /** * Process beacons. * \param ctx the module context. * \param beacon the beacon to process. */ static 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, snid_conflict); } /* Raise the event in the FSM for station action. */ if (sta && (beacon->vf.bt == BSU_BEACON_TYPE_CENTRAL || beacon->vf.bt == BSU_BEACON_TYPE_PROXY)) cp_fsm_post_new_event ( ctx, beacon, BEACON, beacon, net, sta); /* Release my reference on the STA. */ if (sta) slab_release (sta); } } /** * 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 (); /** 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); } } 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 (&discover); bsu_track_avln (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; cp_beacon_clk_sync_call_spoc (ctx, 0); /* Inform BSU. */ bsu_untrack_avln (); } /** * Function to call once the beacon has not been received. * \param ctx the module context. * * The beacon timer has expired and this function had been called by the FSM. */ 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 (&discover); // Program the timer. cp_beacon_reconfigure_timer (ctx, false); } /** * Set the nek in the beacon module. * \param ctx the CP context. * \param eks the eks tu use. * \param nek the nek key. * \param now indicate if the nek shall be use right now. */ void cp_beacon_change_nek (cp_t *ctx, uint eks, cp_key_t nek, bool now) { cp_key_t rnek; uint i,index; dbg_assert (ctx); dbg_assert (ctx->mac_config); if (now) index = bsu_nek_index_current (); else { index = bsu_nek_index_next (); ctx->beacon.eks.kccd = CP_BEACON_COUNTDOWN_EKS; ctx->beacon.eks.kbc = BSU_BEACON_EKS_KBC_NEK; ctx->beacon.eks.new_eks = eks; } cp_secu_pbb_dec_gen (nek, &rnek); ctx->mac_config->nek[index].eks = MAC_EKS_CLEAR; arch_reorder_barrier (); for (i = 0; i < COUNT(nek.key); i++) { ctx->mac_config->nek[index].nek_enc[i] = nek.key[i]; ctx->mac_config->nek[index].nek_dec[i] = rnek.key[i]; } arch_reorder_barrier (); ctx->mac_config->nek[index].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); } cp_beacon_discover_uninit (ctx); } 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_change_hm (cp_t *ctx, uint hybrid_mode) { dbg_assert (ctx); dbg_assert (cp_sta_own_data_get_cco_status (ctx)); if (!ctx->beacon.hm.hmcd) { ctx->beacon.hm.hm = hybrid_mode; ctx->beacon.hm.hmcd = CP_BEACON_COUNTDOWN_HM; } } void cp_beacon_handover (cp_t *ctx, cp_tei_t cco) { dbg_assert (ctx); if (!ctx->beacon.hoip.hoipcd) { ctx->beacon.hoip.cco = cco; ctx->beacon.hoip.hoipcd = CP_BEACON_COUNTDOWN_HOIP; } } void cp_beacon_handover_hoipflag (cp_t *ctx, enum cp_beacon_hoip_e flag) { dbg_assert (ctx); dbg_assert (flag < CP_BEACON_HOIP_MAX); ctx->beacon.hoip.hoip_flag = flag; } 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; }