eNB_scheduler_dlsch.c 93.2 KB
Newer Older
1
2
3
4
5
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
Cedric Roux's avatar
Cedric Roux committed
6
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */
nikaeinn's avatar
nikaeinn committed
21
22
23

/*! \file eNB_scheduler_dlsch.c
 * \brief procedures related to eNB for the DLSCH transport channel
24
 * \author  Navid Nikaein and Raymond Knopp
nikaeinn's avatar
nikaeinn committed
25
 * \date 2010 - 2014
26
 * \email: navid.nikaein@eurecom.fr
nikaeinn's avatar
nikaeinn committed
27
 * \version 1.0
28
29
30
31
 * @ingroup _mac

 */

32
#define _GNU_SOURCE
33

34
35
36
#include "LAYER2/MAC/mac.h"
#include "LAYER2/MAC/mac_proto.h"
#include "LAYER2/MAC/mac_extern.h"
37
38
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
39
40
41
#include "UTIL/OPT/opt.h"
#include "OCG.h"
#include "OCG_extern.h"
Cedric Roux's avatar
Cedric Roux committed
42
#include "PHY/LTE_TRANSPORT/transport_common_proto.h"
43

44
#include "RRC/LTE/rrc_extern.h"
45
46
47
48
49
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"

//#include "LAYER2/MAC/pre_processor.c"
#include "pdcp.h"

50
51
52
#include "SIMULATION/TOOLS/sim.h"	// for taus

#include "assertions.h"
53

54
#if defined(ENABLE_ITTI)
55
#include "intertask_interface.h"
56
57
#endif

58
59
#include <dlfcn.h>

Cedric Roux's avatar
Cedric Roux committed
60
61
#include "T.h"

62
#define ENABLE_MAC_PAYLOAD_DEBUG
knopp's avatar
   
knopp committed
63
//#define DEBUG_eNB_SCHEDULER 1
64

65
#include "common/ran_context.h"
66
extern RAN_CONTEXT_t RC;
67
extern uint8_t nfapi_mode;
68

69
70
//------------------------------------------------------------------------------
void
71
add_ue_dlsch_info(module_id_t module_idP,
72
73
                  int CC_id,
                  int UE_id, sub_frame_t subframeP, UE_DLSCH_STATUS status)
74
//------------------------------------------------------------------------------
75
{
76
  //LOG_D(MAC, "%s(module_idP:%d, CC_id:%d, UE_id:%d, subframeP:%d, status:%d) serving_num:%d rnti:%x\n", __FUNCTION__, module_idP, CC_id, UE_id, subframeP, status, eNB_dlsch_info[module_idP][CC_id][UE_id].serving_num, UE_RNTI(module_idP,UE_id));
77

knopp's avatar
knopp committed
78
  eNB_dlsch_info[module_idP][CC_id][UE_id].rnti =
79
          UE_RNTI(module_idP, UE_id);
knopp's avatar
knopp committed
80
81
82
  //  eNB_dlsch_info[module_idP][CC_id][ue_mod_idP].weight           = weight;
  eNB_dlsch_info[module_idP][CC_id][UE_id].subframe = subframeP;
  eNB_dlsch_info[module_idP][CC_id][UE_id].status = status;
83

knopp's avatar
knopp committed
84
  eNB_dlsch_info[module_idP][CC_id][UE_id].serving_num++;
85
86
87

}

88
89
//------------------------------------------------------------------------------
int
90
schedule_next_dlue(module_id_t module_idP, int CC_id,
91
                   sub_frame_t subframeP)
92
//------------------------------------------------------------------------------
93
{
94

knopp's avatar
knopp committed
95
96
  int next_ue;
  UE_list_t *UE_list = &RC.mac[module_idP]->UE_list;
97

knopp's avatar
knopp committed
98
99
100
  for (next_ue = UE_list->head; next_ue >= 0;
       next_ue = UE_list->next[next_ue]) {
    if (eNB_dlsch_info[module_idP][CC_id][next_ue].status ==
101
        S_DL_WAITING) {
knopp's avatar
knopp committed
102
      return next_ue;
103
    }
knopp's avatar
knopp committed
104
  }
105

knopp's avatar
knopp committed
106
107
108
109
  for (next_ue = UE_list->head; next_ue >= 0;
       next_ue = UE_list->next[next_ue]) {
    if (eNB_dlsch_info[module_idP][CC_id][next_ue].status == S_DL_BUFFERED) {
      eNB_dlsch_info[module_idP][CC_id][next_ue].status = S_DL_WAITING;
110
    }
knopp's avatar
knopp committed
111
  }
knopp's avatar
   
knopp committed
112

113
  return (-1);        //next_ue;
114
115
116

}

117
//------------------------------------------------------------------------------
118
int
119
generate_dlsch_header(unsigned char *mac_header,
120
121
122
123
124
125
126
127
                      unsigned char num_sdus,
                      unsigned short *sdu_lengths,
                      unsigned char *sdu_lcids,
                      unsigned char drx_cmd,
                      unsigned short timing_advance_cmd,
                      unsigned char *ue_cont_res_id,
                      unsigned char short_padding,
                      unsigned short post_padding)
