summaryrefslogtreecommitdiff
path: root/cesar/bsu/src/bsu.c
blob: 9c3b9c0a24935e708b1120ad83974884a613a2ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
/* Cesar project {{{
 *
 * Copyright (C) 2010 Spidcom
 *
 * <<<Licence>>>
 *
 * }}} */
/**
 * \file    bsu/src/bsu.c
 * \brief   BSU core functions.
 * \ingroup bsu
 */
#include "common/std.h"
#include "lib/bitstream.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"

/* 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

/* Define the delay to program the timer. */
#define BSU_WAKEUP_DELAY_MS 2

/** Static declaration. */
static bsu_t bsu_global;

/**
 * Get the associated AVLN.
 * \param  ctx  the module context.
 * \param  nid  the NID of the AVLN.
 * \param  snid  the SNID of the AVLN.
 * \return  the AVLN object.
 *
 * The AVLNs are static object do not release it.
 */
static inline bsu_avln_t*
bsu_avln_get (bsu_t *ctx, u64 nid, u16 snid)
{
    uint i;
    for (i = 0; i < ctx->avlns_nb; i++)
        if (ctx->avlns[i].bs.nid == nid && ctx->avlns[i].bs.snid == snid)
            return &ctx->avlns[i];
    return NULL;
}

/**
 * Add an AVLN.
 * \param  ctx  the module context.
 * \param  nid  the NID of the AVLN.
 * \param  snid  the SNID of the AVLN.
 * \return  the AVLN object.
 *
 * The AVLNs are static object do not release it.
 */
static inline bsu_avln_t*
bsu_avln_add (bsu_t *ctx, u64 nid, u16 snid)
{
    bsu_avln_t* avln = bsu_avln_get (ctx, nid, snid);
    if (!avln && ctx->avlns_nb < BSU_FOREIGN_AVLNS_NB)
    {
        avln = &ctx->avlns[ctx->avlns_nb];
        avln->bs.nid = nid;
        avln->bs.snid = snid;
        ctx->avlns_nb++;
    }
    return avln;
}

/**
 * Initialise the process position index object.
 * \param  ctx  the module context.
 * \param  sched  the schedules.
 * \param  proc  the process object.
 * \param  beacon_period_index  the beacon period for the one the schedule is
 * being computed.
 */
static inline void
bsu_schedules_merge_process_init (bsu_t *ctx, bsu_beacon_schedules_t *sched,
                                  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 < sched->ps.nb; i++)
    {
        if (sched->ps.ps[i].pscd <= beacon_period_index
            && sched->ps.ps[i].pscd + sched->ps.ps[i].cscd
                >= beacon_period_index)
        {
            proc->ps = &sched->ps.ps[i];
            proc->ps_alloc_index = 0;
            break;
        }
    }
    /* Non persistent schedule index. */
    if (sched->nps.ns)
        proc->nps_alloc_index = 0;
}

/**
 * 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.
 */
static 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  proc_schedule  the process schedule structure to take the first
 * allocation.
 * \return  the first SAI of the non persistent schedule.
 */
static inline bsu_beacon_sai_t*
bsu_schedules_process__nps_first_allocation (
    bsu_t *ctx, bsu_avln_schedules_process_t *proc,
    bsu_beacon_schedules_t *schedules)
{
    bsu_beacon_sai_t *sai = &schedules->nps.sais[proc->nps_alloc_index++];
    if (proc->nps_alloc_index == schedules->nps.ns)
        proc->nps_alloc_index = BSU_MERGE_PROCESS_INVALID;
    return sai;
}

/**
 * Get the lesser schedule.
 * \param  ctx  the module context.
 * \param  asched  the schedules read in the beacon.
 * \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_schedules_t *asched,
                                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,
                                                           asched);
    /* 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 <
            asched->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,
                                                               asched);
    }
    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
 */
