return !node->max_assoc || node->n_assoc < node->max_assoc;
}
+static bool
+over_min_signal(struct sta_info *si)
+{
+ if (config.min_snr && si->signal < usteer_snr_to_signal(si->node, config.min_snr))
+ return false;
+
+ if (config.roam_trigger_snr && si->signal < usteer_snr_to_signal(si->node, config.roam_trigger_snr))
+ return false;
+
+ return true;
+}
+
static uint32_t
is_better_candidate(struct sta_info *si_cur, struct sta_info *si_new)
{
if (!below_max_assoc(si_new))
return 0;
+ if (!over_min_signal(si_new))
+ return 0;
+
if (below_assoc_threshold(si_cur, si_new) &&
!below_assoc_threshold(si_new, si_cur))
reasons |= (1 << UEV_SELECT_REASON_NUM_ASSOC);
}
static struct sta_info *
-find_better_candidate(struct sta_info *si_ref, struct uevent *ev)
+find_better_candidate(struct sta_info *si_ref, struct uevent *ev, uint32_t required_criteria)
{
struct sta_info *si;
struct sta *sta = si_ref->sta;
if (!reasons)
continue;
- if (is_better_candidate(si, si_ref))
+ if (!(reasons & required_criteria))
continue;
if (ev) {
return NULL;
}
-static int
-snr_to_signal(struct usteer_node *node, int snr)
+int
+usteer_snr_to_signal(struct usteer_node *node, int snr)
{
int noise = -95;
if (type == EVENT_TYPE_AUTH)
goto out;
- if (type == EVENT_TYPE_ASSOC && !config.assoc_steering)
- goto out;
+ if (type == EVENT_TYPE_ASSOC) {
+ /* Check if assoc request has lower signal than min_signal.
+ * If this is the case, block assoc even when assoc steering is enabled.
+ *
+ * Otherwise, the client potentially ends up in a assoc - kick loop.
+ */
+ if (config.min_snr && si->signal < usteer_snr_to_signal(si->node, config.min_snr)) {
+ ev.reason = UEV_REASON_LOW_SIGNAL;
+ ev.threshold.cur = si->signal;
+ ev.threshold.ref = usteer_snr_to_signal(si->node, config.min_snr);
+ ret = false;
+ goto out;
+ } else if (!config.assoc_steering) {
+ goto out;
+ }
+ }
- min_signal = snr_to_signal(si->node, config.min_connect_snr);
+ min_signal = usteer_snr_to_signal(si->node, config.min_connect_snr);
if (si->signal < min_signal) {
ev.reason = UEV_REASON_LOW_SIGNAL;
ev.threshold.cur = si->signal;
goto out;
}
- if (!find_better_candidate(si, &ev))
+ if (!find_better_candidate(si, &ev, UEV_SELECT_REASON_ALL))
goto out;
ev.reason = UEV_REASON_BETTER_CANDIDATE;
};
int min_signal;
- min_signal = snr_to_signal(si->node, config.roam_trigger_snr);
+ min_signal = usteer_snr_to_signal(si->node, config.roam_trigger_snr);
switch (si->roam_state) {
case ROAM_TRIGGER_SCAN:
if (current_time - si->roam_event < config.roam_scan_interval)
break;
- if (find_better_candidate(si, &ev) ||
+ if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL)) ||
si->roam_scan_done > si->roam_event) {
usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev);
break;
break;
case ROAM_TRIGGER_IDLE:
- if (find_better_candidate(si, &ev)) {
+ if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL))) {
usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev);
break;
}
break;
}
- if (find_better_candidate(si, &ev))
+ if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL)))
usteer_roam_set_state(si, ROAM_TRIGGER_WAIT_KICK, &ev);
break;
return;
usteer_update_time();
- min_signal = snr_to_signal(&ln->node, min_signal);
+ min_signal = usteer_snr_to_signal(&ln->node, min_signal);
list_for_each_entry(si, &ln->node.sta_info, node_list) {
if (si->connected != STA_CONNECTED || si->signal >= min_signal ||
if (!config.min_snr)
return;
- min_signal = snr_to_signal(&ln->node, config.min_snr);
+ min_signal = usteer_snr_to_signal(&ln->node, config.min_snr);
ev.threshold.ref = min_signal;
list_for_each_entry(si, &ln->node.sta_info, node_list) {
if (is_more_kickable(kick1, si))
kick1 = si;
- tmp = find_better_candidate(si, NULL);
+ tmp = find_better_candidate(si, NULL, (1 << UEV_SELECT_REASON_LOAD));
if (!tmp)
continue;