128
//------------------------------------------------------------------------------
129
{
130

knopp's avatar
knopp committed
131
132
133
  SCH_SUBHEADER_FIXED *mac_header_ptr = (SCH_SUBHEADER_FIXED *) mac_header;
  uint8_t first_element = 0, last_size = 0, i;
  uint8_t mac_header_control_elements[16], *ce_ptr;
134

knopp's avatar
knopp committed
135
  ce_ptr = &mac_header_control_elements[0];
136

knopp's avatar
knopp committed
137
  // compute header components
138

knopp's avatar
knopp committed
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  if ((short_padding == 1) || (short_padding == 2)) {
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = SHORT_PADDING;
    first_element = 1;
    last_size = 1;
  }

  if (short_padding == 2) {
    mac_header_ptr->E = 1;
    mac_header_ptr++;
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = SHORT_PADDING;
    last_size = 1;
  }
155

knopp's avatar
knopp committed
156
157
158
159
160
161
  if (drx_cmd != 255) {
    if (first_element > 0) {
      mac_header_ptr->E = 1;
      mac_header_ptr++;
    } else {
      first_element = 1;
162
    }
163

knopp's avatar
knopp committed
164
165
166
167
168
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = DRX_CMD;
    last_size = 1;
  }
169

knopp's avatar
knopp committed
170
171
172
173
174
175
  if (timing_advance_cmd != 31) {
    if (first_element > 0) {
      mac_header_ptr->E = 1;
      mac_header_ptr++;
    } else {
      first_element = 1;
knopp's avatar
   
knopp committed
176
    }
177

knopp's avatar
knopp committed
178
179
180
181
182
183
184
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = TIMING_ADV_CMD;
    last_size = 1;
    //    msg("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);
    ((TIMING_ADVANCE_CMD *) ce_ptr)->R = 0;
    AssertFatal(timing_advance_cmd < 64,
185
186
                "timing_advance_cmd %d > 63\n", timing_advance_cmd);
    ((TIMING_ADVANCE_CMD *) ce_ptr)->TA = timing_advance_cmd;    //(timing_advance_cmd+31)&0x3f;
knopp's avatar
knopp committed
187
    LOG_D(MAC, "timing advance =%d (%d)\n", timing_advance_cmd,
188
          ((TIMING_ADVANCE_CMD *) ce_ptr)->TA);
knopp's avatar
knopp committed
189
190
191
    ce_ptr += sizeof(TIMING_ADVANCE_CMD);
    //msg("offset %d\n",ce_ptr-mac_header_control_elements);
  }
192

knopp's avatar
knopp committed
193
194
195
196
197
198
199
200
201
202
203
204
  if (ue_cont_res_id) {
    if (first_element > 0) {
      mac_header_ptr->E = 1;
      /*
	printf("[eNB][MAC] last subheader : %x (R%d,E%d,LCID%d)\n",*(unsigned char*)mac_header_ptr,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->R,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID);
      */
      mac_header_ptr++;
    } else {
      first_element = 1;
knopp's avatar
   
knopp committed
205
    }
206

knopp's avatar
knopp committed
207
208
209
210
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = UE_CONT_RES;
    last_size = 1;
211

knopp's avatar
knopp committed
212
    LOG_T(MAC,
213
214
215
          "[eNB ][RAPROC] Generate contention resolution msg: %x.%x.%x.%x.%x.%x\n",
          ue_cont_res_id[0], ue_cont_res_id[1], ue_cont_res_id[2],
          ue_cont_res_id[3], ue_cont_res_id[4], ue_cont_res_id[5]);
216

knopp's avatar
knopp committed
217
218
219
220
221
222
223
224
    memcpy(ce_ptr, ue_cont_res_id, 6);
    ce_ptr += 6;
    // msg("(cont_res) : offset %d\n",ce_ptr-mac_header_control_elements);
  }
  //msg("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);

  for (i = 0; i < num_sdus; i++) {
    LOG_T(MAC, "[eNB] Generate DLSCH header num sdu %d len sdu %d\n",
225
          num_sdus, sdu_lengths[i]);
knopp's avatar
knopp committed
226
227
228
229
230
231
232
233
234
235
236
237

    if (first_element > 0) {
      mac_header_ptr->E = 1;
      /*msg("last subheader : %x (R%d,E%d,LCID%d)\n",*(unsigned char*)mac_header_ptr,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->R,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID);
      */
      mac_header_ptr += last_size;
      //msg("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);
    } else {
      first_element = 1;
knopp's avatar
   
knopp committed
238
    }
239

knopp's avatar
knopp committed
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    if (sdu_lengths[i] < 128) {
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->R = 0;
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->E = 0;
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->F = 0;
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->LCID = sdu_lcids[i];
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->L = (unsigned char) sdu_lengths[i];
      last_size = 2;
    } else {
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->R = 0;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->E = 0;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->F = 1;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->LCID = sdu_lcids[i];
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_MSB = ((unsigned short) sdu_lengths[i] >> 8) & 0x7f;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_LSB = (unsigned short) sdu_lengths[i] & 0xff;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->padding = 0x00;
      last_size = 3;
256
#ifdef DEBUG_HEADER_PARSING
knopp's avatar
knopp committed
257
      LOG_D(MAC,
258
259
260
261
        "[eNB] generate long sdu, size %x (MSB %x, LSB %x)\n",
        sdu_lengths[i],
        ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_MSB,
        ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_LSB);
262
#endif
knopp's avatar
   
knopp committed
263
    }
knopp's avatar
knopp committed
264
  }
265

knopp's avatar
knopp committed
266
  /*
267

knopp's avatar
knopp committed
268
    printf("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);
269

knopp's avatar
knopp committed
270
271
272
273
    printf("last subheader : %x (R%d,E%d,LCID%d)\n",*(unsigned char*)mac_header_ptr,
    ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->R,
    ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E,
    ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID);
274
275


knopp's avatar
knopp committed
276
277
278
279
280
    if (((SCH_SUBHEADER_FIXED*)mac_header_ptr)->LCID < UE_CONT_RES) {
    if (((SCH_SUBHEADER_SHORT*)mac_header_ptr)->F == 0)
    printf("F = 0, sdu len (L field) %d\n",(((SCH_SUBHEADER_SHORT*)mac_header_ptr)->L));
    else
    printf("F = 1, sdu len (L field) %d\n",(((SCH_SUBHEADER_LONG*)mac_header_ptr)->L));
281
    }
knopp's avatar
knopp committed
282
  */
283
  if (post_padding > 0) {    // we have lots of padding at the end of the packet
knopp's avatar
knopp committed
284
285
286
287
288
289
290
    mac_header_ptr->E = 1;
    mac_header_ptr += last_size;
    // add a padding element
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = SHORT_PADDING;
    mac_header_ptr++;
291
  } else {            // no end of packet padding
knopp's avatar
knopp committed
292
293
294
    // last SDU subhead is of fixed type (sdu length implicitly to be computed at UE)
    mac_header_ptr++;
  }
295

knopp's avatar
knopp committed
296
  //msg("After subheaders %d\n",(uint8_t*)mac_header_ptr - mac_header);
297

knopp's avatar
knopp committed
298
299
300
  if ((ce_ptr - mac_header_control_elements) > 0) {
    // printf("Copying %d bytes for control elements\n",ce_ptr-mac_header_control_elements);
    memcpy((void *) mac_header_ptr, mac_header_control_elements,
301
           ce_ptr - mac_header_control_elements);
knopp's avatar
knopp committed
302
    mac_header_ptr +=
303
            (unsigned char) (ce_ptr - mac_header_control_elements);
knopp's avatar
knopp committed
304
305
306
307
  }
  //msg("After CEs %d\n",(uint8_t*)mac_header_ptr - mac_header);

  return ((unsigned char *) mac_header_ptr - mac_header);
308
309
}

