1 /*
2 * wmediumd, wireless medium simulator for mac80211_hwsim kernel module
3 * Copyright (c) 2011 cozybit Inc.
4 * Copyright (C) 2020 Intel Corporation
5 *
6 * Author: Javier Lopez <[email protected]>
7 * Javier Cardona <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 */
24
25 #include <sys/timerfd.h>
26 #include <libconfig.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <math.h>
31
32 #include "wmediumd.h"
33 #include "config.h"
34
35 #ifndef MIN
36 #define MIN(a, b) ((a) < (b) ? (a) : (b))
37 #endif
38
39 #ifndef MAX
40 #define MAX(a, b) ((a) > (b) ? (a) : (b))
41 #endif
42
string_to_mac_address(const char * str,u8 * addr)43 static void string_to_mac_address(const char *str, u8 *addr)
44 {
45 int a[ETH_ALEN];
46
47 sscanf(str, "%x:%x:%x:%x:%x:%x",
48 &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]);
49
50 addr[0] = (u8) a[0];
51 addr[1] = (u8) a[1];
52 addr[2] = (u8) a[2];
53 addr[3] = (u8) a[3];
54 addr[4] = (u8) a[4];
55 addr[5] = (u8) a[5];
56 }
57
get_link_snr_default(struct wmediumd * ctx,struct station * sender,struct station * receiver)58 static int get_link_snr_default(struct wmediumd *ctx, struct station *sender,
59 struct station *receiver)
60 {
61 return SNR_DEFAULT;
62 }
63
get_link_snr_from_snr_matrix(struct wmediumd * ctx,struct station * sender,struct station * receiver)64 static int get_link_snr_from_snr_matrix(struct wmediumd *ctx,
65 struct station *sender,
66 struct station *receiver)
67 {
68 return ctx->snr_matrix[sender->index * ctx->num_stas + receiver->index];
69 }
70
_get_error_prob_from_snr(struct wmediumd * ctx,double snr,unsigned int rate_idx,u32 freq,int frame_len,struct station * src,struct station * dst)71 static double _get_error_prob_from_snr(struct wmediumd *ctx, double snr,
72 unsigned int rate_idx, u32 freq,
73 int frame_len,
74 struct station *src, struct station *dst)
75 {
76 return get_error_prob_from_snr(snr, rate_idx, freq, frame_len);
77 }
78
get_error_prob_from_matrix(struct wmediumd * ctx,double snr,unsigned int rate_idx,u32 freq,int frame_len,struct station * src,struct station * dst)79 static double get_error_prob_from_matrix(struct wmediumd *ctx, double snr,
80 unsigned int rate_idx, u32 freq,
81 int frame_len, struct station *src,
82 struct station *dst)
83 {
84 if (dst == NULL) // dst is multicast. returned value will not be used.
85 return 0.0;
86
87 return ctx->error_prob_matrix[ctx->num_stas * src->index + dst->index];
88 }
89
use_fixed_random_value(struct wmediumd * ctx)90 int use_fixed_random_value(struct wmediumd *ctx)
91 {
92 return ctx->error_prob_matrix != NULL;
93 }
94
95 #define FREQ_1CH (2.412e9) // [Hz]
96 #define SPEED_LIGHT (2.99792458e8) // [meter/sec]
97 /*
98 * Calculate path loss based on a free-space path loss
99 *
100 * This function returns path loss [dBm].
101 */
calc_path_loss_free_space(void * model_param,struct station * dst,struct station * src)102 static int calc_path_loss_free_space(void *model_param,
103 struct station *dst, struct station *src)
104 {
105 double PL, d;
106
107 d = sqrt((src->x - dst->x) * (src->x - dst->x) +
108 (src->y - dst->y) * (src->y - dst->y));
109
110 /*
111 * Calculate PL0 with Free-space path loss in decibels
112 *
113 * 20 * log10 * (4 * M_PI * d * f / c)
114 * d: distance [meter]
115 * f: frequency [Hz]
116 * c: speed of light in a vacuum [meter/second]
117 *
118 * https://en.wikipedia.org/wiki/Free-space_path_loss
119 */
120 PL = 20.0 * log10(4.0 * M_PI * d * FREQ_1CH / SPEED_LIGHT);
121 return MAX(PL, 0);
122 }
123 /*
124 * Calculate path loss based on a log distance model
125 *
126 * This function returns path loss [dBm].
127 */
calc_path_loss_log_distance(void * model_param,struct station * dst,struct station * src)128 static int calc_path_loss_log_distance(void *model_param,
129 struct station *dst, struct station *src)
130 {
131 struct log_distance_model_param *param;
132 double PL, PL0, d;
133
134 param = model_param;
135
136 d = sqrt((src->x - dst->x) * (src->x - dst->x) +
137 (src->y - dst->y) * (src->y - dst->y));
138
139 /*
140 * Calculate PL0 with Free-space path loss in decibels
141 *
142 * 20 * log10 * (4 * M_PI * d * f / c)
143 * d: distance [meter]
144 * f: frequency [Hz]
145 * c: speed of light in a vacuum [meter/second]
146 *
147 * https://en.wikipedia.org/wiki/Free-space_path_loss
148 */
149 PL0 = 20.0 * log10(4.0 * M_PI * 1.0 * FREQ_1CH / SPEED_LIGHT);
150
151 /*
152 * Calculate signal strength with Log-distance path loss model
153 * https://en.wikipedia.org/wiki/Log-distance_path_loss_model
154 */
155 PL = PL0 + 10.0 * param->path_loss_exponent * log10(d) + param->Xg;
156
157 return MAX(PL, 0);
158 }
159 /*
160 * Calculate path loss based on a itu model
161 *
162 * This function returns path loss [dBm].
163 */
calc_path_loss_itu(void * model_param,struct station * dst,struct station * src)164 static int calc_path_loss_itu(void *model_param,
165 struct station *dst, struct station *src)
166 {
167 struct itu_model_param *param;
168 double PL, d;
169 int N=28;
170
171 param = model_param;
172
173 d = sqrt((src->x - dst->x) * (src->x - dst->x) +
174 (src->y - dst->y) * (src->y - dst->y));
175
176 if (d>16)
177 N=38;
178 /*
179 * Calculate signal strength with ITU path loss model
180 * Power Loss Coefficient Based on the Paper
181 * Site-Specific Validation of ITU Indoor Path Loss Model at 2.4 GHz
182 * from Theofilos Chrysikos, Giannis Georgopoulos and Stavros Kotsopoulos
183 * LF: floor penetration loss factor
184 * nFLOORS: number of floors
185 */
186 PL = 20.0 * log10(FREQ_1CH) + N * log10(d) + param->LF * param->nFLOORS - 28;
187 return MAX(PL, 0);
188 }
189
recalc_path_loss(struct wmediumd * ctx)190 static void recalc_path_loss(struct wmediumd *ctx)
191 {
192 int start, end, tx_power, path_loss, signal;
193 for (start = 0; start < ctx->num_stas; start++) {
194 for (end = 0; end < ctx->num_stas; end++) {
195 if (start == end) {
196 continue;
197 }
198
199 tx_power = ctx->sta_array[start]->tx_power;
200 path_loss = ctx->calc_path_loss(ctx->path_loss_param, ctx->sta_array[end], ctx->sta_array[start]);
201 // Test breakage exists in WifiStatsTests when signal is not negative value.
202 signal = MIN(tx_power - path_loss, -1);
203 ctx->snr_matrix[ctx->num_stas * start + end] = signal - NOISE_LEVEL;
204 }
205 }
206 }
207
move_stations_to_direction(struct usfstl_job * job)208 static void move_stations_to_direction(struct usfstl_job *job)
209 {
210 struct wmediumd *ctx = job->data;
211 struct station *station;
212
213 list_for_each_entry(station, &ctx->stations, list) {
214 station->x += station->dir_x;
215 station->y += station->dir_y;
216 }
217 recalc_path_loss(ctx);
218
219 job->start += MOVE_INTERVAL * 1000000;
220 usfstl_sched_add_job(&scheduler, job);
221 }
222
parse_path_loss(struct wmediumd * ctx,config_t * cf)223 static int parse_path_loss(struct wmediumd *ctx, config_t *cf)
224 {
225 struct station *station;
226 const config_setting_t *positions, *position;
227 const config_setting_t *directions, *direction;
228 const config_setting_t *tx_powers, *model;
229 const char *path_loss_model_name;
230
231 positions = config_lookup(cf, "model.positions");
232 if (!positions) {
233 w_flogf(ctx, LOG_ERR, stderr,
234 "No positions found in model\n");
235 return -EINVAL;
236 }
237 if (config_setting_length(positions) != ctx->num_stas) {
238 w_flogf(ctx, LOG_ERR, stderr,
239 "Specify %d positions\n", ctx->num_stas);
240 return -EINVAL;
241 }
242
243 directions = config_lookup(cf, "model.directions");
244 if (directions) {
245 if (config_setting_length(directions) != ctx->num_stas) {
246 w_flogf(ctx, LOG_ERR, stderr,
247 "Specify %d directions\n", ctx->num_stas);
248 return -EINVAL;
249 }
250 ctx->move_job.start = MOVE_INTERVAL * 1000000;
251 ctx->move_job.name = "move";
252 ctx->move_job.data = ctx;
253 ctx->move_job.callback = move_stations_to_direction;
254 usfstl_sched_add_job(&scheduler, &ctx->move_job);
255 }
256
257 tx_powers = config_lookup(cf, "model.tx_powers");
258 if (!tx_powers) {
259 w_flogf(ctx, LOG_ERR, stderr,
260 "No tx_powers found in model\n");
261 return -EINVAL;
262 }
263 if (config_setting_length(tx_powers) != ctx->num_stas) {
264 w_flogf(ctx, LOG_ERR, stderr,
265 "Specify %d tx_powers\n", ctx->num_stas);
266 return -EINVAL;
267 }
268
269 model = config_lookup(cf, "model");
270 if (config_setting_lookup_string(model, "model_name",
271 &path_loss_model_name) != CONFIG_TRUE) {
272 w_flogf(ctx, LOG_ERR, stderr, "Specify model_name\n");
273 return -EINVAL;
274 }
275 if (strncmp(path_loss_model_name, "log_distance",
276 sizeof("log_distance")) == 0) {
277 struct log_distance_model_param *param;
278
279 ctx->calc_path_loss = calc_path_loss_log_distance;
280
281 param = malloc(sizeof(*param));
282 if (!param) {
283 w_flogf(ctx, LOG_ERR, stderr,
284 "Out of memory(path_loss_param)\n");
285 return -EINVAL;
286 }
287
288 if (config_setting_lookup_float(model, "path_loss_exp",
289 ¶m->path_loss_exponent) != CONFIG_TRUE) {
290 w_flogf(ctx, LOG_ERR, stderr,
291 "path_loss_exponent not found\n");
292 return -EINVAL;
293 }
294
295 if (config_setting_lookup_float(model, "xg",
296 ¶m->Xg) != CONFIG_TRUE) {
297 w_flogf(ctx, LOG_ERR, stderr, "xg not found\n");
298 return -EINVAL;
299 }
300 ctx->path_loss_param = param;
301 }
302 else if (strncmp(path_loss_model_name, "free_space",
303 sizeof("free_space")) == 0) {
304 struct log_distance_model_param *param;
305 ctx->calc_path_loss = calc_path_loss_free_space;
306 param = malloc(sizeof(*param));
307 if (!param) {
308 w_flogf(ctx, LOG_ERR, stderr,
309 "Out of memory(path_loss_param)\n");
310 return -EINVAL;
311 }
312 }
313 else if (strncmp(path_loss_model_name, "itu",
314 sizeof("itu")) == 0) {
315 struct itu_model_param *param;
316 ctx->calc_path_loss = calc_path_loss_itu;
317 param = malloc(sizeof(*param));
318 if (!param) {
319 w_flogf(ctx, LOG_ERR, stderr,
320 "Out of memory(path_loss_param)\n");
321 return -EINVAL;
322 }
323
324 if (config_setting_lookup_int(model, "nFLOORS",
325 ¶m->nFLOORS) != CONFIG_TRUE) {
326 w_flogf(ctx, LOG_ERR, stderr,
327 "nFLOORS not found\n");
328 return -EINVAL;
329 }
330
331 if (config_setting_lookup_int(model, "LF",
332 ¶m->LF) != CONFIG_TRUE) {
333 w_flogf(ctx, LOG_ERR, stderr, "LF not found\n");
334 return -EINVAL;
335 }
336 ctx->path_loss_param = param;
337 }
338 else {
339 w_flogf(ctx, LOG_ERR, stderr, "No path loss model found\n");
340 return -EINVAL;
341 }
342
343 list_for_each_entry(station, &ctx->stations, list) {
344 position = config_setting_get_elem(positions, station->index);
345 if (config_setting_length(position) != 2) {
346 w_flogf(ctx, LOG_ERR, stderr,
347 "Invalid position: expected (double,double)\n");
348 return -EINVAL;
349 }
350 station->x = config_setting_get_float_elem(position, 0);
351 station->y = config_setting_get_float_elem(position, 1);
352
353 if (directions) {
354 direction = config_setting_get_elem(directions,
355 station->index);
356 if (config_setting_length(direction) != 2) {
357 w_flogf(ctx, LOG_ERR, stderr,
358 "Invalid direction: expected (double,double)\n");
359 return -EINVAL;
360 }
361 station->dir_x = config_setting_get_float_elem(
362 direction, 0);
363 station->dir_y = config_setting_get_float_elem(
364 direction, 1);
365 }
366
367 station->tx_power = config_setting_get_float_elem(
368 tx_powers, station->index);
369 }
370
371 recalc_path_loss(ctx);
372
373 return 0;
374 }
375
pseudo_normal_distribution(void)376 static double pseudo_normal_distribution(void)
377 {
378 int i;
379 double normal = -6.0;
380
381 for (i = 0; i < 12; i++)
382 normal += drand48();
383
384 return normal;
385 }
386
_get_fading_signal(struct wmediumd * ctx)387 static int _get_fading_signal(struct wmediumd *ctx)
388 {
389 return ctx->fading_coefficient * pseudo_normal_distribution();
390 }
391
get_no_fading_signal(struct wmediumd * ctx)392 static int get_no_fading_signal(struct wmediumd *ctx)
393 {
394 return 0;
395 }
396
397 /* Existing link is from from -> to; copy to other dir */
mirror_link(struct wmediumd * ctx,int from,int to)398 static void mirror_link(struct wmediumd *ctx, int from, int to)
399 {
400 ctx->snr_matrix[ctx->num_stas * to + from] =
401 ctx->snr_matrix[ctx->num_stas * from + to];
402
403 if (ctx->error_prob_matrix) {
404 ctx->error_prob_matrix[ctx->num_stas * to + from] =
405 ctx->error_prob_matrix[ctx->num_stas * from + to];
406 }
407 }
408
validate_config(const char * file)409 int validate_config(const char* file) {
410 struct wmediumd ctx = {};
411
412 INIT_LIST_HEAD(&ctx.stations);
413 INIT_LIST_HEAD(&ctx.clients);
414 INIT_LIST_HEAD(&ctx.clients_to_free);
415
416 int load_result = load_config(&ctx, file, NULL);
417
418 clear_config(&ctx);
419
420 if (load_result < 0) return 0;
421
422 return 1;
423 }
424
425 /*
426 * Loads a config file into memory
427 */
load_config(struct wmediumd * ctx,const char * file,const char * per_file)428 int load_config(struct wmediumd *ctx, const char *file, const char *per_file)
429 {
430 config_t cfg, *cf;
431 const config_setting_t *ids, *links, *model_type;
432 const config_setting_t *error_probs = NULL, *error_prob;
433 const config_setting_t *enable_interference;
434 const config_setting_t *fading_coefficient, *default_prob;
435 int count_ids, i, j;
436 int start, end, snr;
437 struct station *station;
438 const char *model_type_str;
439 float default_prob_value = 0.0;
440 bool *link_map = NULL;
441
442 ctx->config_path = strdup(file);
443
444 /*initialize the config file*/
445 cf = &cfg;
446 config_init(cf);
447
448 /*read the file*/
449 if (!config_read_file(cf, file)) {
450 w_logf(ctx, LOG_ERR, "Error loading file %s at line:%d, reason: %s\n",
451 file,
452 config_error_line(cf),
453 config_error_text(cf));
454 config_destroy(cf);
455 return -EIO;
456 }
457
458 ids = config_lookup(cf, "ifaces.ids");
459 if (!ids) {
460 w_logf(ctx, LOG_ERR, "ids not found in config file\n");
461 return -EIO;
462 }
463 count_ids = config_setting_length(ids);
464
465 w_logf(ctx, LOG_NOTICE, "#_if = %d\n", count_ids);
466
467 /* Fill the mac_addr */
468 ctx->sta_array = calloc(count_ids, sizeof(struct station *));
469 if (!ctx->sta_array) {
470 w_flogf(ctx, LOG_ERR, stderr, "Out of memory(sta_array)!\n");
471 return -ENOMEM;
472 }
473 for (i = 0; i < count_ids; i++) {
474 u8 addr[ETH_ALEN];
475 const char *str = config_setting_get_string_elem(ids, i);
476 string_to_mac_address(str, addr);
477
478 station = calloc(1, sizeof(*station));
479 if (!station) {
480 w_flogf(ctx, LOG_ERR, stderr, "Out of memory!\n");
481 return -ENOMEM;
482 }
483 station->index = i;
484 memcpy(station->addr, addr, ETH_ALEN);
485 memcpy(station->hwaddr, addr, ETH_ALEN);
486 station->tx_power = SNR_DEFAULT;
487 station_init_queues(station);
488 list_add_tail(&station->list, &ctx->stations);
489 ctx->sta_array[i] = station;
490
491 w_logf(ctx, LOG_NOTICE, "Added station %d: " MAC_FMT "\n", i, MAC_ARGS(addr));
492 }
493 ctx->num_stas = count_ids;
494
495 enable_interference = config_lookup(cf, "ifaces.enable_interference");
496 if (enable_interference &&
497 config_setting_get_bool(enable_interference)) {
498 ctx->intf = calloc(ctx->num_stas * ctx->num_stas,
499 sizeof(struct intf_info));
500 if (!ctx->intf) {
501 w_flogf(ctx, LOG_ERR, stderr, "Out of memory(intf)\n");
502 return -ENOMEM;
503 }
504 for (i = 0; i < ctx->num_stas; i++)
505 for (j = 0; j < ctx->num_stas; j++)
506 ctx->intf[i * ctx->num_stas + j].signal = -200;
507 } else {
508 ctx->intf = NULL;
509 }
510
511 fading_coefficient =
512 config_lookup(cf, "model.fading_coefficient");
513 if (fading_coefficient &&
514 config_setting_get_int(fading_coefficient) > 0) {
515 ctx->get_fading_signal = _get_fading_signal;
516 ctx->fading_coefficient =
517 config_setting_get_int(fading_coefficient);
518 } else {
519 ctx->get_fading_signal = get_no_fading_signal;
520 ctx->fading_coefficient = 0;
521 }
522
523 /* create link quality matrix */
524 ctx->snr_matrix = calloc(sizeof(int), count_ids * count_ids);
525 if (!ctx->snr_matrix) {
526 w_flogf(ctx, LOG_ERR, stderr, "Out of memory!\n");
527 return -ENOMEM;
528 }
529 /* set default snrs */
530 for (i = 0; i < count_ids * count_ids; i++)
531 ctx->snr_matrix[i] = SNR_DEFAULT;
532
533 links = config_lookup(cf, "ifaces.links");
534 if (!links) {
535 model_type = config_lookup(cf, "model.type");
536 if (model_type) {
537 model_type_str = config_setting_get_string(model_type);
538 if (memcmp("snr", model_type_str, strlen("snr")) == 0) {
539 links = config_lookup(cf, "model.links");
540 } else if (memcmp("prob", model_type_str,
541 strlen("prob")) == 0) {
542 error_probs = config_lookup(cf, "model.links");
543 } else if (memcmp("path_loss", model_type_str,
544 strlen("path_loss")) == 0) {
545 /* calculate signal from positions */
546 if (parse_path_loss(ctx, cf))
547 goto fail;
548 }
549 }
550 }
551
552 if (per_file && error_probs) {
553 w_flogf(ctx, LOG_ERR, stderr,
554 "per_file and error_probs could not be used at the same time\n");
555 goto fail;
556 }
557
558 ctx->get_link_snr = get_link_snr_from_snr_matrix;
559 ctx->get_error_prob = _get_error_prob_from_snr;
560
561 ctx->per_matrix = NULL;
562 ctx->per_matrix_row_num = 0;
563 if (per_file && read_per_file(ctx, per_file))
564 goto fail;
565
566 ctx->error_prob_matrix = NULL;
567 if (error_probs) {
568 ctx->error_prob_matrix = calloc(sizeof(double),
569 count_ids * count_ids);
570 if (!ctx->error_prob_matrix) {
571 w_flogf(ctx, LOG_ERR, stderr,
572 "Out of memory(error_prob_matrix)\n");
573 goto fail;
574 }
575
576 ctx->get_link_snr = get_link_snr_default;
577 ctx->get_error_prob = get_error_prob_from_matrix;
578
579 default_prob = config_lookup(cf, "model.default_prob");
580 if (default_prob) {
581 default_prob_value = config_setting_get_float(
582 default_prob);
583 if (default_prob_value < 0.0 ||
584 default_prob_value > 1.0) {
585 w_flogf(ctx, LOG_ERR, stderr,
586 "model.default_prob should be in [0.0, 1.0]\n");
587 goto fail;
588 }
589 }
590 }
591
592 link_map = calloc(sizeof(bool), count_ids * count_ids);
593 if (!link_map) {
594 w_flogf(ctx, LOG_ERR, stderr, "Out of memory\n");
595 goto fail;
596 }
597
598 /* read snr values */
599 for (i = 0; links && i < config_setting_length(links); i++) {
600 config_setting_t *link;
601
602 link = config_setting_get_elem(links, i);
603 if (config_setting_length(link) != 3) {
604 w_flogf(ctx, LOG_ERR, stderr, "Invalid link: expected (int,int,int)\n");
605 goto fail;
606 }
607 start = config_setting_get_int_elem(link, 0);
608 end = config_setting_get_int_elem(link, 1);
609 snr = config_setting_get_int_elem(link, 2);
610
611 if (start < 0 || start >= ctx->num_stas ||
612 end < 0 || end >= ctx->num_stas) {
613 w_flogf(ctx, LOG_ERR, stderr, "Invalid link [%d,%d,%d]: index out of range\n",
614 start, end, snr);
615 goto fail;
616 }
617 ctx->snr_matrix[ctx->num_stas * start + end] = snr;
618 link_map[ctx->num_stas * start + end] = true;
619 }
620
621 /* initialize with default_prob */
622 for (start = 0; error_probs && start < ctx->num_stas; start++)
623 for (end = start + 1; end < ctx->num_stas; end++) {
624 ctx->error_prob_matrix[ctx->num_stas *
625 start + end] =
626 ctx->error_prob_matrix[ctx->num_stas *
627 end + start] = default_prob_value;
628 }
629
630 /* read error probabilities */
631 for (i = 0; error_probs &&
632 i < config_setting_length(error_probs); i++) {
633 float error_prob_value;
634
635 error_prob = config_setting_get_elem(error_probs, i);
636 if (config_setting_length(error_prob) != 3) {
637 w_flogf(ctx, LOG_ERR, stderr, "Invalid error probability: expected (int,int,float)\n");
638 goto fail;
639 }
640
641 start = config_setting_get_int_elem(error_prob, 0);
642 end = config_setting_get_int_elem(error_prob, 1);
643 error_prob_value = config_setting_get_float_elem(error_prob, 2);
644
645 if (start < 0 || start >= ctx->num_stas ||
646 end < 0 || end >= ctx->num_stas ||
647 error_prob_value < 0.0 || error_prob_value > 1.0) {
648 w_flogf(ctx, LOG_ERR, stderr, "Invalid error probability [%d,%d,%f]\n",
649 start, end, error_prob_value);
650 goto fail;
651 }
652
653 ctx->error_prob_matrix[ctx->num_stas * start + end] =
654 error_prob_value;
655 link_map[ctx->num_stas * start + end] = true;
656 }
657
658 /*
659 * If any links are specified in only one direction, mirror them,
660 * making them symmetric. If specified in both directions they
661 * can be asymmetric.
662 */
663 for (i = 0; i < ctx->num_stas; i++) {
664 for (j = 0; j < ctx->num_stas; j++) {
665 if (i == j)
666 continue;
667
668 if (link_map[ctx->num_stas * i + j] &&
669 !link_map[ctx->num_stas * j + i]) {
670 mirror_link(ctx, i, j);
671 }
672 }
673 }
674
675 free(link_map);
676 config_destroy(cf);
677 return 0;
678
679 fail:
680 free(ctx->snr_matrix);
681 free(ctx->error_prob_matrix);
682 config_destroy(cf);
683 return -EINVAL;
684 }
685
clear_config(struct wmediumd * ctx)686 int clear_config(struct wmediumd *ctx) {
687 free(ctx->sta_array);
688 free(ctx->intf);
689 free(ctx->snr_matrix);
690 free(ctx->error_prob_matrix);
691 free(ctx->config_path);
692
693 ctx->sta_array = NULL;
694 ctx->intf = NULL;
695 ctx->snr_matrix = NULL;
696 ctx->error_prob_matrix = NULL;
697 ctx->config_path = NULL;
698
699 while (!list_empty(&ctx->stations)) {
700 struct station *station;
701
702 station = list_first_entry(&ctx->stations,
703 struct station, list);
704
705 list_del(&station->list);
706
707 free(station->lci);
708 free(station->civicloc);
709 free(station);
710 }
711
712 return 0;
713 }
714
calc_path_loss(struct wmediumd * ctx)715 void calc_path_loss(struct wmediumd *ctx) {
716 recalc_path_loss(ctx);
717 }