static inline void
bsu_schedules_merge__allocation_add (bsu_t *ctx, ca_schedule_t *schedules,
                                     uint pos, u32 end_time_atu, u8 glid)
{
    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  asched  the schedules read in the beacon.
 * \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_schedules_t *asched,
                     ca_schedule_t *schedules, uint bp_index)
{
    uint alloc = 0;
    bsu_beacon_sai_t *sai;
    bsu_avln_schedules_process_t process;
    bsu_schedules_merge_process_init (ctx, asched, &process, bp_index);
    while ((sai = bsu_schedules_merge_get_lesser (ctx, asched, &process)))
    {
        /* Add a hole between the previous allocation end this one. */
        if ((alloc
             && sai->stpf
             && MAC_ATU_TO_TCK(sai->start_time_atu) !=
             schedules->allocations[alloc-1].end_offset_tck)
            || (alloc == 0 && sai->start_time_atu != 0))
        {
            bsu_schedules_merge__allocation_add (ctx, schedules, alloc,
                                                 sai->start_time_atu,
                                                 MAC_LID_SPC_HOLE);
            alloc++;
        }
        /* Add this allocation. */
        bsu_schedules_merge__allocation_add (ctx, schedules, alloc,
                                             sai->end_time_atu,
                                             sai->glid);
        alloc++;
    }
    /* store the number of allocations. */
    schedules->allocations_nb = alloc;
    /* Store the end of data for this schedule-> */
    schedules->coexistence_mode = asched->hm[bp_index];
    schedules->nek_switch = asched->nek_switch[bp_index];
    schedules->snid = asched->snid[bp_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  schedules  the CA schedules objects to fill.
 * \param  nb  number of schedules.
 *
 * Merge the schedules read from the beacon.
 * Provide the schedules to the CA.
 */
inline void
bsu_ca_schedules (bsu_t *ctx, bsu_avln_t *avln)
{
    ca_beacon_period_t beacon_period[BSU_BEACON_PERIOD_NB];
    ca_schedule_t *schedules;
    u32 bpsd[BSU_BEACON_PERIOD_NB];
    uint i;
    bsu_aclf_beacon_period_start_date (ctx->aclf, bpsd, COUNT (bpsd));
    /* Get the CA schedules from the next one and for BPSD_NB. */
    for (i = 0; i < BSU_BEACON_PERIOD_NB; i++)
    {
        schedules = ca_alloc_get_schedule (ctx->ca, ctx->ca_index);
        bsu_schedules_merge (ctx, &avln->bs.schedules, schedules, i);
        beacon_period[i].start_date = bpsd[i];
        beacon_period[i].schedule_index = ctx->ca_index;
        ctx->ca_index = (ctx->ca_index + 1) % CA_SCHEDULE_NB;
    }
    /* Provide the schedules to the CA. */
    ca_alloc_update_beacon_periods (ctx->ca, beacon_period,
                                    BSU_BEACON_PERIOD_NB);
}

/**
 * Decrease schedules countdown.
 * \param  ctx  the module context.
 * \param  avln  the AVLN object.
 */
inline void
bsu_avln_schedules_beacon_not_received (bsu_t *ctx, bsu_avln_t *avln)
{
    uint i;
    for (i = 0; i < avln->bs.schedules.ps.nb; i++)
    {
        /* If one of the persistent schedules has the CSCD == 0, it should be
         * removed from the schedules. */
        if (avln->bs.schedules.ps.ps[i].pscd == 0
            && avln->bs.schedules.ps.ps[i].cscd == 0)
        {
            uint j;
            for (j = i; j < avln->bs.schedules.ps.nb - 1; j++)
                avln->bs.schedules.ps.ps[j] = avln->bs.schedules.ps.ps[j+1];
            avln->bs.schedules.ps.nb--;
        }
        if (avln->bs.schedules.ps.nb)
        {
            if (avln->bs.schedules.ps.ps[i].pscd)
                avln->bs.schedules.ps.ps[i].pscd--;
            else if (avln->bs.schedules.ps.ps[i].cscd)
                avln->bs.schedules.ps.ps[i].cscd--;
        }
    }
}

/**
 * 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;
    u32 bpsd[BSU_BEACON_PERIOD_NB];
    dbg_assert (ctx);
    if (ctx->is_sta)
    {
        /* If it reached this point, the beacon has not been received. */
        bsu_aclf_shift_beacon_period_start_date (ctx->aclf);
        bsu_aclf_beacon_period_start_date (ctx->aclf, bpsd, COUNT (bpsd));
        /* Compute the CA schedules using the last beacon received. */
        bsu_avln_schedules_beacon_not_received (ctx, ctx->sta_avln);
    }
    else
    {
        dbg_assert (ctx->beacon);
        pb_beacon_t *beacon;
        pbproc_tx_beacon_params_t params;
        bsu_aclf_ac_compute_beacon_period_start_date (ctx->aclf);
        bsu_aclf_beacon_period_start_date (ctx->aclf, bpsd, COUNT (bpsd));
        /* Are update data late ?. */
        if (less_mod2p32 (ctx->beacon->beacon_period_start_date, bpsd[0]))
            /* Create and send the beacon. */
            bsu_beacon_countdown (ctx->beacon);
        beacon = bsu_beacon_write (ctx->beacon, BSU_BEACON_TYPE_CENTRAL,
                                   ctx->mac_config, &params);
        /* Send the beacon. */
        bsu_beacon_send (ctx, BSU_BEACON_TYPE_CENTRAL, beacon,
                         &ctx->beacon->bsu_params, &params);
    }
    bsu_ca_schedules (ctx, ctx->sta_avln);
    /* Reprogram the timer. */
    hal_timer_instance_program (ctx->hal_timer, &ctx->timer,
                                bpsd[1]
                                - MAC_MS_TO_TCK (BSU_WAKEUP_DELAY_MS));
}

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,
          bsu_beacon_processed_t cb, void *cb_ud)
{
    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);
    dbg_assert (cb);
    /* 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->ul_cb = cb;
    ctx->ul_data = cb_ud;
    ctx->hal_timer = timer;
    /* Initialise the NTB. */
    uint i;
    for (i = 0; i < HPAV_AVLNS_NB_MAX; i++)
        bsu_ntb_init (&ctx->avlns[i].sync);
    /* Initialise the SAR callback. */
    sar_init_beacon_cb (sar, ctx, (sar_beacon_cb_t)  bsu_beacon_recv);
    /* Initialise timer events. */
    hal_timer_instance_init (timer, &ctx->timer, ctx,
                             bsu_timer_event_process);