310
311
//------------------------------------------------------------------------------
void
312
set_ul_DAI(int module_idP, int UE_idP, int CC_idP, int frameP,
313
           int subframeP)
314
//------------------------------------------------------------------------------
315
316
{

knopp's avatar
knopp committed
317
318
319
320
  eNB_MAC_INST *eNB = RC.mac[module_idP];
  UE_list_t *UE_list = &eNB->UE_list;
  unsigned char DAI;
  COMMON_channels_t *cc = &eNB->common_channels[CC_idP];
321
  if (cc->tdd_Config != NULL) {    //TDD
knopp's avatar
knopp committed
322
323
    DAI = (UE_list->UE_template[CC_idP][UE_idP].DAI - 1) & 3;
    LOG_D(MAC,
324
325
          "[eNB %d] CC_id %d Frame %d, subframe %d: DAI %d for UE %d\n",
          module_idP, CC_idP, frameP, subframeP, DAI, UE_idP);
knopp's avatar
knopp committed
326
327
328
    // Save DAI for Format 0 DCI

    switch (cc->tdd_Config->subframeAssignment) {
329
      case 0:
330
        //      if ((subframeP==0)||(subframeP==1)||(subframeP==5)||(subframeP==6))
331
        break;
332

333
334
335
336
337
      case 1:
        switch (subframeP) {
        case 0:
        case 1:
          UE_list->UE_template[CC_idP][UE_idP].DAI_ul[7] = DAI;
338
        break;
339

340
341
342
        case 4:
          UE_list->UE_template[CC_idP][UE_idP].DAI_ul[8] = DAI;
          break;
343

344
345
346
347
        case 5:
        case 6:
          UE_list->UE_template[CC_idP][UE_idP].DAI_ul[2] = DAI;
          break;
348

349
350
351
352
        case 9:
          UE_list->UE_template[CC_idP][UE_idP].DAI_ul[3] = DAI;
          break;
        }
353

354
355
356
357
      case 2:
        //      if ((subframeP==3)||(subframeP==8))
        //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
        break;
358

359
      case 3:
360

361
362
363
364
365
366
367
368
369
370
        //if ((subframeP==6)||(subframeP==8)||(subframeP==0)) {
        //  LOG_D(MAC,"schedule_ue_spec: setting UL DAI to %d for subframeP %d => %d\n",DAI,subframeP, ((subframeP+8)%10)>>1);
        //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul[((subframeP+8)%10)>>1] = DAI;
        //}
        switch (subframeP) {
          case 5:
          case 6:
          case 1:
            UE_list->UE_template[CC_idP][UE_idP].DAI_ul[2] = DAI;
            break;
371

372
373
374
375
          case 7:
          case 8:
            UE_list->UE_template[CC_idP][UE_idP].DAI_ul[3] = DAI;
            break;
knopp's avatar
knopp committed
376

377
378
379
380
          case 9:
          case 0:
            UE_list->UE_template[CC_idP][UE_idP].DAI_ul[4] = DAI;
            break;
knopp's avatar
knopp committed
381

382
383
384
          default:
            break;
        }
knopp's avatar
knopp committed
385

386
        break;
knopp's avatar
knopp committed
387

388
389
390
391
      case 4:
        //      if ((subframeP==8)||(subframeP==9))
        //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
        break;
knopp's avatar
knopp committed
392

393
394
395
396
397
398
399
400
401
402
403
404
      case 5:
        //      if (subframeP==8)
        //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
        break;

      case 6:
        //      if ((subframeP==1)||(subframeP==4)||(subframeP==6)||(subframeP==9))
        //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
        break;

      default:
        break;
knopp's avatar
   
knopp committed
405
    }
knopp's avatar
knopp committed
406
  }
knopp's avatar
   
knopp committed
407
408
}

