diff --git a/src/Makefile b/src/Makefile index 48552a89f41e3ec2ad75cbb00e64602ffb62d1b3..656e03d702790df9d52ffcb35c17e39f41d2b418 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,12 +19,14 @@ OBJS = \ config.o \ main.o +OBJS += steer_module.o + LIBS = -lubus -lubox -ljson-c -lblobmsg_json -luci -pthread LIBS += -rdynamic -ldl LIBS += -leasy LIBS += -lieee1905 -lmaputil -plugin_subdirs ?= $(wildcard plugins/*) +plugin_subdirs ?= $(wildcard plugins/*/*) plugin_sofile = $(wildcard $(d)/*.so) plugin_files = $(foreach d, $(plugin_subdirs), $(plugin_sofile)) HOOKS = pre-commit diff --git a/src/cntlr.c b/src/cntlr.c index a0482e95e072c800c5e94596cbacab8e6d3bba0d..49f2cf41d01fec51fcf977a843dc1e08aace10a9 100644 --- a/src/cntlr.c +++ b/src/cntlr.c @@ -44,6 +44,7 @@ #include "cntlr_ubus.h" #include "cntlr_map.h" #include "cntlr_cmdu.h" +#include "steer_module.h" #define map_plugin "ieee1905.map" @@ -352,9 +353,12 @@ static int forall_node_update_neighbors(struct controller *c) } #endif -void cntlr_add_sta_steer_attempt(struct controller *c, uint8_t *sta_mac, - uint8_t *src_bssid, uint8_t *dst_bssid, - enum steer_method method, enum steer_trigger trigger) +void cntlr_update_sta_steer_counters(struct controller *c, + uint8_t *sta_mac, + uint8_t *src_bssid, + uint8_t *dst_bssid, + enum steer_method method, + enum steer_trigger trigger) { trace("%s:--->\n", __func__); @@ -402,103 +406,118 @@ void cntlr_add_sta_steer_attempt(struct controller *c, uint8_t *sta_mac, s->num_steer_attempts += 1; } -#define STEER_ATTEMPT_MIN_INTV 30000 /* ms */ +static int cntlr_steer_sta(struct controller *c, struct sta *s, + struct steer_target *to, uint32_t mode, + uint32_t reason) +{ + struct cmdu_buff *cmdu; + + + if (!to || hwaddr_is_zero(to->bssid)) { + dbg("%s: steer verdict = ok, but target AP = NULL!\n", __func__); + return 0; + } + + if (!memcmp(to->bssid, s->bssid, 6)) { + s->stats.no_candidate_cnt++; + dbg("%s: " MACFMT " connected to best AP! No steer needed.\n", + __func__); + return 0; + } + + dbg("%s: Try to steer " MACFMT " from " MACFMT " to " MACFMT "\n", + __func__, MAC2STR(s->macaddr), MAC2STR(s->bssid), MAC2STR(to->bssid)); + + UNUSED(mode); //TODO + UNUSED(reason); + + cmdu = cntlr_gen_client_steer_request(c, s->fh->agent->alid, + s->bssid, 0, + 1, (uint8_t (*)[6])s->macaddr, + 1, (uint8_t (*)[6])to->bssid, + 1); /* mandate */ + if (!cmdu) { + warn("%s: Failed to generate cmdu for steering sta!\n", __func__); + return -1; + } + + cntlr_update_sta_steer_counters(c, s->macaddr, s->bssid, to->bssid, + METHOD_BTM_REQ, TRIGGER_LINK_QUALITY); + send_cmdu(c, cmdu); + cmdu_free(cmdu); + + return 0; +} + static void cntlr_bcn_metrics_parse(struct uloop_timeout *t) { trace("%s:--->\n", __func__); struct sta *s = container_of(t, struct sta, bcn_metrics_timer); - struct bcn_metrics *best = NULL, *b = NULL; struct node *n = s->fh->agent; struct controller *c = n->cntlr; struct netif_iface *bss = NULL; + struct bcn_metrics *b, *tmp; + struct steer_sta candidate = { + .s = s, + .nbrlist = NULL, + .bcnlist = &s->bcnlist, + .best = NULL, + }; + int ret; - dbg("|%s:%d| for "MACFMT" attached to bssid " MACFMT "\n", - __func__, __LINE__, - MAC2STR(s->macaddr), MAC2STR(s->bssid)); - dbg("|%s:%d| node = "MACFMT"\n", __func__, __LINE__, - MAC2STR(n->alid)); - //for (i = 0; i < s->num_bcn_metrics; i++) { - list_for_each_entry(b, &s->bcnlist, list) { - dbg("|%s:%d| bcn "MACFMT" \n", __func__, __LINE__, - MAC2STR(b->bssid)); + dbg("%s: STA " MACFMT" connected to " MACFMT " in Node " MACFMT"\n", + __func__, MAC2STR(s->macaddr), MAC2STR(s->bssid), MAC2STR(n->alid)); + + list_for_each_entry_safe(b, tmp, &s->bcnlist, list) { + dbg("bcn-report from " MACFMT "\n", MAC2STR(b->bssid)); + /* Skip entry not in our network */ bss = cntlr_iterate_fbss(c, b->bssid); if (!bss) { - /* Skip - bss not in the mesh */ - dbg("|%s:%d| bssid "MACFMT" is not in the mesh\n", - __func__, __LINE__, MAC2STR(b->bssid)); - continue; - } - - if (!best) { - best = b; - continue; - } - - dbg("|%s:%d| best rcpi %u this rcpi %u\n", __func__, __LINE__, - best->rcpi, b->rcpi); - - if ((b->rcpi - best->rcpi) > 10) { - dbg("|%s:%d| new best bcn "MACFMT" with rcpi %d\n", - __func__, __LINE__, - MAC2STR(b->bssid), - b->rcpi); - best = b; + list_del(&b->list); + free(b); + dbg("Delete alien entry "MACFMT"\n", MAC2STR(b->bssid)); } } - if (!best) { - dbg("|%s:%d| no best bcn!\n", __func__, __LINE__); + if (!c->cfg.enable_sta_steer) return; - } - - if (!hwaddr_is_zero(best->bssid)) { - struct cmdu_buff *cmdu; - if (!c->cfg.enable_sta_steer) { - trace("|%s:%d| better bssid found, but will not steer "MACFMT",\ - because the 'enable_sta_steer' is not set!\n", - __func__, __LINE__, MAC2STR(s->macaddr)); - return; - } + /* check if sta should be steered? */ + ret = cntlr_maybe_steer_sta(c, &candidate); + if (ret) { + fprintf(stderr, "cntlr_maybe_steer_sta() ret = %d\n", ret); + return; + } - /* Do not steer too often */ + switch (candidate.verdict) { + case STEER_VERDICT_OK: if (!timestamp_expired(&s->stats.last_steer_time, STEER_ATTEMPT_MIN_INTV)) { - dbg("|%s:%d| previous steer request less than 30s ago\n", __func__, __LINE__); - return; - } - - if (!memcmp(best->bssid, s->bssid, 6)) { - /* No better bssid found - update stats */ - s->stats.no_candidate_cnt++; - dbg("|%s:%d| same bssid - do not steer!\n", - __func__, __LINE__); + dbg("%s: last steer attempt < %us ago; skip steering\n", + __func__, STEER_ATTEMPT_MIN_INTV / 1000); return; } - dbg("|%s:%d| new bssid, wow! try to steer sta " \ - MACFMT " from " MACFMT " to " MACFMT "\n", - __func__, __LINE__, - MAC2STR(s->macaddr), MAC2STR(s->bssid), MAC2STR(best->bssid)); - - cmdu = cntlr_gen_client_steer_request(c, s->fh->agent->alid, - s->bssid, 0, - 1, (uint8_t (*)[6])s->macaddr, - 1, (uint8_t (*)[6])best->bssid, - 1); /* mandate */ - if (cmdu) { - cntlr_add_sta_steer_attempt(c, s->macaddr, s->bssid, best->bssid, - METHOD_BTM_REQ, TRIGGER_LINK_QUALITY); - send_cmdu(c, cmdu); - cmdu_free(cmdu); - } + cntlr_steer_sta(c, s, candidate.best, candidate.mode, candidate.reason); + break; + case STEER_VERDICT_NOK: + return; + case STEER_VERDICT_MAYBE: + /* TODO: check next steer-control ? */ + break; + case STEER_VERDICT_EXCLUDE: + /* TODO: exclude this STA from subsequent steer attempts */ + break; + default: + break; } + dbg("%s exiting\n", __func__); } -static void cntlr_init_steer_stats(struct sta *s) +static void cntlr_init_sta_steer_counters(struct sta *s) { memset(&s->stats, 0, sizeof(struct steer_stats)); @@ -531,7 +550,7 @@ struct sta *cntlr_add_sta(struct controller *c, uint8_t *macaddr) memcpy(s->macaddr, macaddr, 6); list_add(&s->list, &c->stalist); s->bcn_metrics_timer.cb = cntlr_bcn_metrics_parse; - cntlr_init_steer_stats(s); + cntlr_init_sta_steer_counters(s); return s; } @@ -2441,6 +2460,12 @@ int start_controller(void) controller_subscribe_for_cmdus(c); + /* steer-control */ + INIT_LIST_HEAD(&c->sclist); + cntlr_load_steer_modules(c); + if (!list_empty(&c->sclist)) + cntlr_assign_steer_module_default(c); + uloop_run(); out_exit: ubus_unregister_event_handler(ctx, &c->evh); diff --git a/src/cntlr.h b/src/cntlr.h index 911ca1eb4fff5766d2d82fffb76435adfedd2622..dd11e35faa13f1c22791796cf7ad43bc29395640 100644 --- a/src/cntlr.h +++ b/src/cntlr.h @@ -48,7 +48,6 @@ struct bcn_metrics { uint8_t rcpi; uint8_t rsni; uint8_t bssid[6]; - struct list_head list; }; @@ -387,6 +386,9 @@ struct controller { mapmodule_cmdu_mask_t cmdu_mask; void *subscriber; bool subscribed; + + struct list_head sclist; /* steer-control module list */ + struct steer_control *sctrl; /* active steer-control module */ }; struct sta_channel_report { @@ -435,7 +437,7 @@ struct netif_radio *cntlr_node_add_radio(struct controller *c, struct node *n, struct netif_iface *cntlr_radio_add_interface(struct controller *c, struct netif_radio *r, uint8_t *hwaddr); struct netif_iface *cntlr_iterate_fbss(struct controller *c, uint8_t *mac); -void cntlr_add_sta_steer_attempt(struct controller *c, uint8_t *sta_mac, +void cntlr_update_sta_steer_counters(struct controller *c, uint8_t *sta_mac, uint8_t *src_bssid, uint8_t *dst_bssid, enum steer_method method, enum steer_trigger trigger); struct sta *cntlr_add_sta(struct controller *c, uint8_t *macaddr); @@ -461,4 +463,7 @@ bool cntlr_node_opclass_expired(struct node *node); int cntlr_sync_dyn_controller_config(struct controller *c, uint8_t *agent); +void cntlr_load_steer_modules(struct controller *c); +void cntlr_unload_steer_modules(struct controller *c); + #endif /* CNTLR_H */ diff --git a/src/cntlr_ubus.c b/src/cntlr_ubus.c index a3357742c974a12555cc24af7a6b68eedbc22f8b..de21dc677be252236476c9643d0e91087982163e 100644 --- a/src/cntlr_ubus.c +++ b/src/cntlr_ubus.c @@ -1067,25 +1067,31 @@ fail_cmdu: return err; } -static void cntlr_register_steer_attempt(struct controller *c, - uint8_t *bssid, uint32_t sta_nr, uint8_t sta_id[][6], - uint32_t bssid_nr, uint8_t target_bbsid[][6]) +static void cntlr_update_sta_steering_stats(struct controller *c, uint8_t *bssid, + uint32_t sta_nr, uint8_t sta_id[][6], + uint32_t bssid_nr, uint8_t target_bbsid[][6]) { int i; /* Number of STAs and BSSIDs are equal, map STA to BSSID */ if (sta_nr == bssid_nr) { for (i = 0; i < sta_nr; i++) { - cntlr_add_sta_steer_attempt(c, sta_id[i], - bssid, target_bbsid[i], - METHOD_BTM_REQ, TRIGGER_UNKNOWN); + cntlr_update_sta_steer_counters(c, + sta_id[i], + bssid, + target_bbsid[i], + METHOD_BTM_REQ, + TRIGGER_UNKNOWN); } /* Multiple STAs and single BSSID - one attempt per STA */ } else if (sta_nr > 0 && bssid_nr == 1) { for (i = 0; i < sta_nr; i++) { - cntlr_add_sta_steer_attempt(c, sta_id[i], - bssid, target_bbsid[0], - METHOD_BTM_REQ, TRIGGER_UNKNOWN); + cntlr_update_sta_steer_counters(c, + sta_id[i], + bssid, + target_bbsid[0], + METHOD_BTM_REQ, + TRIGGER_UNKNOWN); } } /* No STA provided, request applies to ALL associated STAs */ @@ -1094,18 +1100,24 @@ static void cntlr_register_steer_attempt(struct controller *c, list_for_each_entry(s, &c->stalist, list) { if (!memcmp(s->bssid, bssid, 6)) { - cntlr_add_sta_steer_attempt(c, s->macaddr, - bssid, target_bbsid[0], - METHOD_BTM_REQ, TRIGGER_UNKNOWN); + cntlr_update_sta_steer_counters(c, + s->macaddr, + bssid, + target_bbsid[0], + METHOD_BTM_REQ, + TRIGGER_UNKNOWN); } } } /* No BSSID specified for the STAs - automatic best */ else if (sta_nr > 0 && bssid_nr == 0) { for (i = 0; i < sta_nr; i++) { - cntlr_add_sta_steer_attempt(c, sta_id[i], - bssid, NULL, - METHOD_BTM_REQ, TRIGGER_UNKNOWN); + cntlr_update_sta_steer_counters(c, + sta_id[i], + bssid, + NULL, + METHOD_BTM_REQ, + TRIGGER_UNKNOWN); } } } @@ -1246,9 +1258,8 @@ static int cntlr_client_steering(struct ubus_context *ctx, struct ubus_object *o UNUSED(sta_multi_nr); #endif - /* Register steer attempt */ - cntlr_register_steer_attempt(c, bss_id, sta_nr, sta_id, - bssid_nr, target_bbsid); + cntlr_update_sta_steering_stats(c, bss_id, sta_nr, sta_id, + bssid_nr, target_bbsid); cmdu_put_eom(cmdu); send_cmdu(c, cmdu); diff --git a/src/config.c b/src/config.c index 1a4bf924573b3aa5c6b53c9e2d14cd211c0c40ab..d77f565a243f8e9151ae40894750f72ff1ce50c3 100644 --- a/src/config.c +++ b/src/config.c @@ -524,6 +524,7 @@ int cntlr_config_defaults(struct controller *cntlr, struct controller_config *cf INIT_LIST_HEAD(&cfg->radiolist); INIT_LIST_HEAD(&cfg->nodelist); INIT_LIST_HEAD(&cfg->aplist); + INIT_LIST_HEAD(&cfg->sclist); return 0; } @@ -539,6 +540,7 @@ static int cntlr_config_get_base(struct controller_config *c, CNTLR_ENABLE_BSTA_STEER, CNTLR_USE_BCN_METRICS, CNTLR_USE_USTA_METRICS, + CNTLR_STEER_MODULE, NUM_CNTLR_ATTRS }; const struct uci_parse_option opts[] = { @@ -550,6 +552,7 @@ static int cntlr_config_get_base(struct controller_config *c, { .name = "enable_bsta_steer", .type = UCI_TYPE_STRING }, { .name = "use_bcn_metrics", .type = UCI_TYPE_STRING }, { .name = "use_usta_metrics", .type = UCI_TYPE_STRING }, + { .name = "steer_module", .type = UCI_TYPE_LIST }, }; struct uci_option *tb[NUM_CNTLR_ATTRS]; @@ -606,6 +609,23 @@ static int cntlr_config_get_base(struct controller_config *c, c->use_usta_metrics = atoi(val) == 1 ? true : false; } + if (tb[CNTLR_STEER_MODULE]) { + struct uci_element *e; + struct steer_control_config *sc; + + fprintf(stderr, "Steer module: "); + uci_foreach_element(&tb[CNTLR_STEER_MODULE]->v.list, e) { + fprintf(stderr, "%s ", e->name); + + sc = calloc(1, sizeof(*sc)); + if (sc) { + strncpy(sc->name, e->name, 63); + list_add_tail(&sc->list, &c->sclist); + } + } + fprintf(stderr, "\n"); + } + return 0; } diff --git a/src/config.h b/src/config.h index 158cd02345c9a2c35e5e3417e03add77e58fc3bd..7a9997a1f5491831dc24d3bde14db37259279a47 100644 --- a/src/config.h +++ b/src/config.h @@ -137,6 +137,11 @@ struct radio_policy { struct list_head list; /* link to next policy */ }; +struct steer_control_config { + char name[64]; + struct list_head list; +}; + struct controller_config { bool enabled; bool has_registrar_5g; @@ -152,6 +157,7 @@ struct controller_config { struct list_head nodelist; struct list_head aplist; struct list_head radiolist; + struct list_head sclist; /* steer-control list */ }; struct controller; diff --git a/src/plugins/steer/rcpi/Makefile b/src/plugins/steer/rcpi/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..89e1e34f1fead6cd51b293c210181facc62a2975 --- /dev/null +++ b/src/plugins/steer/rcpi/Makefile @@ -0,0 +1,13 @@ +CC ?= gcc +CFLAGS += -I. -I../../.. -I../../../utils -O2 -Wall -g -Werror + +all: rcpi.so + +%.o: %.c + $(CC) $(CFLAGS) -fPIC -c $< -o $@ + +rcpi.so: rcpi.o + $(CC) $(CFLAGS) $(LDFLAGS) -shared -Wl,-soname,$@ -o $@ $^ + +clean: + rm -f *.o *.so* diff --git a/src/plugins/steer/rcpi/rcpi.c b/src/plugins/steer/rcpi/rcpi.c new file mode 100644 index 0000000000000000000000000000000000000000..907baa00d6368d45f692f19566966e17c15a480a --- /dev/null +++ b/src/plugins/steer/rcpi/rcpi.c @@ -0,0 +1,98 @@ +/* + * rcpi.c - RCPI based STA steering. + * + * Copyright (C) 2022 IOPSYS Software Solutions AB. All rights reserved. + * + * See LICENSE file for license related information. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include <easy/easy.h> + +#include "utils.h" +#include "debug.h" +#include "steer.h" + +struct rcpi_steer_control { + uint8_t diffsnr; + uint8_t low; + int8_t hysteresis; + uint8_t rcpi_threshold; + void *self; +}; + +static int rcpi_steer_init(void **priv); +static int rcpi_steer_exit(void *priv); + +int rcpi_steer(void *priv, struct steer_sta *s) +{ + struct rcpi_steer_control *p = (struct rcpi_steer_control *)priv; + struct steer_target *b; + + + fprintf(stderr, "%s: --------->\n", __func__); + UNUSED(p); //TODO + + s->verdict = STEER_VERDICT_UNDECIDED; + s->reason = 2; //TRIGGER_LINK_QUALITY + + if (list_empty(s->bcnlist)) + return 0; + + s->best = list_first_entry(s->bcnlist, struct steer_target, list); + list_for_each_entry(b, s->bcnlist, list) { + if ((b->rcpi - s->best->rcpi) > p->diffsnr) { + dbg("%s: new best bcn from "MACFMT" with rcpi %d\n", + __func__, MAC2STR(b->bssid), b->rcpi); + s->best = b; + } + } + + return 0; +} + +extern struct steer_control rcpi; +struct steer_control rcpi = { + .name = "rcpi", + .init = rcpi_steer_init, + .exit = rcpi_steer_exit, + .steer = rcpi_steer, +}; + + +static int rcpi_steer_init(void **priv) +{ + struct rcpi_steer_control *p; + + + p = calloc(1, sizeof(struct rcpi_steer_control)); + if (!p) + return -1; + + *priv = p; + p->self = &rcpi; + p->low = 90; + p->rcpi_threshold = 100; + p->hysteresis = 5; + p->diffsnr = 10; + + fprintf(stderr, "%s: ========================>\n", __func__); + return 0; +} + +static int rcpi_steer_exit(void *priv) +{ + struct rcpi_steer_control *p = (struct rcpi_steer_control *)priv; + + if (p) + free(p); + + fprintf(stderr, "%s: <========================\n", __func__); + return 0; +} + diff --git a/src/steer.h b/src/steer.h new file mode 100644 index 0000000000000000000000000000000000000000..2f99fa656ef0348a624918cc2ef75c81d00999e1 --- /dev/null +++ b/src/steer.h @@ -0,0 +1,84 @@ +/* + * steer.h - header for defining a new steering module + * + * Copyright (C) 2022 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + * See LICENSE file for license related information. + * + */ + +#ifndef STEER_H +#define STEER_H + +#include <stdint.h> +#include <libubox/list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum steer_verdict { + STEER_VERDICT_UNDECIDED, + STEER_VERDICT_OK, + STEER_VERDICT_MAYBE, + STEER_VERDICT_NOK, + STEER_VERDICT_EXCLUDE, +}; + +enum steer_reason { + STEER_REASON_UNDEFINED, + STEER_REASON_LOW_RCPI, + STEER_REASON_LOW_THPUT, + STEER_REASON_HIGH_PER, + STEER_REASON_OTHER, +}; + +typedef enum steer_verdict steer_verdict_t; + +struct steer_config { + uint8_t rcpi_threshold; + uint8_t rcpi_hysteresis; + uint8_t ch_utilization; +}; + +struct steer_target { + uint8_t opclass; + uint8_t channel; + uint8_t rcpi; + uint8_t rsni; + uint8_t bssid[6]; + struct list_head list; +}; + +struct steer_sta { + struct sta *s; + struct list_head *nbrlist; + struct list_head *bcnlist; + steer_verdict_t verdict; + struct steer_target *best; + uint32_t reason; + uint32_t mode; +}; + +struct steer_control { + char name[64]; + uint8_t rcpi_threshold; + uint8_t rcpi_hysteresis; + uint8_t cbinterval; + void *priv; + int (*init)(void **); + int (*exit)(void *); + int (*steer)(void *, struct steer_sta *candidate); + int (*config)(void *, struct steer_config *); + void *handle; + struct list_head list; +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* STEER_H */ diff --git a/src/steer_module.c b/src/steer_module.c new file mode 100644 index 0000000000000000000000000000000000000000..04b34ec05089871831a5818204305a1054869dd4 --- /dev/null +++ b/src/steer_module.c @@ -0,0 +1,224 @@ +/* + * steer_module.c - STA steering module + * + * Copyright (C) 2022 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + * See LICENSE file for license related information. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <dlfcn.h> +#include <sys/time.h> + +#include <json-c/json.h> +#include <libubox/blobmsg.h> +#include <libubox/blobmsg_json.h> +#include <libubox/uloop.h> +#include <libubox/ustream.h> +#include <libubox/utils.h> +#include <libubus.h> + +#include <easy/easy.h> + +#include <timer_impl.h> +#include <cmdu.h> +#include <1905_tlvs.h> +#include <map2.h> +#include <map_module.h> +#include <uci.h> + +#include "utils/debug.h" +#include "utils/utils.h" +#include "config.h" +#include "cntlr.h" +#include "steer_module.h" + +#define CNTLR_STEER_MODULE_PATH "/usr/lib/mapcontroller" + + +static int plugin_load(const char *path, const char *name, void **handle) +{ + char abspath[256] = {0}; + int flags = 0; + void *h; + + if (!handle || !name || !path) + return -1; + + flags |= RTLD_NOW | RTLD_GLOBAL; + snprintf(abspath, sizeof(abspath) - 1, "%s/%s", path, name); + h = dlopen(abspath, flags); + if (!h) { + fprintf(stderr, "%s: Error: %s\n", __func__, dlerror()); + return -1; + } + + *handle = h; + return 0; +} + +static int plugin_unload(void *handle) +{ + if (!handle) + return -1; + + return dlclose(handle); +} + +static int cntlr_unload_steer_module(struct steer_control *sc) +{ + int ret; + + ret = plugin_unload(sc->handle); + list_del(&sc->list); + + return !ret ? 0 : -1; +} + +int cntlr_load_steer_module(struct controller *priv, const char *name, + struct steer_control **sc) +{ + struct steer_control *p, *pp = NULL; + char fname[128] = {0}; + void *handle; + int ret; + + + snprintf(fname, 127, "%s.so", name); + ret = plugin_load(CNTLR_STEER_MODULE_PATH, fname, &handle); + if (ret) + return -1; + + pp = dlsym(handle, name); + if (!pp) { + fprintf(stderr, "Symbol '%s' not found\n", name); + return -1; + } + + p = calloc(1, sizeof(struct steer_control)); + if (!p) { + plugin_unload(handle); + return -1; + } + + memcpy(p, pp, sizeof(struct steer_control)); + p->handle = handle; + *sc = p; + + if (p->init) + p->init(&p->priv); + + fprintf(stderr, "Registered %s (priv = 0x%p)\n", name, p->priv); + return 0; +} + +static struct steer_control *cntlr_lookup_steer_module(struct controller *c, + const char *name) +{ + struct steer_control *sc; + + list_for_each_entry(sc, &c->sclist, list) { + if (!strncmp(sc->name, name, strlen(sc->name))) + return sc; + } + + return NULL; +} + +void cntlr_assign_steer_module(struct controller *c, const char *name) +{ + if (!name || name[0] == '\0') { + c->sctrl = NULL; + return; + } + + c->sctrl = cntlr_lookup_steer_module(c, name); +} + + +void cntlr_load_steer_modules(struct controller *c) +{ + struct steer_control_config *e; + + + list_for_each_entry(e, &c->cfg.sclist, list) { + struct steer_control *sc = NULL; + int ret = 0; + + if (cntlr_lookup_steer_module(c, e->name)) + continue; + + dbg("Loading steer module '%s'\n", e->name); + ret = cntlr_load_steer_module(c, e->name, &sc); + if (!ret) + list_add_tail(&sc->list, &c->sclist); + } +} + +void cntlr_unload_steer_modules(struct controller *c) +{ + struct steer_control *p, *tmp; + + list_for_each_entry_safe(p, tmp, &c->sclist, list) { + if (p->exit) + p->exit(p->priv); + + list_del(&p->list); + plugin_unload(p->handle); + free(p); + } +} + +int cntlr_register_steer_module(struct controller *c, const char *name) +{ + struct steer_control *sc; + int ret; + + + if (!name || name[0] == '\0') + return -1; + + if (cntlr_lookup_steer_module(c, name)) { + info("Steer module '%s' already registered\n", name); + return 0; + } + + ret = cntlr_load_steer_module(c, name, &sc); + if (!ret) { + list_add_tail(&sc->list, &c->sclist); + return 0; + } + + return -1; +} + +int cntlr_unregister_steer_module(struct controller *c, char *name) +{ + struct steer_control *sc; + + + if (!name || name[0] == '\0') + return -1; + + sc = cntlr_lookup_steer_module(c, name); + if (!sc) + return -1; + + return cntlr_unload_steer_module(sc); +} + +int cntlr_maybe_steer_sta(struct controller *c, struct steer_sta *s) +{ + struct steer_control *sc = cntlr_get_steer_control(c); + + if (sc && sc->steer) + return sc->steer(sc->priv, s); + + return 0; +} diff --git a/src/steer_module.h b/src/steer_module.h new file mode 100644 index 0000000000000000000000000000000000000000..ef0be16ea0a5dab41619875939e256750dd81416 --- /dev/null +++ b/src/steer_module.h @@ -0,0 +1,49 @@ +/* + * steer_module.h - header for steering related stuff + * + * Copyright (C) 2022 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + * See LICENSE file for license related information. + * + */ + +#ifndef STEER_MODULE_H +#define STEER_MODULE_H + +#include <stdint.h> +#include <libubox/list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "steer.h" + +#define STEER_ATTEMPT_MIN_INTV 30000 /* ms */ + +static inline struct steer_control *cntlr_get_steer_control(struct controller *c) +{ + return c->sctrl; +} + +static inline void cntlr_assign_steer_module_default(struct controller *c) +{ + c->sctrl = !list_empty(&c->sclist) ? + list_first_entry(&c->sclist, struct steer_control, list) : + NULL; +} + +void cntlr_assign_steer_module(struct controller *c, const char *name); + +int cntlr_register_steer_module(struct controller *c, const char *name); +int cntlr_unregister_steer_module(struct controller *c, char *name); +int cntlr_maybe_steer_sta(struct controller *c, struct steer_sta *s); + + +#ifdef __cplusplus +} +#endif + +#endif /* STEER_MODULE_H */