/* Cesar project {{{ * * Copyright (C) 2010 Spidcom * * <<>> * * }}} */ /** * \file bsu/src/bsu.c * \brief BSU core functions. * \ingroup bsu * * Explanation of how bsu handles schedules programmation: * - On timer event, BSU copy the first two CA schedules and just modify the * start date. It creates the last and third one using data present in the * beacon i.e. bsu->sta_avln->beacon. * - On beacon reception, BSU write all the schedules for the CA, it allows * the CA to have non persistent schedules programmed for the current * beacon period. */ #include "common/std.h" #include "lib/bitstream.h" #include "lib/fixed.h" #include "bsu/bsu.h" #include "bsu/ntb/ntb.h" #include "bsu/aclf/aclf.h" #include "mac/ca/ca.h" #include "mac/common/timings.h" #include "bsu/inc/context.h" #include "bsu/inc/interface.h" #include #include "hal/arch/arch.h" #include "mac/pbproc/pbproc.h" #include "config/bsu.h" /* Define the process invalid value. */ #define BSU_MERGE_PROCESS_INVALID 0xff /* Define the number of beacon periods to provide to the CA. */ #define BSU_BEACON_PERIOD_NB 4 /** Static declaration. */ static bsu_t bsu_global; /** * Store the values computed into the stats. * \param ctx the module context. * * Stats registered are dependent on the AVLN the station is tracking/using * it can change at any time. */ PRIVATE void bsu_stats_store (bsu_t *ctx) { #if CONFIG_STATS ctx->stats.ntb_offset_tck = ctx->sta_avln->sync.ntb_offset_tck; if (ctx->sta_avln->sync.sync_nb >= ctx->ntb_clk_sync_nb_stable) ctx->stats.frequency_error_q30 = FIXED (ctx->sta_avln->sync.fe, BSU_NTB_FIXED_POINT); else ctx->stats.frequency_error_q30 = 0; #endif } /** * Remove the MFS beacon. * \param ctx the module context. * \param index the MFS index. */ PRIVATE inline void bsu_mfs_remove (bsu_t *ctx, uint index) { if (ctx->mfs_beacons[index]) { ca_mfs_remove (ctx->ca, ctx->mfs_beacons[index]); pbproc_mfs_remove_all (ctx->mfs_beacons[index]); mac_store_mfs_remove (ctx->mac_store, PARENT_OF (mfs_t, tx, ctx->mfs_beacons[index])); blk_release (ctx->mfs_beacons[index]); ctx->mfs_beacons[index] = NULL; } } /** * Clear BSU beacons MFS. * \param ctx the module context. */ PRIVATE void bsu_clear_mfs_beacons (bsu_t *ctx) { uint i; for (i = 0; i < COUNT (ctx->mfs_beacons); i++) bsu_mfs_remove (ctx, i); } /** * Reprogram bsu timer. * \param ctx the module context. * \param date the date to wake up. */ PRIVATE inline void bsu_reprogram_timer (bsu_t *ctx, u32 date) { /* Reprogram the timer. */ hal_timer_instance_program ( ctx->hal_timer, &ctx->timer, date - MAC_MS_TO_TCK (CONFIG_BSU_WAKEUP_DELAY_MS)); } /** * Set tracking data. * \param ctx the module context. * \param avln the new AVLN to track. * * \warn providing a NULL AVLN will be considered as untracked. */ PRIVATE void bsu_track_avln_identify (bsu_t *ctx, bsu_avln_t *avln) { bsu_clear_mfs_beacons (ctx); if (avln) { ctx->nid_track = avln->beacon.vf.nid; ctx->snid_track = avln->snid; ctx->tei_track = avln->beacon.vf.stei; ctx->cco_mac_address_track = avln->cco_mac_address; ctx->sta_avln = avln; } else { ctx->nid_track = 0; ctx->snid_track = 0; ctx->tei_track = MAC_TEI_UNASSOCIATED; ctx->cco_mac_address_track = MAC_ZERO; bsu_avln_t *old_avln = ctx->sta_avln; ctx->sta_avln = &ctx->poweron; dbg_assert (!ctx->poweron.sync.init); dbg_assert (ctx->poweron.sync.fe == 0.0); /* When the CCo starts it must keep the ntb offset tck of the network. */ ctx->poweron.sync.ntb_offset_tck = old_avln->sync.ntb_offset_tck; bsu_aclf_clear_sync (ctx->aclf); } ctx->track_new = true; BSU_TRACE (TRACK, ctx->nid_track, ctx->snid_track, ctx->tei_track, TRACE_U64 (ctx->cco_mac_address_track)); #if CONFIG_STATS ctx->stats.neighbor_beacon_nb = 0; ctx->stats.neighbor_beacon_last_mac = 0; #endif /* CONFIG_STATS */ } /** * Get the associated AVLN. * \param ctx the module context. * \param nid the NID of the AVLN. * \param snid the SNID of the AVLN. * \param mac the CCo's mac address. * \return the AVLN object. * * The AVLNs are static object do not release it. */ bsu_avln_t* bsu_avln_get (bsu_t *ctx, u64 nid, u8 snid, mac_t mac) { uint i; for (i = 0; i < ctx->avlns_nb; i++) if (ctx->avlns[i].beacon.vf.nid == nid && ctx->avlns[i].snid == snid && ctx->avlns[i].cco_mac_address == mac) return &ctx->avlns[i]; return NULL; } /** * Initialise the AVLN data. * \param avln the avln structure to initialise. * \param nid the network identifier. * \param snid the short network identifier. * \param mac the CCo mac address. */ PRIVATE void bsu_avln_init (bsu_avln_t *avln, u64 nid, u8 snid, mac_t mac) { bsu_ntb_init (&avln->sync); avln->beacon.vf.nid = nid; avln->snid = snid; avln->cco_mac_address = mac; /* Simulate an activity by setting the date to the current one, this is * only used by BSU to * - not remove it if adding a new AVLN * - not process two beacons of the same AVLN on the beacon period. */ avln->beacon.beacon_period_start_date = phy_date (); } /** * Add an AVLN. * \param ctx the module context. * \param nid the NID of the AVLN. * \param snid the SNID of the AVLN. * \param mac the CCo's mac address. * \param added set true if newly added. * \return the AVLN object. * * The AVLNs are static object do not release it. * \warn When calling this function, if no more room are available the oldest * AVLN will be used. */ inline bsu_avln_t* bsu_avln_add (bsu_t *ctx, u64 nid, u8 snid, mac_t mac, bool *added) { *added = false; bsu_avln_t* avln = bsu_avln_get (ctx, nid, snid, mac); if (!avln) { if(ctx->avlns_nb < BSU_FOREIGN_AVLNS_NB) { avln = &ctx->avlns[ctx->avlns_nb]; ctx->avlns_nb++; } /* No more space available. */ else { uint i; for (i = 1, avln = &ctx->avlns[0]; i < BSU_FOREIGN_AVLNS_NB - 1; i++) { if (less_mod2p32 ( ctx->avlns[i].beacon.beacon_period_start_date, avln->beacon.beacon_period_start_date)) /* found the oldest AVLN. */ avln = &ctx->avlns[i]; } dbg_assert (ctx->sta_avln != avln); } bsu_avln_init (avln, nid, snid, mac); *added = true; } return avln; } void bsu_avln_remove (bsu_t *ctx, u64 nid, u8 snid, mac_t mac) { dbg_assert (ctx); bsu_avln_t *to_remove = NULL; uint i, pos; arch_dsr_lock (); for (i = 0; i < ctx->avlns_nb; i++) { if (ctx->avlns[i].beacon.vf.nid == nid && ctx->avlns[i].snid == snid && ctx->avlns[i].cco_mac_address == mac) { to_remove = &ctx->avlns[i]; pos = i; break; } } if (to_remove) { /* Station's AVLN is the removed one. */ if (to_remove == ctx->sta_avln) bsu_track_avln_identify (ctx, NULL); for (i = pos; i < ctx->avlns_nb - 1; i++) { if (ctx->sta_avln == &ctx->avlns[i+1]) ctx->sta_avln = &ctx->avlns[i]; ctx->avlns[i] = ctx->avlns[i+1]; } ctx->avlns_nb--; } arch_dsr_unlock (); } /** * Initialise the process position index object. * \param ctx the module context. * \param beacon the beacon to use to read schedules. * \param proc the process object. * \param beacon_period_index the beacon period for the one the schedule is * being computed. */ PRIVATE inline void bsu_schedules_merge_process_init (bsu_t *ctx, bsu_beacon_t *beacon, bsu_avln_schedules_process_t *proc, uint beacon_period_index) { uint i; proc->ps_alloc_index = BSU_MERGE_PROCESS_INVALID; proc->ps = NULL; proc->nps_alloc_index = BSU_MERGE_PROCESS_INVALID; /* Persistent schedules position index. */ for (i = 0; i < beacon->bmis.ps.nb; i++) { if ((beacon->bmis.ps.ps[i].pscd <= beacon_period_index && (u32) (beacon->bmis.ps.ps[i].pscd + beacon->bmis.ps.ps[i].cscd) >= beacon_period_index) || BSU_BEACON_PERSISTENT_ALLOC_IS_PERMANENT(beacon->bmis.ps.ps[i])) { proc->ps = &beacon->bmis.ps.ps[i]; proc->ps_alloc_index = 0; break; } } /* Non persistent schedule index. */ if (beacon->bmis.nps.ns) proc->nps_alloc_index = 0; if (proc->nps_alloc_index == BSU_MERGE_PROCESS_INVALID && proc->ps_alloc_index == BSU_MERGE_PROCESS_INVALID) BSU_TRACE (SCHEDULES_WARN); } /** * Take the first allocation available from the persistent schedules. * \param proc_schedule the process schedule structure to take the first * allocation. * \return the first SAI of the persistent schedule. */ PRIVATE inline bsu_beacon_sai_t* bsu_schedules_process__ps_first_allocation ( bsu_t *ctx, bsu_avln_schedules_process_t *proc) { bsu_beacon_sai_t *sai = &proc->ps->sais[proc->ps_alloc_index++]; if (proc->ps_alloc_index == proc->ps->ns) { proc->ps_alloc_index = BSU_MERGE_PROCESS_INVALID; proc->ps = NULL; } return sai; } /** * Take the first allocation available from the non persistent schedules. * \param ctx the module context. * \param proc_schedule the process schedule structure to take the first * allocation. * \param beacon the beacon to get the schedules from. * \return the first SAI of the non persistent schedule. */ PRIVATE inline bsu_beacon_sai_t* bsu_schedules_process__nps_first_allocation ( bsu_t *ctx, bsu_avln_schedules_process_t *proc, bsu_beacon_t *beacon) { bsu_beacon_sai_t *sai = &beacon->bmis.nps.sais[proc->nps_alloc_index++]; if (proc->nps_alloc_index == beacon->bmis.nps.ns) proc->nps_alloc_index = BSU_MERGE_PROCESS_INVALID; return sai; } /** * Get the lesser schedule. * \param ctx the module context. * \param beacon the beacon to read schedules. * \param proc the process object. * \return the schedule to insert in the CA allocation. */ inline bsu_beacon_sai_t* bsu_schedules_merge_get_lesser (bsu_t *ctx, bsu_beacon_t *beacon, bsu_avln_schedules_process_t *proc) { bsu_beacon_sai_t *sai = NULL; /* Persistent schedules lonely. */ if (proc->ps && proc->nps_alloc_index == BSU_MERGE_PROCESS_INVALID && proc->ps_alloc_index != BSU_MERGE_PROCESS_INVALID) sai = bsu_schedules_process__ps_first_allocation (ctx, proc); /* Non persistent schedule alone. */ else if (!proc->ps && proc->nps_alloc_index != BSU_MERGE_PROCESS_INVALID) sai = bsu_schedules_process__nps_first_allocation ( ctx, proc, beacon); /* Non persistent and persistent schedules are available. */ else if (proc->nps_alloc_index != BSU_MERGE_PROCESS_INVALID && proc->ps_alloc_index != BSU_MERGE_PROCESS_INVALID) { /* Persistent schedules is before the non persistent one. */ if (proc->ps->sais[proc->ps_alloc_index].end_time_atu < beacon->bmis.nps.sais[proc->nps_alloc_index].end_time_atu) sai = bsu_schedules_process__ps_first_allocation (ctx, proc); /* Non persistent schedules is before the persistent one. */ else sai = bsu_schedules_process__nps_first_allocation ( ctx, proc, beacon); } return sai; } /** * Add an allocation into CA schedules. * \param ctx the module context. * \param schedules the CA schedules. * \param pos the position in the schedules allocations array. * \param end_time_atu the end of the allocation. * \param glid the GLID of the allocation */ PRIVATE inline void bsu_schedules_merge__allocation_add (bsu_t *ctx, ca_schedule_t *schedules, uint pos, u32 end_time_atu, u8 glid) { dbg_claim (end_time_atu); schedules->allocations[pos].end_offset_tck = MAC_ATU_TO_TCK (end_time_atu); schedules->allocations[pos].glid = glid; } /** * Merge schedules read from the central beacon into the ca schedules. * \param ctx the module context. * \param beacon the beacon containing the schedules. * \param schedules the CA schedules objects to fill. * \param bp_index the beacon period for the one the schedule is being * computed. */ inline void bsu_schedules_merge (bsu_t *ctx, bsu_beacon_t *beacon, ca_schedule_t *schedule, uint bp_index) { uint alloc = 0; bsu_beacon_sai_t *sai; bsu_avln_schedules_process_t process; bsu_schedules_merge_process_init (ctx, beacon, &process, bp_index); while ((sai = bsu_schedules_merge_get_lesser (ctx, beacon, &process))) { /* A hole is present for the first allocation. It is a beacon region, * BSU should inform the CA with the correct glid (a reserved one). * - When acting as CCo glid is MAC_LID_SPC_CENTRAL. * - When acting as STA glid is MAC_LID_SPC_BEACON_LISTEN. */ if (alloc == 0 && sai->stpf && sai->start_time_atu != 0) { u8 glid; if (ctx->is_sta == BSU_UPDATE_STA_TYPE_CCO) glid = MAC_LID_SPC_CENTRAL; else glid = MAC_LID_SPC_BEACON_LISTEN; bsu_schedules_merge__allocation_add ( ctx, schedule, alloc, sai->start_time_atu, glid); alloc++; } /* Add a hole between the previous allocation and this one. */ else if (alloc && sai->stpf && MAC_ATU_TO_TCK(sai->start_time_atu) != schedule->allocations[alloc-1].end_offset_tck) { bsu_schedules_merge__allocation_add ( ctx, schedule, alloc, sai->start_time_atu, MAC_LID_SPC_HOLE); alloc++; } /* Add this allocation. */ bsu_schedules_merge__allocation_add ( ctx, schedule, alloc, sai->end_time_atu, sai->glid | 0x80); alloc++; } /* Special behavior if we miss too much beacons. */ if (!alloc) { u8 lid = beacon->vf.nm == MAC_NM_CSMA_ONLY ? MAC_LID_SHARED_CSMA : MAC_LID_SPC_HOLE; schedule->allocations[0].end_offset_tck = BITS_ONES (24); schedule->allocations[0].glid = lid; alloc++; } /* Special behavior for CSMA only mode. */ if (beacon->vf.nm == MAC_NM_CSMA_ONLY) schedule->allocations[alloc-1].end_offset_tck = BITS_ONES (24); /* store the number of allocations. */ schedule->allocations_nb = alloc; } /** * Fill the Hybrid mode, SNID and NEK value for CA schedule. * \param ctx the module context. * \param avln the AVLN schedule structure containing the necessary data. * \param casched the Channel access schedule to fill. * \param bpn the beacon period number, 0 for the current one, 1 for the * next one and so on. */ inline void bsu_ca_schedules_settings (bsu_t *ctx, bsu_avln_t *avln, ca_schedule_t *casched, uint bpn) { /* SNID. */ if (avln->beacon.bmis.change_snid.present && ((ctx->is_sta == BSU_UPDATE_STA_TYPE_STA && avln->beacon.bmis.change_snid.snidccd <= bpn) || (ctx->is_sta == BSU_UPDATE_STA_TYPE_CCO && (uint) 1 + avln->beacon.bmis.change_snid.snidccd <= bpn))) casched->snid = avln->beacon.bmis.change_snid.new_snid; else casched->snid = avln->snid; /* NEK switch. */ if (avln->beacon.bmis.eks.present && avln->beacon.bmis.eks.kbc == BSU_BEACON_EKS_KBC_NEK && ((ctx->is_sta == BSU_UPDATE_STA_TYPE_STA && avln->beacon.bmis.eks.kccd <= bpn) ||(ctx->is_sta == BSU_UPDATE_STA_TYPE_CCO && ((uint) 1 + avln->beacon.bmis.eks.kccd) <= bpn))) casched->nek_switch = !ctx->nek_switch; else casched->nek_switch = ctx->nek_switch; /* Hybrid mode. */ if (avln->beacon.bmis.change_hm.present && ((ctx->is_sta == BSU_UPDATE_STA_TYPE_STA && avln->beacon.bmis.change_hm.hmccd <= bpn) || (ctx->is_sta == BSU_UPDATE_STA_TYPE_CCO && (uint) 1 + avln->beacon.bmis.change_hm.hmccd <= bpn))) casched->coexistence_mode = avln->beacon.bmis.change_hm.newhm; else casched->coexistence_mode = avln->beacon.vf.hm; } /** * If the network is in CSMA only mode, check the current schedule is * permanent and with a full CSMA period. * \param ctx the module context. * \param beacon the beacon to modify. * * If the schedules are not compatible with the network mode, the default will * be applied. */ inline void bsu_ca_schedules_nm_csma_only (bsu_t *ctx, bsu_beacon_t *beacon) { if (beacon->vf.nm == MAC_NM_CSMA_ONLY && (beacon->bmis.ps.nb == 0 || (beacon->bmis.ps.nb == 1 && beacon->bmis.ps.ps[0].cscd == 0 && beacon->bmis.ps.ps[0].pscd == 0))) { beacon->bmis.nps.ns = 0; beacon->bmis.ps.nb = 1; beacon->bmis.ps.ps[0].ns = 1; beacon->bmis.ps.ps[0].pscd = 0; beacon->bmis.ps.ps[0].cscd = BSU_BEACON_PERSISTENT_SCHEDULES_PERMANENT_COUNTDOWN; beacon->bmis.ps.ps[0].sais[0].stpf = false; beacon->bmis.ps.ps[0].sais[0].glid = MAC_LID_SHARED_CSMA & 0x7f; } } /** * Get available slot of schedules for the CA. * \param ctx the module context. * \param indexes the array to fill with available slots. */ PRIVATE void bsu_ca_schedules_available (bsu_t *ctx, uint *indexes) { uint i, j; uint nb = 0; bool in_use; for (i = 0; i < CA_SCHEDULE_NB && nb < BSU_BEACON_SCHEDULES_NB; i++) { in_use = false; for (j = 0; j < COUNT (ctx->ca_schedules_in_use) && !in_use; j++) { if (ctx->ca_schedules_in_use[j] == i) in_use = true; } if (!in_use) indexes[nb++] = i; } } /** * Fill the missing schedule for the CA. * \param ctx the module context. * \param avln the avln containing the data to fill the missing schedules. * \param bp the array of data to provide to the CA. * \param bpsd the array of beacon period start date to fill the * allocations. * \param start_index start filling the array at this index. * * This function takes an array with bp_nb - start_index elements filled * corresponding to the current schedules in use in the CA. This should only * fill the last ones. * * It is not this function responsibility to provide the computed schedules * to the CA, the caller must handle it. */ PRIVATE void bsu_ca_schedule_prepare_missing ( bsu_t *ctx, bsu_avln_t *avln, ca_beacon_period_t *bp, u32 *bpsd, uint start_index) { ca_schedule_t *schedule; uint i, j; uint ca_index[BSU_BEACON_SCHEDULES_NB]; bsu_ca_schedules_available (ctx, ca_index); /* Get non used schedules from the CA. */ for (i = start_index, j = 0; i < BSU_BEACON_SCHEDULES_NB; i++, j++) { /* Get the next channel access index not in use. */ schedule = ca_alloc_get_schedule (ctx->ca, ca_index[j]); bsu_schedules_merge (ctx, &avln->beacon, schedule, i); bsu_ca_schedules_settings (ctx, avln, schedule, i); bp[i].start_date = bpsd[i]; bp[i].schedule_index = ca_index[j]; } /* Store the current schedules in use. */ for (i = 0; i < BSU_BEACON_SCHEDULES_NB; i++) ctx->ca_schedules_in_use[i] = bp[i].schedule_index; } /** * End to fill the schedules and provide it to the CA. * \param ctx the module context. * \param avln the station avln data containing the schedules read from the * beacon. * \param copy_nb copy the first nb schedules. */ void bsu_ca_schedules (bsu_t *ctx, bsu_avln_t *avln, uint copy_nb) { uint start_nb; u32 bpsd[BSU_BEACON_SCHEDULES_NB]; ca_beacon_period_t beacon_period[BSU_BEACON_SCHEDULES_NB]; bsu_aclf_beacon_period_start_date (ctx->aclf, bpsd, COUNT (bpsd)); for (start_nb = 0; start_nb < copy_nb; start_nb++) { beacon_period[start_nb].start_date = bpsd[start_nb]; beacon_period[start_nb].schedule_index = ctx->ca_schedules_in_use[start_nb]; } bsu_ca_schedule_prepare_missing ( ctx, avln, beacon_period, bpsd, start_nb); /* Provide the schedules to the CA. */ BSU_TRACE (SCHEDULES, bpsd[0], bpsd[1], bpsd[2]); ca_alloc_update_beacon_periods ( ctx->ca, beacon_period, BSU_BEACON_SCHEDULES_NB); } /** * Decrease countdown EKS, SNID and HM. * \param ctx the module context. * \param avln the AVLN schedules data. * This should be done after programming the CA with the current schedules to * have data ready for next awake. */ PRIVATE inline void bsu_avln_countdown_beacon_entries (bsu_t *ctx, bsu_avln_t *avln) { if (avln->beacon.bmis.change_snid.present) avln->beacon.bmis.change_snid.snidccd--; if (avln->beacon.bmis.change_hm.present) avln->beacon.bmis.change_hm.hmccd--; } /** * Apply EKS, SNID and HM. * \param ctx the module context. * \param avln the AVLN schedules data. * This should be done after programming the CA with the current schedules to * have data ready for next awake. */ PRIVATE inline void bsu_avln_countdown_beacon_entries_apply_changes (bsu_t *ctx, bsu_avln_t *avln) { if (avln->beacon.bmis.change_snid.present && avln->beacon.bmis.change_snid.snidccd == 1) { avln->beacon.bmis.change_snid.present = false; avln->snid = avln->beacon.bmis.change_snid.new_snid; if (avln == ctx->sta_avln) ctx->snid_track = avln->snid; } if (avln->beacon.bmis.change_hm.present && avln->beacon.bmis.change_hm.hmccd == 1) { avln->beacon.bmis.change_hm.present = false; avln->beacon.vf.hm = avln->beacon.bmis.change_hm.newhm; } } /** * Handle Key change related actions. * \param ctx bsu context. * This should be done after programming the CA with the current schedules to * have data ready for next awake. */ PRIVATE void bsu_handle_key_change (bsu_t *ctx) { dbg_assert (ctx); bsu_avln_t *avln = ctx->sta_avln; mac_nek_mgr_t *nek_mgr = &ctx->mac_config->nek_mgr; if (avln->beacon.bmis.eks.present) { if (avln->beacon.bmis.eks.kbc == BSU_BEACON_EKS_KBC_NEK) { /* Do we have the new NEK? */ if (nek_mgr->next_nek && (nek_mgr->next_nek->eks == avln->beacon.bmis.eks.new_eks)) { /* We have it. Set it as next. */ mac_nek_t *old_next_nek = nek_mgr->use[!ctx->nek_switch]; /* No need to set it, if it's already set. */ if (nek_mgr->next_nek != old_next_nek) { nek_mgr->next_nek->in_use = true; arch_reorder_barrier (); nek_mgr->use[!ctx->nek_switch] = nek_mgr->next_nek; arch_reorder_barrier (); if (old_next_nek != nek_mgr->use[ctx->nek_switch]) old_next_nek->in_use = false; } } /* else: * The next NEK is not available yet. So either: * - we currently have the current NEK set in the next slot too. * Because that's what bsu_nek_use() does. * - or, we have previously set a NEK in the next slot, but in the * meantime we received an MME with an EKS different from * new_eks. (Seems unlikely, but...). * In both cases, there will be a NEK in the next slot, so the * pbproc will find a NEK to use. */ if (avln->beacon.bmis.eks.kccd == 1) ctx->nek_switch = !ctx->nek_switch; } /* Prepare for next beacon period, in case we don't get the beacon. */ if (avln->beacon.bmis.eks.kccd > 1) avln->beacon.bmis.eks.kccd--; else avln->beacon.bmis.eks.present = false; } else { /* No key change to handle. * But did we change nek_switch on the previous run of this function? */ if (ctx->nek_switch != ctx->prev_nek_switch) { /* On the previous run of this function, we changed nek_switch. * This means that a NEK change occured at the start of the current * beacon period. The new NEK (if we had it) should now be in use, * and we can remove the old NEK. * But first: did we have the new NEK? */ if (nek_mgr->use[ctx->nek_switch] != nek_mgr->use[!ctx->nek_switch]) { mac_nek_t *prev_nek = nek_mgr->use[!ctx->nek_switch]; nek_mgr->use[!ctx->nek_switch] = nek_mgr->use[ctx->nek_switch]; /* Use a barrier to be sure that the NEK is not used anymore by * the pbproc when we mark it as not used. */ arch_reorder_barrier (); prev_nek->in_use = false; } ctx->prev_nek_switch = ctx->nek_switch; } } } /** * Decrease schedules countdown. * \param ctx the module context. * \param avln the AVLN object. */ inline void bsu_avln_schedules_decrease_countdown (bsu_t *ctx, bsu_avln_t *avln) { uint i; for (i = 0; i < avln->beacon.bmis.ps.nb; i++) { /* If one of the persistent schedules has the CSCD == 0, it should be * removed from the schedules. */ if (avln->beacon.bmis.ps.ps[i].pscd == 0 && avln->beacon.bmis.ps.ps[i].cscd == 0) { uint j; for (j = i; j < avln->beacon.bmis.ps.nb - 1; j++) avln->beacon.bmis.ps.ps[j] = avln->beacon.bmis.ps.ps[j+1]; avln->beacon.bmis.ps.nb--; } if (avln->beacon.bmis.ps.nb) { if (avln->beacon.bmis.ps.ps[i].pscd) avln->beacon.bmis.ps.ps[i].pscd--; else if (avln->beacon.bmis.ps.ps[i].cscd && (avln->beacon.vf.nm != MAC_NM_CSMA_ONLY || (avln->beacon.vf.nm == MAC_NM_CSMA_ONLY && avln->beacon.bmis.ps.ps[i].cscd != HPAV_SCHEDULE_PERMAMENT_VALUE))) avln->beacon.bmis.ps.ps[i].cscd--; } } } /** * Create the beacon to be send over the PLC. * \param ctx the module context. * \param type the beacon type. * \param bsu_beacon the beacon to use to write the PB. */ PRIVATE void bsu_beacon_send_prepare (bsu_t *ctx, bsu_beacon_type_t type, bsu_beacon_t *bsu_beacon) { pb_beacon_t *beacon; pbproc_tx_beacon_params_t params; /* Make a copy of the beacon for upper layers. */ bsu_beacon_t *ulbeacon = blk_alloc (); memcpy (ulbeacon, bsu_beacon, sizeof (bsu_beacon_t)); ulbeacon->vf.stei = ctx->mac_config->tei; bsu_aclf_bto (ctx->aclf, (s16*) params.bto, COUNT (params.bto)); /* Reset the updated flag in the discover info beacon entry. */ if (type == BSU_BEACON_TYPE_DISCOVER) { ulbeacon->bmis.mac_address.present = true; ulbeacon->bmis.mac_address.mac_address = ctx->mac_config->sta_mac_address; ulbeacon->bmis.discover_info = ctx->discover_info; ulbeacon->bmis.discover_info.present = true; bitstream_direct_write (&ctx->discover_info.info_data, 0, 0, 1); } beacon = bsu_beacon_write (ulbeacon, type, ctx->mac_config, ¶ms); /* Send the beacon. */ bsu_beacon_send (ctx, type, beacon, ¶ms); bsu_beacon_send_upper_layer ( ctx, ulbeacon, BSU_BEACON_DIRECTION_TO_PLC); } /** * Inform upper layers (cp, in particular) that an expected beacon was not * received, and use a fake beacon to pass any needed information. * \param ctx bsu context. */ static void bsu_beacon_inform_beacon_not_received (bsu_t *ctx) { /* Send a fake beacon to the cp. */ bsu_beacon_t *fake_beacon = blk_alloc (); dbg_assert (fake_beacon); fake_beacon->bmis.eks = ctx->sta_avln->beacon.bmis.eks; bsu_beacon_send_upper_layer (ctx, fake_beacon, BSU_BEACON_DIRECTION_FROM_BSU); } /** * Handle the timer as STA. * \param ctx the module context. */ static void bsu_timer_event_process__sta (bsu_t *ctx) { /* Have we at least received a beacon before using it ? * This can happen after a deactivate followed by an activate event in * which the timer is not stopped. */ if (ctx->sta_avln->sync.init) { u32 bpsd0; bsu_aclf_beacon_period_start_date (ctx->aclf, &bpsd0, 1); if (ctx->track_new) { u32 bpsd[BSU_BEACON_SCHEDULES_NB]; uint bp = bsu_aclf_beacon_period_tck (ctx->aclf); /* Reset STA clock correction. */ bsu_ntb_clock_configure ( &ctx->sta_avln->sync, ctx->mac_config, ctx->phy); /* Compute the new BPSD with the data of the new AVLN. */ bsu_aclf_compute_beacon_period_start_date ( ctx->aclf, ctx->sta_avln->sync.bts - ctx->sta_avln->sync.ntb_offset_tck, ctx->sta_avln->bto, ctx->sta_avln->beacon.bmis.bpsto.present ? ctx->sta_avln->beacon.bmis.bpsto.bpsto : 0); bsu_aclf_beacon_period_start_date ( ctx->aclf, bpsd, COUNT (bpsd)); /* Decrease countdown, the first schedule will not be * programmed. */ bsu_avln_schedules_decrease_countdown (ctx, ctx->sta_avln); /* The current beacon period ends after the next beacon period * in the new AVLN starts. */ if (less_mod2p32 (bpsd[1], bpsd0 + bp)) { bsu_aclf_bpsd_avoid_overlap (ctx->aclf); bsu_avln_schedules_decrease_countdown (ctx, ctx->sta_avln); ctx->sta_avln->beacon.beacon_period_start_date = bpsd[2]; } else ctx->sta_avln->beacon.beacon_period_start_date = bpsd[1]; ctx->track_new = false; } else if (less_mod2p32 (ctx->sta_avln->beacon.beacon_period_start_date, bpsd0 - MAC_TOLERANCE_TCK)) { bsu_avln_schedules_decrease_countdown (ctx, ctx->sta_avln); ctx->sta_avln->beacon.beacon_period_start_date = bpsd0; bsu_beacon_inform_beacon_not_received (ctx); } } } /** * Handle the timer as CCo. * \param ctx the module context. */ static void bsu_timer_event_process__cco (bsu_t *ctx) { u32 bpsd0; bsu_aclf_beacon_period_start_date (ctx->aclf, &bpsd0, 1); /* Are update data late ?. */ if (less_mod2p32 ( ctx->sta_avln->beacon.beacon_period_start_date, bpsd0)) /* Create and send the beacon. */ bsu_beacon_countdown (&ctx->sta_avln->beacon); if (ctx->track_new) { /* Reset STA clock correction. */ bsu_ntb_clock_configure ( &ctx->sta_avln->sync, ctx->mac_config, ctx->phy); ctx->track_new = false; } /* Inform PBProc we send a central beacon. */ pbproc_beacon_detected ( ctx->pbproc, bsu_aclf_beacon_period_start_date_next (ctx->aclf) + bsu_aclf_beacon_period_tck (ctx->aclf)); bsu_beacon_send_prepare (ctx, BSU_BEACON_TYPE_CENTRAL, &ctx->sta_avln->beacon); if (ctx->sta_avln->beacon.bmis.discover.present && ctx->sta_avln->beacon.bmis.discover.tei == ctx->mac_config->tei) { bsu_beacon_send_prepare (ctx, BSU_BEACON_TYPE_DISCOVER, &ctx->sta_avln->beacon); ctx->sta_avln->beacon.bmis.discover.present = false; } } mac_nek_t * bsu_nek_get_current (const bsu_t *ctx) { dbg_assert (ctx); return ctx->mac_config->nek_mgr.use[ctx->nek_switch]; } void bsu_nek_set_next (bsu_t *ctx, mac_nek_t *nek) { dbg_assert (ctx); dbg_assert (nek); ctx->mac_config->nek_mgr.next_nek = nek; } void bsu_nek_use (bsu_t *ctx, mac_nek_t *nek) { dbg_assert (ctx); dbg_assert (nek); /* To start using a NEK, both the next and the current NEK slots are * changed. The next NEK is changed for two reasons: * - In case there is a programmed NEK change, not updating the next NEK * will render the call to this function useless, because the passed NEK * won't be used after the NEK change. * - When using one NEK, the choice has been made to put it in both slots. * This way, the pbproc easily finds the NEK in Rx. */ mac_nek_t *old_current; mac_nek_t *old_next; mac_nek_mgr_t *nek_mgr = &ctx->mac_config->nek_mgr; /* Protect ourself from the bsu running in dsr context. */ arch_dsr_lock (); /* Suppose that a NEK change is about to happen on the next beacon period. * Let n1 be the current NEK. n2 the next NEK. n3 the NEK passed to this * function. * - The current NEK slot is set to n3. So the pbproc starts using it. * - New beacon period, so the pbproc starts using n2. * - The next NEK slot is set to n3. So the pbproc starts using n3. * So, the pbproc used n3, then n2, then n3. To prevent the temporary use of * n2, the next NEK slot is updated first, then the current NEK slot. */ old_next = nek_mgr->use[!ctx->nek_switch]; old_current = nek_mgr->use[ctx->nek_switch]; nek->in_use = true; arch_reorder_barrier (); nek_mgr->use[!ctx->nek_switch] = nek; arch_reorder_barrier (); nek_mgr->use[ctx->nek_switch] = nek; arch_reorder_barrier (); arch_dsr_unlock (); old_next->in_use = false; old_current->in_use = false; } /** * BSU timer expires. * \param ud the module context. * * Reprogram schedules using previous data, when acting as CCo sends the * central beacon. */ void bsu_timer_event_process (void *ud) { bsu_t *ctx = (bsu_t *) ud; if (ctx->activate) { dbg_assert (ctx); dbg_assert ( less_mod2p32 (phy_date (), bsu_aclf_beacon_period_start_date_next(ctx->aclf))); if (ctx->is_sta == BSU_UPDATE_STA_TYPE_STA) bsu_timer_event_process__sta (ctx); else bsu_timer_event_process__cco (ctx); bsu_ca_schedules (ctx, ctx->sta_avln, 2); bsu_avln_countdown_beacon_entries_apply_changes (ctx, ctx->sta_avln); bsu_avln_countdown_beacon_entries (ctx, ctx->sta_avln); bsu_handle_key_change (ctx); bsu_aclf_track_powerline (ctx->aclf); if (ctx->is_sta == BSU_UPDATE_STA_TYPE_STA) bsu_aclf_shift_beacon_period_start_date (ctx->aclf); else bsu_aclf_ac_compute_beacon_period_start_date (ctx->aclf); /* Shift schedules in use for next beacon period. */ uint i; uint index_0 = ctx->ca_schedules_in_use[0]; for (i = 0; i < COUNT (ctx->ca_schedules_in_use) - 1; i++) ctx->ca_schedules_in_use[i] = ctx->ca_schedules_in_use[i+1]; /* To avoid the schedule of index_0 to be requested it is put at * the end of the array. This schedule will not be used for the next * program. */ ctx->ca_schedules_in_use[i] = index_0; bsu_reprogram_timer ( ctx, bsu_aclf_beacon_period_start_date_next (ctx->aclf)); bsu_stats_store (ctx); } } /** * Initialise stats for bsu. * \param ctx the module context. */ PRIVATE void bsu_stats_init (bsu_t *ctx) { #if CONFIG_STATS dbg_assert (ctx); /* Register our statistics. */ lib_stats_set_stat_value_notype ("ntb_offset_tck", &ctx->stats.ntb_offset_tck, LIB_STATS_ACCESS_READ_ONLY, LIB_STATS_USER); lib_stats_set_stat_value_notype ("frequency_error_q30", &ctx->stats.frequency_error_q30, LIB_STATS_ACCESS_READ_ONLY, LIB_STATS_USER); lib_stats_set_stat_value_notype ("bsu_neighbor_beacon_nb", &ctx->stats.neighbor_beacon_nb, LIB_STATS_ACCESS_READ_ONLY, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("bsu_neighbor_beacon_last_mac", &ctx->stats.neighbor_beacon_last_mac, LIB_STATS_ACCESS_READ_ONLY, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("CLK_SYNC_WEIGHT_K", &ctx->ntb_clk_sync_weight_k, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_USER); lib_stats_set_stat_value_notype ("CLK_SYNC_NB_STABLE", &ctx->ntb_clk_sync_nb_stable, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_USER); #endif } bsu_t * bsu_init (bsu_aclf_t *aclf, mac_config_t *mac_config, phy_t *phy, mac_store_t *mac_store, ca_t *ca, sar_t *sar, hal_timer_t *timer, pbproc_t *pbproc) { bsu_t *ctx = &bsu_global; dbg_assert (aclf); dbg_assert (mac_config); dbg_assert (phy); dbg_assert (mac_store); dbg_assert (ca); dbg_assert (sar); dbg_assert (timer); memset (&bsu_global, 0x0, sizeof (bsu_t)); /* Initialise the context. */ ctx->aclf = aclf; ctx->mac_config = mac_config; ctx->phy = phy; ctx->mac_store = mac_store; ctx->ca = ca; ctx->sar = sar; ctx->hal_timer = timer; ctx->pbproc = pbproc; /* Initialise the NTB. */ uint i; for (i = 0; i < HPAV_AVLNS_NB_MAX; i++) { memset (&ctx->avlns[i], 0, sizeof (bsu_avln_t)); ctx->avlns[i].beacon.vf.nm = MAC_NM_CSMA_ONLY; bsu_ntb_init (&ctx->avlns[i].sync); } ctx->sta_avln = &ctx->poweron; memset (&ctx->poweron, 0, sizeof (bsu_avln_t)); ctx->poweron.beacon.vf.nm = MAC_NM_CSMA_ONLY; bsu_ntb_init (&ctx->poweron.sync); /* Initialise the SAR callback. */ sar_init_beacon_cb ( sar, ctx, (sar_beacon_cb_t) CALLBACK (bsu_beacon_recv)); /* Initialise timer events. */ hal_timer_instance_init ( timer, &ctx->timer, ctx, CALLBACK (bsu_timer_event_process)); ctx->activate = false; ctx->is_sta = BSU_UPDATE_STA_TYPE_STA; for (i = 0; i < COUNT (ctx->mfs_beacons); i++) ctx->mfs_beacons[i] = NULL; for (i = 0; i < COUNT (ctx->beacon_nb_recv); i++) ctx->beacon_nb_recv[i] = 0; for (i = 0; i < COUNT (ctx->beacon_nb_sent); i++) ctx->beacon_nb_sent[i] = 0; for (i = 0; i < COUNT (ctx->ca_schedules_in_use); i++) ctx->ca_schedules_in_use[i] = CA_SCHEDULE_NB; ctx->ntb_clk_sync_weight_k = BSU_NTB_CLK_SYNC_WEIGHT_K_DEFAULT; ctx->ntb_clk_sync_nb_stable = BSU_NTB_CLK_SYNC_NB_STABLE_DEFAULT; /* Trace. */ bsu_trace_init (ctx); bsu_stats_init (ctx); return ctx; } void bsu_init_beacon_cb (bsu_t *ctx, bsu_beacon_processed_t cb, void *cb_ud) { ctx->ul_cb = cb; ctx->ul_data = cb_ud; } void bsu_uninit (bsu_t *ctx) { dbg_assert (ctx); hal_timer_instance_cancel (ctx->hal_timer, &ctx->timer); bsu_ntb_uninit (&ctx->sta_avln->sync); uint i; for (i = 0; i < BSU_FOREIGN_AVLNS_NB; i++) bsu_ntb_uninit (&ctx->avlns[i].sync); hal_timer_instance_cancel (ctx->hal_timer, &ctx->timer); bsu_clear_mfs_beacons (ctx); bsu_trace_uninit (ctx); } /** * Get the information from the beacon of the tracked AVLN. * \param ctx the module context. * \param beacon the beacon unpacked. * \param params the RX parameters. */ PRIVATE inline bsu_avln_t* bsu_beacon_process__avln_tracked (bsu_t *ctx, bsu_beacon_t *beacon, pbproc_rx_beacon_params_t *params) { u32 i, bts_date, bpsd0; /* Inform PBProc we received the central beacon. */ pbproc_beacon_detected ( ctx->pbproc, params->preamble_date + bsu_aclf_beacon_period_tck (ctx->aclf)); memcpy (&ctx->sta_avln->beacon, beacon, sizeof (bsu_beacon_t)); /* NTB synchronisation. */ bsu_ntb_clk_sync (&ctx->sta_avln->sync, params->bts, params->preamble_sysdate, params->preamble_date, ctx->ntb_clk_sync_weight_k); /* Configure the clock frequency. */ bsu_ntb_clock_configure ( &ctx->sta_avln->sync, ctx->mac_config, ctx->phy); for (i = 0; i < HPAV_BEACON_BTO_NB; i++) ctx->sta_avln->bto[i] = params->bto[i]; bts_date = params->bts - ctx->sta_avln->sync.ntb_offset_tck; bsu_aclf_beacon_period_start_date (ctx->aclf, &bpsd0, 1); BSU_TRACE ( BEACON_PROCESS, phy_date (), params->snid, beacon->vf.stei, ctx->sta_avln->sync.ntb_offset_tck, FIXED (ctx->sta_avln->sync.fe, BSU_NTB_FIXED_POINT)); if (ctx->track_new == false && lesseq_mod2p32 (bpsd0 - MAC_TOLERANCE_TCK, bts_date)) { bsu_aclf_compute_beacon_period_start_date ( ctx->aclf, bts_date, (s16*) params->bto, ctx->sta_avln->beacon.bmis.bpsto.present ? ctx->sta_avln->beacon.bmis.bpsto.bpsto : 0); /* Create the CA schedules. */ bsu_ca_schedules (ctx, ctx->sta_avln, 0); } BSU_TRACE (BEACON_DATA, params->bts, params->bto[0], params->bto[1], params->bto[2]); /* A discover beacon had been requested ? */ if (ctx->sta_avln->beacon.bmis.discover.present && ctx->sta_avln->beacon.bmis.discover.tei == ctx->mac_config->tei) bsu_beacon_send_prepare (ctx, BSU_BEACON_TYPE_DISCOVER, beacon); ctx->sta_avln->beacon.beacon_period_start_date = bts_date; bsu_stats_store (ctx); return ctx->sta_avln; } /** * Get the information from the beacon of another AVLN. * \param ctx the module context. * \param beacon the beacon unpacked. * \param params the RX parameters. */ PRIVATE inline bsu_avln_t* bsu_beacon_process__avln_not_tracked (bsu_t *ctx, bsu_beacon_t *beacon, pbproc_rx_beacon_params_t *params) { bool added; mac_t mac = beacon->bmis.mac_address.present ? beacon->bmis.mac_address.mac_address: MAC_ZERO; bsu_avln_t *avln = bsu_avln_add ( ctx, beacon->vf.nid, params->snid, mac, &added); u32 bpsd, i; bsu_aclf_beacon_period_start_date (ctx->aclf, &bpsd, 1); /* Other AVLN. */ if ((!added && avln != ctx->sta_avln && less_mod2p32 (avln->beacon.beacon_period_start_date, bpsd)) || added) { for (i = 0; i < HPAV_BEACON_BTO_NB; i++) avln->bto[i] = params->bto[i]; memcpy (&avln->beacon, beacon, sizeof (bsu_beacon_t)); /* NTB synchronisation. */ bsu_ntb_clk_sync (&avln->sync, params->bts, params->preamble_sysdate, params->preamble_date, ctx->ntb_clk_sync_weight_k); avln->beacon.beacon_period_start_date = params->bts - avln->sync.ntb_offset_tck; BSU_TRACE (BEACON_PROCESS, phy_date (), params->snid, beacon->vf.stei, avln ? avln->sync.ntb_offset_tck : 0, avln ? FIXED (avln->sync.fe, BSU_NTB_FIXED_POINT): 0); BSU_TRACE (BEACON_DATA, params->bts, params->bto[0], params->bto[1], params->bto[2]); #if CONFIG_STATS /* Record neighbor network for stats. */ ctx->stats.neighbor_beacon_nb++; ctx->stats.neighbor_beacon_last_mac = mac; #endif /* CONFIG_STATS */ } return avln; } bsu_beacon_t* bsu_beacon_process (bsu_t *ctx, pb_beacon_t *beacon, pbproc_rx_beacon_params_t *params) { bsu_avln_t *avln = NULL; bsu_beacon_t *bsu_beacon = blk_alloc (); dbg_assert (ctx); dbg_assert (beacon); dbg_assert (params); /* Check the CRC. */ if (bsu_beacon_read (beacon, bsu_beacon)) { memcpy (&bsu_beacon->params.rx_parameters, params, sizeof (pbproc_rx_beacon_params_t)); ctx->beacon_nb_recv[bsu_beacon->vf.bt]++; bsu_ca_schedules_nm_csma_only (ctx, bsu_beacon); if (bsu_beacon->vf.bt == BSU_BEACON_TYPE_CENTRAL) { mac_t cco_mac = bsu_beacon->bmis.mac_address.present ? bsu_beacon->bmis.mac_address.mac_address: MAC_ZERO; /* It the beacon from our AVLN ? */ if (ctx->is_sta == BSU_UPDATE_STA_TYPE_STA && ctx->nid_track == bsu_beacon->vf.nid && ctx->snid_track == params->snid && ctx->tei_track == bsu_beacon->vf.stei && ctx->cco_mac_address_track == cco_mac) avln = bsu_beacon_process__avln_tracked ( ctx, bsu_beacon, params); /* Other AVLN. */ else avln = bsu_beacon_process__avln_not_tracked ( ctx, bsu_beacon, params); bsu_beacon->params.frequency_error = FIXED (avln->sync.fe, BSU_NTB_FIXED_POINT); bsu_beacon->params.ntb_offset_tck = avln->sync.ntb_offset_tck; bsu_beacon->params.frequency_error_valid = avln->sync.sync_nb >= ctx->ntb_clk_sync_nb_stable; } } else { BSU_TRACE (BEACON_ERROR, phy_date ()); blk_release (bsu_beacon); return NULL; } return bsu_beacon; } /** * Check if new persistent schedules is scheduled from the CP and copy it into * the BSU context. * \param ctx the module context. * \param beacon the beacon data to be used in the future. */ inline void bsu_update__persistent_schedules (bsu_t *ctx, bsu_beacon_t *beacon) { uint index; dbg_assert ((beacon->bmis.ps.nb == BSU_BEACON_BMI_PERSISTENT_SCHEDULE_MAX && beacon->bmis.ps.ps[0].pscd + beacon->bmis.ps.ps[0].cscd < beacon->bmis.ps.ps[1].pscd) || beacon->bmis.ps.nb != BSU_BEACON_BMI_PERSISTENT_SCHEDULE_MAX); /* If the persistent schedules number are at the maximum * ignore the data from the CP. */ for (index = ctx->sta_avln->beacon.bmis.ps.nb; ctx->sta_avln->beacon.bmis.ps.nb < beacon->bmis.ps.nb; index ++) { ctx->sta_avln->beacon.bmis.ps.nb++; ctx->sta_avln->beacon.bmis.ps.ps[index] = beacon->bmis.ps.ps[index]; uint beacon_delta = (bsu_aclf_beacon_period_start_date_next (ctx->aclf) - beacon->beacon_period_start_date) / bsu_aclf_beacon_period_tck (ctx->aclf); if (ctx->sta_avln->beacon.bmis.ps.ps[index].pscd <= beacon_delta) { beacon_delta -= ctx->sta_avln->beacon.bmis.ps.ps[index].pscd; ctx->sta_avln->beacon.bmis.ps.ps[index].pscd = 0; if (ctx->sta_avln->beacon.vf.nm != MAC_NM_CSMA_ONLY || (ctx->sta_avln->beacon.vf.nm == MAC_NM_CSMA_ONLY && ctx->sta_avln->beacon.bmis.ps.ps[index].cscd != HPAV_SCHEDULE_PERMAMENT_VALUE)) ctx->sta_avln->beacon.bmis.ps.ps[index].cscd -= beacon_delta; } else ctx->sta_avln->beacon.bmis.ps.ps[index].pscd -= beacon_delta; } } void bsu_update (bsu_t *ctx, bsu_beacon_t *beacon, bsu_update_sta_type_t is_sta) { dbg_assert (ctx); dbg_assert (beacon); dbg_assert (ctx->is_sta == is_sta || (ctx->is_sta == BSU_UPDATE_STA_TYPE_STA && is_sta == BSU_UPDATE_STA_TYPE_CCO && ctx->tei_track == MAC_TEI_UNASSOCIATED && ctx->nid_track == 0 && ctx->snid_track == 0) || (ctx->is_sta == BSU_UPDATE_STA_TYPE_CCO && is_sta == BSU_UPDATE_STA_TYPE_STA)); arch_dsr_lock (); ctx->is_sta = is_sta; dbg_assert ((beacon->bmis.ps.nb && beacon->bmis.ps.ps[0].ns > 0) || beacon->bmis.nps.ns); /* If persistent schedules are present and there is more than one. The * second can only be present if the first one has its PSCD equal to 0. * See specification section 5.1.2 Beacon Period Structure Figure 5-3. */ dbg_assert (beacon->bmis.ps.nb < BSU_BEACON_BMI_PERSISTENT_SCHEDULE_MAX || (beacon->bmis.ps.nb == BSU_BEACON_BMI_PERSISTENT_SCHEDULE_MAX && beacon->bmis.ps.ps[0].pscd == 0 && beacon->bmis.ps.ps[0].pscd < beacon->bmis.ps.ps[1].pscd)); dbg_assert (beacon->vf.hm < MAC_COEXISTENCE_NB); bool restore_bmi_eks = false; bsu_beacon_bmi_eks_t bmi_eks; if (ctx->is_sta == BSU_UPDATE_STA_TYPE_CCO && ctx->sta_avln->beacon.bmis.eks.present) { /* 1) The cp should only set eks.present once, just to inform the bsu. * 2) There shouldn't be a key change in the middle of another key * change. */ dbg_assert (!beacon->bmis.eks.present); /* Backup the current key change data. */ bmi_eks = ctx->sta_avln->beacon.bmis.eks; restore_bmi_eks = true; } /* If the data provided are for the next beacon period. */ if (lesseq_mod2p32 (bsu_aclf_beacon_period_start_date_next (ctx->aclf), beacon->beacon_period_start_date) || !ctx->activate) { bsu_update_discover_info (ctx, &beacon->bmis.discover_info); ctx->sta_avln->beacon = *beacon; } /* Data in the beacon had already expired. */ else { /* Copy some payload variant fields. */ ctx->sta_avln->beacon.vf.ncnr = beacon->vf.ncnr; ctx->sta_avln->beacon.vf.npsm = beacon->vf.npsm; ctx->sta_avln->beacon.vf.rtsbf = beacon->vf.rtsbf; ctx->sta_avln->beacon.vf.ccocap = beacon->vf.ccocap; ctx->sta_avln->beacon.vf.hoip = beacon->vf.hoip; /* Copy data in non countdown beacon entry. */ ctx->sta_avln->beacon.bmis.mac_address = beacon->bmis.mac_address; ctx->sta_avln->beacon.bmis.discover = beacon->bmis.discover; ctx->sta_avln->beacon.bmis.discover_info = beacon->bmis.discover_info; ctx->sta_avln->beacon.bmis.bpsto = beacon->bmis.bpsto; ctx->sta_avln->beacon.bmis.nps = beacon->bmis.nps; ctx->sta_avln->beacon.bmis.region = beacon->bmis.region; /* Process the beacon entry with countdowns. */ bsu_update__persistent_schedules (ctx, beacon); ctx->sta_avln->beacon.bmis.eks = beacon->bmis.eks; if (beacon->bmis.handover.present && !ctx->sta_avln->beacon.bmis.handover.present) ctx->sta_avln->beacon.bmis.handover = beacon->bmis.handover; if (beacon->bmis.relocation.present && !ctx->sta_avln->beacon.bmis.relocation.present) ctx->sta_avln->beacon.bmis.relocation = beacon->bmis.relocation; if (beacon->bmis.aclsc.present && !ctx->sta_avln->beacon.bmis.aclsc.present) ctx->sta_avln->beacon.bmis.aclsc = beacon->bmis.aclsc; if (beacon->bmis.cns.present && !ctx->sta_avln->beacon.bmis.cns.present) ctx->sta_avln->beacon.bmis.cns = beacon->bmis.cns; if (beacon->bmis.change_hm.present && !ctx->sta_avln->beacon.bmis.change_hm.present) ctx->sta_avln->beacon.bmis.change_hm = beacon->bmis.change_hm; if (beacon->bmis.change_snid.present && !ctx->sta_avln->beacon.bmis.change_snid.present) ctx->sta_avln->beacon.bmis.change_snid = beacon->bmis.change_snid; if (beacon->bmis.mac_address.present && !ctx->sta_avln->beacon.bmis.mac_address.present) ctx->sta_avln->beacon.bmis.mac_address = beacon->bmis.mac_address; if (beacon->bmis.discover_info.present) ctx->sta_avln->beacon.bmis.discover_info = beacon->bmis.discover_info; } if (restore_bmi_eks) ctx->sta_avln->beacon.bmis.eks = bmi_eks; arch_dsr_unlock (); } void bsu_track_avln (bsu_t *ctx, u64 nid, u16 snid, u8 tei, mac_t mac) { bsu_avln_t *avln; dbg_assert (ctx); arch_dsr_lock (); /* Station acts as STA now. */ ctx->is_sta = BSU_UPDATE_STA_TYPE_STA; avln = bsu_avln_get (ctx, nid, snid, mac); dbg_assert_print ( avln, "No AVLN found with nid 0x%llx snid 0x%x TEI 0x%x mac 0x%llx", nid, snid, tei, mac); bsu_track_avln_identify (ctx, avln); arch_dsr_unlock (); } void bsu_untrack_avln (bsu_t *ctx) { dbg_assert (ctx); arch_dsr_lock (); /* Reset tracking data. */ bsu_track_avln_identify (ctx, NULL); arch_dsr_unlock (); } void bsu_power_on (bsu_t *ctx, u8 snid) { dbg_assert (ctx); arch_dsr_lock (); ctx->sta_avln = &ctx->poweron; ctx->sta_avln->snid = snid; ctx->prev_nek_switch = ctx->nek_switch = 0; dbg_assert (ctx->sta_avln->beacon.bmis.ps.nb != 0 || ctx->sta_avln->beacon.bmis.nps.ns != 0); arch_dsr_unlock (); } void bsu_activate (bsu_t *ctx, bool status) { dbg_assert (ctx); dbg_assert (status != ctx->activate); ctx->activate = status; arch_dsr_lock (); if (status) { /* Program the first schedules in the CA. */ bsu_aclf_acl_frequency_detection (ctx->aclf); bsu_aclf_ac_compute_beacon_period_start_date (ctx->aclf); /* Prepare firsts schedules to the CA.*/ bsu_ca_schedules (ctx, ctx->sta_avln, 0); /* Program the next wake up of the BSU. */ bsu_reprogram_timer ( ctx, bsu_aclf_beacon_period_start_date_next (ctx->aclf)); } /* Stops BSU. */ else { uint i; hal_timer_instance_cancel (ctx->hal_timer, &ctx->timer); for (i = 0; i < HPAV_AVLNS_NB_MAX; i++) bsu_ntb_init (&ctx->avlns[i].sync); bsu_track_avln_identify (ctx, NULL); ctx->avlns_nb = 0; bsu_aclf_clear (ctx->aclf); } arch_dsr_unlock (); } void bsu_update_discover_info ( bsu_t *ctx, bsu_beacon_bmi_discover_info_t *discover) { dbg_assert (ctx); dbg_assert (discover); arch_dsr_lock (); bool discover_updated = bitstream_direct_read (&discover->info_data, 0, 1); bool current_discover_updated = bitstream_direct_read (&ctx->discover_info.info_data, 0, 1); ctx->discover_info = *discover; /* BSU is responsible for reseting the updated flag in this entry. */ if (current_discover_updated || discover_updated) bitstream_direct_write (&ctx->discover_info.info_data, 0, 1, 1); arch_dsr_unlock (); } void bsu_update_nid_snid (bsu_t *ctx, u64 nid, u8 snid) { dbg_assert (ctx); arch_dsr_lock (); ctx->sta_avln->beacon.vf.nid = nid; ctx->sta_avln->snid = snid; arch_dsr_unlock (); } void bsu_update_tracking (bsu_t *ctx, u8 tei, mac_t mac_addr_track) { dbg_assert (ctx); arch_dsr_lock (); ctx->tei_track = tei; ctx->cco_mac_address_track = mac_addr_track; arch_dsr_unlock (); }