409
410
//------------------------------------------------------------------------------
void
411
schedule_dlsch(module_id_t module_idP, frame_t frameP, sub_frame_t subframeP, int *mbsfn_flag) {
412

413
  int i = 0;
414
  slice_info_t *sli = &RC.mac[module_idP]->slice_info;
415

416
417
  sli->tot_pct_dl = 0;
  sli->avg_pct_dl = 1.0 / sli->n_dl;
418
  //sli->slice_counter = sli->n_dl;
419
420

  // reset the slice percentage for inactive slices
421
422
  for (i = sli->n_dl; i < MAX_NUM_SLICES; i++) {
    sli->dl[i].pct = 0;
423
  }
424
425
  for (i = 0; i < sli->n_dl; i++) {
    if (sli->dl[i].pct < 0) {
426
427
      LOG_W(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: invalid percentage %f. resetting to zero",
            module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].pct);
428
      sli->dl[i].pct = 0;
429
    }
430
    sli->tot_pct_dl += sli->dl[i].pct;
431
432
  }

433
  // Check for *intra*slice share activation
434
435
  if (sli->intraslice_share_active_current != sli->intraslice_share_active) {
    if (sli->intraslice_share_active != 1 && sli->intraslice_share_active != 0) {
436
437
      LOG_W(MAC,
            "[eNB %d][DL] frame %d subframe %d: invalid intraslice sharing status (%d), revert to its previous value (%d)\n",
438
439
            module_idP, frameP, subframeP, sli->intraslice_share_active, sli->intraslice_share_active_current);
      sli->intraslice_share_active = sli->intraslice_share_active_current;
440
441
    } else {
      LOG_N(MAC, "[eNB %d][DL] frame %d subframe %d: intraslice sharing status has changed (%x-->%x)\n",
442
443
            module_idP, frameP, subframeP, sli->intraslice_share_active_current, sli->intraslice_share_active);
      sli->intraslice_share_active_current = sli->intraslice_share_active;
444
445
446
447
    }
  }

  // Check for *inter*slice share activation
448
449
  if (sli->interslice_share_active_current != sli->interslice_share_active) {
    if (sli->interslice_share_active != 1 && sli->interslice_share_active != 0) {
450
451
      LOG_W(MAC,
            "[eNB %d][DL] frame %d subframe %d: invalid interslice sharing status (%d), revert to its previous value (%d)\n",
452
453
            module_idP, frameP, subframeP, sli->interslice_share_active, sli->interslice_share_active_current);
      sli->interslice_share_active = sli->interslice_share_active_current;
454
455
    } else {
      LOG_N(MAC, "[eNB %d][DL] frame %d subframe %d: interslice sharing status has changed (%x-->%x)\n",
456
457
            module_idP, frameP, subframeP, sli->interslice_share_active_current, sli->interslice_share_active);
      sli->interslice_share_active_current = sli->interslice_share_active;
458
459
460
    }
  }

461
  for (i = 0; i < sli->n_dl; i++) {
462
463

    // Load any updated functions
464
465
466
467
    if (sli->dl[i].update_sched > 0) {
      sli->dl[i].sched_cb = dlsym(NULL, sli->dl[i].sched_name);
      sli->dl[i].update_sched = 0;
      sli->dl[i].update_sched_current = 0;
468
469
      LOG_I(MAC, "update dl scheduler (%s) slice index %d ID %d\n",
            sli->dl[i].sched_name, i, sli->dl[i].id);
470
471
    }

472
    if (sli->tot_pct_dl <= 1.0) { // the new total RB share is within the range
473
474

      // check if the number of slices has changed, and log
475
476
      if (sli->n_dl_current != sli->n_dl) {
        if ((sli->n_dl > 0) && (sli->n_dl <= MAX_NUM_SLICES)) {
477
          LOG_I(MAC, "[eNB %d]frame %d subframe %d: number of active DL slices has changed: %d-->%d\n",
478
479
                module_idP, frameP, subframeP, sli->n_dl_current, sli->n_dl);
          sli->n_dl_current = sli->n_dl;
480
481
        } else {
          LOG_W(MAC, "invalid number of DL slices %d, revert to the previous value %d\n",
482
483
                sli->n_dl, sli->n_dl_current);
          sli->n_dl = sli->n_dl_current;
484
        }
485
486
487
      }

      // check if the slice rb share has changed, and log the console
488
      if (sli->dl[i].pct_current != sli->dl[i].pct) { // new slice percentage
489
490
        LOG_I(MAC,
              "[eNB %d][SLICE %d][DL] frame %d subframe %d: total percentage %f-->%f, slice RB percentage has changed: %f-->%f\n",
491
492
              module_idP, sli->dl[i].id, frameP, subframeP,
              sli->tot_pct_dl_current, sli->tot_pct_dl,
493
494
495
              sli->dl[i].pct_current, sli->dl[i].pct);
        sli->tot_pct_dl_current = sli->tot_pct_dl;
        sli->dl[i].pct_current = sli->dl[i].pct;
496
497
498
      }

      // check if the slice max MCS, and log the console
499
500
      if (sli->dl[i].maxmcs_current != sli->dl[i].maxmcs) {
        if ((sli->dl[i].maxmcs >= 0) && (sli->dl[i].maxmcs < 29)) {
501
          LOG_I(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: slice MAX MCS has changed: %d-->%d\n",
502
503
                module_idP, sli->dl[i].id, frameP, subframeP,
                sli->dl[i].maxmcs_current, sli->dl[i].maxmcs);
504
          sli->dl[i].maxmcs_current = sli->dl[i].maxmcs;
505
506
        } else {
          LOG_W(MAC, "[eNB %d][SLICE %d][DL] invalid slice max mcs %d, revert the previous value %d\n",
507
                module_idP, sli->dl[i].id, sli->dl[i].maxmcs, sli->dl[i].maxmcs_current);
508
          sli->dl[i].maxmcs = sli->dl[i].maxmcs_current;
509
        }
510
511
512
      }

      // check if a new scheduler, and log the console
513
514
      if (sli->dl[i].update_sched_current != sli->dl[i].update_sched) {
        LOG_I(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: DL scheduler for this slice is updated: %s \n",
515
              module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].sched_name);
516
        sli->dl[i].update_sched_current = sli->dl[i].update_sched;
517
518
519
520
521
      }

    } else {
      // here we can correct the values, e.g. reduce proportionally

522
      if (sli->n_dl == sli->n_dl_current) {
523
524
        LOG_W(MAC,
              "[eNB %d][SLICE %d][DL] invalid total RB share (%f->%f), reduce proportionally the RB share by 0.1\n",
525
              module_idP, sli->dl[i].id, sli->tot_pct_dl_current, sli->tot_pct_dl);
526
527
528
        if (sli->dl[i].pct >= sli->avg_pct_dl) {
          sli->dl[i].pct -= 0.1;
          sli->tot_pct_dl -= 0.1;
529
        }
530
      } else {
531
532
        LOG_W(MAC,
              "[eNB %d][SLICE %d][DL] invalid total RB share (%f->%f), revert the number of slice to its previous value (%d->%d)\n",
533
              module_idP, sli->dl[i].id, sli->tot_pct_dl_current, sli->tot_pct_dl,
534
535
536
              sli->n_dl, sli->n_dl_current);
        sli->n_dl = sli->n_dl_current;
        sli->dl[i].pct = sli->dl[i].pct_current;
537
538
539
      }
    }