#ifndef BSU_UNIT_TEST
    /* Detect the frequency. */
    bsu_aclf_acl_frequency_detection (ctx->aclf);
#endif
    /* Program the timer. */
    hal_timer_instance_program (
        timer, &ctx->timer,
        bsu_aclf_beacon_period_start_date_next (ctx->aclf)
        - MAC_US_TO_TCK (BSU_WAKEUP_DELAY_MS));
    return ctx;
}

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);
}

void
bsu_beacon_process (bsu_t *ctx, pb_beacon_t *beacon,
                    pbproc_rx_beacon_params_t *params)
{
    dbg_assert (ctx);
    dbg_assert (beacon);
    dbg_assert (params);
    /* Check the CRC. */
    if (!((pb_t *) beacon)->phy_pb.pb_rx.pb_measurement.crc_error)
    {
        u64 nid = beacon->first_data_word
            | ((u64)bitstream_direct_read (beacon->data, 0, 22) << 32);
        u8 tei = bitstream_direct_read (beacon->data, 42, 8);
        /* It the beacon from our AVLN ? */
        if (nid == ctx->nid_track && params->snid == ctx->snid_track
            && ctx->tei_track == tei)
        {
            u32 bpsto = 0;
            ctx->sta_avln->bs.nid = nid;
            ctx->sta_avln->bs.snid = params->snid;
            ctx->sta_avln->bs.schedules.bpsto.bpsto = 0;
            ctx->sta_avln->bs.schedules.bpsto.present = false;
            bsu_beacon_read_schedules (beacon, &ctx->sta_avln->bs.schedules);
            if (ctx->sta_avln->bs.schedules.bpsto.present)
                bpsto = ctx->sta_avln->bs.schedules.bpsto.bpsto;
            bsu_aclf_compute_beacon_period_start_date (
                ctx->aclf, params->bts, (s16*) params->bto,
                ctx->sta_avln->bs.schedules.bpsto.bpsto);
            /* Create the CA schedules. */
            bsu_ca_schedules (ctx, ctx->sta_avln);
            /* NTB synchronisation. */
            bsu_ntb_clk_sync (&ctx->sta_avln->sync, ctx->phy,
                              params->bts, params->preamble_sysdate,
                              params->preamble_date);
            /* Configure the clock frequency. */
            bsu_ntb_clock_configure (&ctx->sta_avln->sync, ctx->mac_config,
                                     ctx->phy);
        }
        else
        {
            bsu_avln_t *avln;
            avln = bsu_avln_add (ctx, nid, params->snid);
            if (avln)
            {
                /* NTB synchronisation. */
                bsu_ntb_clk_sync (&avln->sync, ctx->phy,
                                  params->bts, params->preamble_sysdate,
                                  params->preamble_date);
            }
        }
    }
    else
        blk_release_desc ((blk_t*) beacon);
}

void
bsu_update (bsu_beacon_t *beacon, bool is_sta)
{
    bsu_t *ctx = &bsu_global;
    dbg_assert (ctx);
    dbg_assert (beacon);
    ctx->beacon = beacon;
    ctx->is_sta = is_sta;
    beacon->beacon_period_start_date = phy_date (ctx->phy);
}

void
bsu_track_avln (bsu_t *ctx, u64 nid, u16 snid, u8 tei)
{
    dbg_assert (ctx);
    ctx->nid_track = nid;
    ctx->snid_track = snid;
    ctx->tei_track = tei;
    ctx->sta_avln = bsu_avln_add (ctx, nid, snid);
}