17bool mac_eq(
const uint8_t *a,
const uint8_t *b)
19 return memcmp(a, b, 6) == 0;
23int best_other(
const uint8_t *current,
const pc_roam_neighbor *nb, uint8_t n)
26 for (uint8_t i = 0; i < n; i++)
28 if (mac_eq(nb[i].bssid, current))
32 if (best < 0 || nb[i].rssi_dbm > nb[best].rssi_dbm)
41int find_bssid(
const uint8_t *target,
const pc_roam_neighbor *nb, uint8_t n)
43 for (uint8_t i = 0; i < n; i++)
45 if (mac_eq(nb[i].bssid, target))
53void pick(pc_roam_decision *out,
const pc_roam_neighbor *nb,
int idx, pc_roam_reason reason)
56 memcpy(out->target_bssid, nb[idx].bssid, 6);
57 out->target_channel = nb[idx].channel;
62void pc_roam_decide(
const uint8_t current_bssid[6], int8_t current_rssi_dbm,
const pc_roam_neighbor *neighbors,
63 uint8_t n,
const pc_roam_btm *btm,
const pc_roam_policy *policy, pc_roam_decision *out)
70 memset(out->target_bssid, 0, 6);
71 out->target_channel = 0;
72 out->reason = PC_ROAM_NONE;
73 if (!current_bssid || (n && !neighbors))
79 int8_t threshold = policy ? policy->roam_rssi_threshold_dbm : (int8_t)-75;
80 uint8_t hysteresis = policy ? policy->hysteresis_db : (uint8_t)8;
82 int best = best_other(current_bssid, neighbors, n);
86 if (btm && btm->present && btm->disassoc_imminent)
89 if (btm->has_preferred)
91 target = find_bssid(btm->preferred_bssid, neighbors, n);
99 pick(out, neighbors, target, PC_ROAM_BTM_IMMINENT);
106 if (btm && btm->present && btm->has_preferred)
108 int target = find_bssid(btm->preferred_bssid, neighbors, n);
109 if (target >= 0 && neighbors[target].rssi_dbm >= current_rssi_dbm)
111 pick(out, neighbors, target, PC_ROAM_BTM_SUGGESTED);
118 if (best >= 0 && current_rssi_dbm <= threshold &&
119 (
int)neighbors[best].rssi_dbm >= (
int)current_rssi_dbm + (
int)hysteresis)
121 pick(out, neighbors, best, PC_ROAM_LOW_RSSI);
128uint8_t pc_roam_parse_neighbor_report(
const uint8_t *elems,
size_t len, pc_roam_neighbor *out, uint8_t max)
136 const uint8_t NR_BODY_MIN = 13;
139 while (off + 2 <= len && count < max)
141 uint8_t
id = elems[off];
142 uint8_t elen = elems[off + 1];
143 if (off + 2 + elen > len)
147 if (
id == PC_ROAM_NR_ELEM_ID && elen >= NR_BODY_MIN)
149 const uint8_t *b = elems + off + 2;
150 memcpy(out[count].bssid, b, 6);
151 out[count].channel = b[11];
152 out[count].rssi_dbm = PC_ROAM_RSSI_UNKNOWN;
155 off += (size_t)2 + elen;
160bool pc_roam_parse_btm_request(
const uint8_t *frame,
size_t len, pc_roam_btm *out)
166 memset(out, 0,
sizeof(*out));
168 if (!frame || len < 7 || frame[0] != PC_ROAM_WNM_CATEGORY || frame[1] != PC_ROAM_BTM_REQ_ACTION)
172 uint8_t mode = frame[3];
174 out->disassoc_imminent = (mode & PC_ROAM_BTM_DISASSOC) != 0;
178 if (mode & PC_ROAM_BTM_TERM_INCL)
182 if (mode & PC_ROAM_BTM_ESS_DISASSOC)
188 off += (size_t)1 + frame[off];
191 if ((mode & PC_ROAM_BTM_PREF_LIST) && off + 2 + 6 <= len && frame[off] == PC_ROAM_NR_ELEM_ID &&
192 frame[off + 1] >= 13)
194 memcpy(out->preferred_bssid, frame + off + 2, 6);
195 out->has_preferred =
true;
Wi-Fi roaming decision layer (PC_ENABLE_ROAMING) - the policy that picks a roam target.