540
    // Check for new slice positions
541
542
543
    if (sli->dl[i].pos_low > sli->dl[i].pos_high ||
        sli->dl[i].pos_low < 0 ||
        sli->dl[i].pos_high > N_RBG_MAX) {
544
      LOG_W(MAC, "[eNB %d][SLICE %d][DL] invalid slicing position (%d-%d), using previous values (%d-%d)\n",
545
            module_idP, sli->dl[i].id,
546
547
548
549
            sli->dl[i].pos_low, sli->dl[i].pos_high,
            sli->dl[i].pos_low_current, sli->dl[i].pos_high_current);
      sli->dl[i].pos_low = sli->dl[i].pos_low_current;
      sli->dl[i].pos_high = sli->dl[i].pos_high_current;
550
    } else {
551
      if (sli->dl[i].pos_low_current != sli->dl[i].pos_low) {
552
        LOG_N(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: start frequency has changed (%d-->%d)\n",
553
              module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].pos_low_current, sli->dl[i].pos_low);
554
        sli->dl[i].pos_low_current = sli->dl[i].pos_low;
555
      }
556
      if (sli->dl[i].pos_high_current != sli->dl[i].pos_high) {
557
        LOG_N(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: end frequency has changed (%d-->%d)\n",
558
              module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].pos_high_current, sli->dl[i].pos_high);
559
        sli->dl[i].pos_high_current = sli->dl[i].pos_high;
560
561
562
      }
    }

563
    // Check for new sorting policy
564
    if (sli->dl[i].sorting_current != sli->dl[i].sorting) {
565
      LOG_I(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: UE sorting policy has changed (%x-->%x)\n",
566
            module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].sorting_current, sli->dl[i].sorting);
567
      sli->dl[i].sorting_current = sli->dl[i].sorting;
568
569
570
    }

    // Check for new slice isolation
571
572
    if (sli->dl[i].isol_current != sli->dl[i].isol) {
      if (sli->dl[i].isol != 1 && sli->dl[i].isol != 0) {
573
574
        LOG_W(MAC,
              "[eNB %d][SLICE %d][DL] frame %d subframe %d: invalid slice isolation setting (%d), revert to its previous value (%d)\n",
575
              module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].isol, sli->dl[i].isol_current);
576
        sli->dl[i].isol = sli->dl[i].isol_current;
577
578
      } else {
        LOG_I(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: slice isolation setting has changed (%x-->%x)\n",
579
              module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].isol_current, sli->dl[i].isol);
580
        sli->dl[i].isol_current = sli->dl[i].isol;
581
582
583
584
      }
    }

    // Check for new slice priority
585
586
    if (sli->dl[i].prio_current != sli->dl[i].prio) {
      LOG_I(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: slice priority setting has changed (%d-->%d)\n",
587
            module_idP, sli->dl[i].id, frameP, subframeP, sli->dl[i].prio_current, sli->dl[i].prio);
588
      sli->dl[i].prio_current = sli->dl[i].prio;
589
590
    }

591
    // Check for new accounting policy
592
593
    if (sli->dl[i].accounting_current != sli->dl[i].accounting) {
      if (sli->dl[i].accounting > 1 || sli->dl[i].accounting < 0) {
594
595
        LOG_W(MAC,
              "[eNB %d][SLICE %d][DL] frame %d subframe %d: invalid accounting policy (%d), revert to its previous value (%d)\n",
596
597
              module_idP, sli->dl[i].id, frameP, subframeP,
              sli->dl[i].accounting, sli->dl[i].accounting_current);
598
        sli->dl[i].accounting = sli->dl[i].accounting_current;
599
      } else {
600
        LOG_N(MAC, "[eNB %d][SLICE %d][DL] frame %d subframe %d: UE sorting policy has changed (%x-->%x)\n",
601
602
              module_idP, sli->dl[i].id, frameP, subframeP,
              sli->dl[i].accounting_current, sli->dl[i].accounting);
603
        sli->dl[i].accounting_current = sli->dl[i].accounting;
604
605
606
      }
    }

607
    // Run each enabled slice-specific schedulers one by one
608
    sli->dl[i].sched_cb(module_idP, i, frameP, subframeP, mbsfn_flag/*, dl_info*/);
609
610
611
  }

}
612

613
614
// changes to pre-processor for eMTC

615
616
//------------------------------------------------------------------------------
void
617
schedule_ue_spec(module_id_t module_idP, int slice_idxP,
618
                 frame_t frameP, sub_frame_t subframeP, int *mbsfn_flag)
619
//------------------------------------------------------------------------------
620
{
621
  int CC_id;
knopp's avatar
knopp committed
622
  int UE_id;
623
  int aggregation;
knopp's avatar
knopp committed
624
  mac_rlc_status_resp_t rlc_status;
625
626
627
628
629
630
  int ta_len = 0;
  unsigned char sdu_lcids[NB_RB_MAX];
  int lcid, offset, num_sdus = 0;
  int nb_rb, nb_rb_temp, nb_available_rb;
  uint16_t sdu_lengths[NB_RB_MAX];
  int TBS, j, rnti, padding = 0, post_padding = 0;
knopp's avatar
knopp committed
631
  unsigned char dlsch_buffer[MAX_DLSCH_PAYLOAD_BYTES];
632
633
  int round = 0;
  int harq_pid = 0;
knopp's avatar
knopp committed
634
  eNB_UE_STATS *eNB_UE_stats = NULL;
635
  int sdu_length_total = 0;
knopp's avatar
knopp committed
636
637
638
639
640
641

  eNB_MAC_INST *eNB = RC.mac[module_idP];
  COMMON_channels_t *cc = eNB->common_channels;
  UE_list_t *UE_list = &eNB->UE_list;
  int continue_flag = 0;
  int32_t normalized_rx_power, target_rx_power;
642
  int tpc = 1;
knopp's avatar
knopp committed
643
644
645
  UE_sched_ctrl *ue_sched_ctl;
  int mcs;
  int i;
646
647
648
649
  int min_rb_unit[NFAPI_CC_MAX];
  int N_RB_DL[NFAPI_CC_MAX];
  int total_nb_available_rb[NFAPI_CC_MAX];
  int N_RBG[NFAPI_CC_MAX];
knopp's avatar
knopp committed
650
651
652
653
  nfapi_dl_config_request_body_t *dl_req;
  nfapi_dl_config_request_pdu_t *dl_config_pdu;
  int tdd_sfa;
  int ta_update;
654
655
  int header_length_last;
  int header_length_total;
nikaeinn's avatar
nikaeinn committed
656

knopp's avatar
knopp committed
657
  start_meas(&eNB->schedule_dlsch);
658
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SCHEDULE_DLSCH, VCD_FUNCTION_IN);
659

knopp's avatar
knopp committed
660
661
662
663
  // for TDD: check that we have to act here, otherwise return
  if (cc[0].tdd_Config) {
    tdd_sfa = cc[0].tdd_Config->subframeAssignment;
    switch (subframeP) {
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
      case 0:
        // always continue
        break;
      case 1:
        return;
        break;
      case 2:
        return;
        break;
      case 3:
        if ((tdd_sfa != 2) && (tdd_sfa != 5))
          return;
        break;
      case 4:
        if ((tdd_sfa != 1) && (tdd_sfa != 2) && (tdd_sfa != 4)
            && (tdd_sfa != 5))
          return;
        break;
      case 5:
        break;
      case 6:
      case 7:
        if ((tdd_sfa != 3) && (tdd_sfa != 4) && (tdd_sfa != 5))
          return;
        break;
      case 8:
        if ((tdd_sfa != 2) && (tdd_sfa != 3) && (tdd_sfa != 4)
            && (tdd_sfa != 5))
          return;
        break;
      case 9:
        if (tdd_sfa == 0)
          return;
        break;
698
    }
knopp's avatar
knopp committed
699
700
701
  }
  //weight = get_ue_weight(module_idP,UE_id);
  aggregation = 2;
702
  for (CC_id = 0; CC_id < NFAPI_CC_MAX; CC_id++) {
knopp's avatar
knopp committed
703
704
705
706
707
708
    N_RB_DL[CC_id] = to_prb(cc[CC_id].mib->message.dl_Bandwidth);
    min_rb_unit[CC_id] = get_min_rb_unit(module_idP, CC_id);
    // get number of PRBs less those used by common channels
    total_nb_available_rb[CC_id] = N_RB_DL[CC_id];
    for (i = 0; i < N_RB_DL[CC_id]; i++)
      if (cc[CC_id].vrb_map[i] != 0)
709
        total_nb_available_rb[CC_id]--;
knopp's avatar
knopp committed
710
711
712
713
714
715
716
717
718
719

    N_RBG[CC_id] = to_rbg(cc[CC_id].mib->message.dl_Bandwidth);

    // store the global enb stats:
    eNB->eNB_stats[CC_id].num_dlactive_UEs = UE_list->num_UEs;
    eNB->eNB_stats[CC_id].available_prbs = total_nb_available_rb[CC_id];
    eNB->eNB_stats[CC_id].total_available_prbs += total_nb_available_rb[CC_id];
    eNB->eNB_stats[CC_id].dlsch_bytes_tx = 0;
    eNB->eNB_stats[CC_id].dlsch_pdus_tx = 0;
  }
720

721
722
723
  // CALLING Pre_Processor for downlink scheduling
  // (Returns estimation of RBs required by each UE and the allocation on sub-band)
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_PREPROCESSOR, VCD_FUNCTION_IN);
724

knopp's avatar
   
knopp committed
725
  start_meas(&eNB->schedule_dlsch_preprocessor);
726
  dlsch_scheduler_pre_processor(module_idP,
727
                                slice_idxP,
728
729
                                frameP,
                                subframeP,
Xu Bo's avatar
Xu Bo committed
730
                                mbsfn_flag);
knopp's avatar
   
knopp committed
731
  stop_meas(&eNB->schedule_dlsch_preprocessor);
732
733

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_PREPROCESSOR, VCD_FUNCTION_OUT);
734

735
  //RC.mac[module_idP]->slice_info.slice_counter--;
736
  // Do the multiplexing and actual allocation only when all slices have been pre-processed.
737
738
739
740
741
  //if (RC.mac[module_idP]->slice_info.slice_counter > 0) {
    //stop_meas(&eNB->schedule_dlsch);
    //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SCHEDULE_DLSCH, VCD_FUNCTION_OUT);
    //return;
  //}
742

743
  if (RC.mac[module_idP]->slice_info.interslice_share_active) {
744
745
    dlsch_scheduler_interslice_multiplexing(module_idP, frameP, subframeP);
  }
746
747

  for (CC_id = 0; CC_id < NFAPI_CC_MAX; CC_id++) {
knopp's avatar
knopp committed
748
    LOG_D(MAC, "doing schedule_ue_spec for CC_id %d\n", CC_id);
749

knopp's avatar
knopp committed
750
    dl_req = &eNB->DL_req[CC_id].dl_config_request_body;
751

knopp's avatar
knopp committed
752
    if (mbsfn_flag[CC_id] > 0)
knopp's avatar
   
knopp committed
753
      continue;
754

755
756
    for (UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
      continue_flag = 0; // reset the flag to allow allocation for the remaining UEs
knopp's avatar
knopp committed
757
      rnti = UE_RNTI(module_idP, UE_id);
758
      eNB_UE_stats = &UE_list->eNB_UE_stats[CC_id][UE_id];
759
      ue_sched_ctl = &UE_list->UE_sched_ctrl[UE_id];
760

knopp's avatar
knopp committed
761
      if (rnti == NOT_A_RNTI) {
762
763
        LOG_D(MAC, "Cannot find rnti for UE_id %d (num_UEs %d)\n", UE_id, UE_list->num_UEs);
        continue_flag = 1;
764
      }
765

knopp's avatar
knopp committed
766
      if (eNB_UE_stats == NULL) {
767
768
        LOG_D(MAC, "[eNB] Cannot find eNB_UE_stats\n");
        continue_flag = 1;
769
      }
770

771
      if (!ue_dl_slice_membership(module_idP, UE_id, slice_idxP))
772
        continue;
773

knopp's avatar
knopp committed
774
      if (continue_flag != 1) {
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
        switch (get_tmode(module_idP, CC_id, UE_id)) {
          case 1:
          case 2:
          case 7:
            aggregation = get_aggregation(get_bw_index(module_idP, CC_id),
                                          ue_sched_ctl->dl_cqi[CC_id],
                                          format1);
            break;
          case 3:
            aggregation = get_aggregation(get_bw_index(module_idP, CC_id),
                                          ue_sched_ctl->dl_cqi[CC_id],
                                          format2A);
            break;
          default:
            LOG_W(MAC, "Unsupported transmission mode %d\n", get_tmode(module_idP, CC_id, UE_id));
            aggregation = 2;
        }
knopp's avatar
knopp committed
792
      }
793

knopp's avatar
knopp committed
794
      /* if (continue_flag != 1 */
Cedric Roux's avatar
Cedric Roux committed
795
      if ((ue_sched_ctl->pre_nb_available_rbs[CC_id] == 0) ||	// no RBs allocated
knopp's avatar
knopp committed
796
797
798
799
800
801
802
	  CCE_allocation_infeasible(module_idP, CC_id, 1, subframeP,
				    aggregation, rnti)) {
	LOG_D(MAC,
	      "[eNB %d] Frame %d : no RB allocated for UE %d on CC_id %d: continue \n",
	      module_idP, frameP, UE_id, CC_id);
	continue_flag = 1;	//to next user (there might be rbs availiable for other UEs in TM5
      }
knopp's avatar
   
knopp committed
803

804
805
806
807
808
809
810
811
812
      // If TDD
      if (cc[CC_id].tdd_Config != NULL) {    //TDD
        set_ue_dai(subframeP,
                   UE_id,
                   CC_id,
                   cc[CC_id].tdd_Config->subframeAssignment,
                   UE_list);
        // update UL DAI after DLSCH scheduling
        set_ul_DAI(module_idP, UE_id, CC_id, frameP, subframeP);
knopp's avatar
knopp committed
813
      }
814

knopp's avatar
knopp committed
815
      if (continue_flag == 1) {
816
817
        add_ue_dlsch_info(module_idP, CC_id, UE_id, subframeP, S_DL_NONE);
        continue;
knopp's avatar
knopp committed
818
      }
Cedric Roux's avatar
Cedric Roux committed
819

knopp's avatar
knopp committed
820
      nb_available_rb = ue_sched_ctl->pre_nb_available_rbs[CC_id];
821
      harq_pid = frame_subframe2_dl_harq_pid(cc->tdd_Config,frameP ,subframeP);
822

knopp's avatar
knopp committed
823
      round = ue_sched_ctl->round[CC_id][harq_pid];
824

825
      UE_list->eNB_UE_stats[CC_id][UE_id].crnti = rnti;
knopp's avatar
knopp committed
826
      UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status = mac_eNB_get_rrc_status(module_idP, rnti);
827
      UE_list->eNB_UE_stats[CC_id][UE_id].harq_pid = harq_pid;
knopp's avatar
knopp committed
828
      UE_list->eNB_UE_stats[CC_id][UE_id].harq_round = round;
829

830
831
      if (UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status < RRC_CONNECTED)
        continue;
832

833
      header_length_total = 0;
knopp's avatar
knopp committed
834
835
      sdu_length_total = 0;
      num_sdus = 0;
836

knopp's avatar
knopp committed
837
      /*
838
839
840
      DevCheck(((eNB_UE_stats->dl_cqi < MIN_CQI_VALUE) ||
                (eNB_UE_stats->dl_cqi > MAX_CQI_VALUE)),
                eNB_UE_stats->dl_cqi, MIN_CQI_VALUE, MAX_CQI_VALUE);
knopp's avatar
knopp committed
841
842
      */
      if (nfapi_mode) {
843
844
        eNB_UE_stats->dlsch_mcs1 = 10; // cqi_to_mcs[ue_sched_ctl->dl_cqi[CC_id]];
      } else { // this operation is also done in the preprocessor
845
        eNB_UE_stats->dlsch_mcs1 = cmin(eNB_UE_stats->dlsch_mcs1,
846
                                        RC.mac[module_idP]->slice_info.dl[slice_idxP].maxmcs);  // cmin(eNB_UE_stats->dlsch_mcs1, openair_daq_vars.target_ue_dl_mcs);
knopp's avatar
knopp committed
847
      }
848

849
850
      // Store stats
      // UE_list->eNB_UE_stats[CC_id][UE_id].dl_cqi= eNB_UE_stats->dl_cqi;
851

852
      // Initializing the rb allocation indicator for each UE
knopp's avatar
knopp committed
853
      for (j = 0; j < N_RBG[CC_id]; j++) {
854
        UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] = 0;
knopp's avatar
knopp committed
855
856
857
      }

      LOG_D(MAC,
858
859
860
861
862
            "[eNB %d] Frame %d: Scheduling UE %d on CC_id %d (rnti %x, harq_pid %d, round %d, rb %d, cqi %d, mcs %d, rrc %d)\n",
            module_idP, frameP, UE_id, CC_id, rnti, harq_pid, round,
            nb_available_rb, ue_sched_ctl->dl_cqi[CC_id],
            eNB_UE_stats->dlsch_mcs1,
            UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status);
knopp's avatar
knopp committed
863
864
865

      /* process retransmission  */
      if (round != 8) {
866

867
868
869
        // get freq_allocation
        nb_rb = UE_list->UE_template[CC_id][UE_id].nb_rb[harq_pid];
        TBS = get_TBS_DL(UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid], nb_rb);
knopp's avatar
knopp committed
870
871
872
873
874

	if (nb_rb <= nb_available_rb) {
	  if (cc[CC_id].tdd_Config != NULL) {
	    UE_list->UE_template[CC_id][UE_id].DAI++;
	    update_ul_dci(module_idP, CC_id, rnti,
875
			  UE_list->UE_template[CC_id][UE_id].DAI, subframeP);
876
	    LOG_D(MAC,
knopp's avatar
knopp committed
877
878
879
880
881
		  "DAI update: CC_id %d subframeP %d: UE %d, DAI %d\n",
		  CC_id, subframeP, UE_id,
		  UE_list->UE_template[CC_id][UE_id].DAI);
	  }

882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
          if (nb_rb == ue_sched_ctl->pre_nb_available_rbs[CC_id]) {
            for (j = 0; j < N_RBG[CC_id]; ++j) { // for indicating the rballoc for each sub-band
              UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] = ue_sched_ctl->rballoc_sub_UE[CC_id][j];
            }
          } else {
            nb_rb_temp = nb_rb;
            j = 0;

            while ((nb_rb_temp > 0) && (j < N_RBG[CC_id])) {
              if (ue_sched_ctl->rballoc_sub_UE[CC_id][j] == 1) {
                if (UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j])
                  printf("WARN: rballoc_subband not free for retrans?\n");
                UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] = ue_sched_ctl->rballoc_sub_UE[CC_id][j];

                if ((j == N_RBG[CC_id] - 1) && ((N_RB_DL[CC_id] == 25) || (N_RB_DL[CC_id] == 50))) {
                  nb_rb_temp = nb_rb_temp - min_rb_unit[CC_id] + 1;
                } else {
                  nb_rb_temp = nb_rb_temp - min_rb_unit[CC_id];
                }
              }
902

903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950