Skip to content

Commit d4f2a36

Browse files
src/cyw43_ctrl: Add roaming and interference-mitigation config via ioctl helpers
Expose the roaming and interference-mitigation ioctls needed to tune link stability, implemented entirely in terms of cyw43_ioctl() rather than adding dedicated low-level (cyw43_ll_*) functions for each setting, per maintainer feedback on PR #146. Only the two generic u32 helpers cost binary space; the feature-specific wrappers are static inline and the named-iovar helpers are the only additional functions. Roaming is enabled by default with no way to configure it. When a STA's RSSI drops below the (hidden) roam trigger the firmware starts background roam scans, which causes dropped packets and severe throughput degradation on a point-to-point link (see #111 and raspberrypi/pico-sdk#2835). Add public helpers: - cyw43_ioctl_set_u32(self, cmd, val, iface) - cyw43_ioctl_get_u32(self, cmd, *out_val, iface) Wrap cyw43_ioctl() for the common case of a 4-byte ioctl value. Argument order matches cyw43_ioctl() (iface last). Add CYW43_IOCTL_xxx constants, encoded as (WLC_cmd << 1) | is_set to match the existing CYW43_IOCTL_ defines: - GET/SET_ROAM_TRIGGER, GET/SET_ROAM_DELTA, GET/SET_ROAM_SCAN_PERIOD - GET/SET_INTERFERENCE_MODE Add the configuration API: - cyw43_wifi_set_roam_params() / cyw43_wifi_get_roam_params() static inline, built on the u32 ioctl helpers. - cyw43_wifi_set_interference_mode() / cyw43_wifi_get_interference_mode() static inline, built on the u32 ioctl helpers, with CYW43_IFMODE_* mode constants. - cyw43_wifi_set_roam_enabled() / cyw43_wifi_get_roam_enabled() toggle/read the "roam_off" iovar via WLC_SET_VAR / WLC_GET_VAR. The default roaming behaviour is left unchanged.
1 parent 055d642 commit d4f2a36

3 files changed

Lines changed: 238 additions & 0 deletions

File tree

src/cyw43.h

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,20 @@
104104
#define CYW43_LINK_BADAUTH (-3) ///< Authenticatation failure
105105
//!\}
106106

107+
/*!
108+
* \name Interference mitigation mode
109+
* \anchor CYW43_IFMODE_
110+
* \see cyw43_wifi_set_interference_mode() to set the mitigation mode
111+
* \see cyw43_wifi_get_interference_mode() to get the mitigation mode
112+
*/
113+
//!\{
114+
#define CYW43_IFMODE_NONE (0) ///< None/Disabled
115+
#define CYW43_IFMODE_NONWLAN (1) ///< Non-WLAN
116+
#define CYW43_IFMODE_WLANMANUAL (2) ///< Adjacent channel interference (ACI) mode
117+
#define CYW43_IFMODE_AUTO (3) ///< Automatic as determined by driver (ACI)
118+
#define CYW43_IFMODE_AUTONOISE (4) ///< Automatic as determined by driver with noise reduction (ACI)
119+
//!\}
120+
107121
typedef struct _cyw43_t {
108122
cyw43_ll_t cyw43_ll;
109123

@@ -186,6 +200,34 @@ void cyw43_deinit(cyw43_t *self);
186200
*/
187201
int cyw43_ioctl(cyw43_t *self, uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface);
188202

203+
/*!
204+
* \brief Set an ioctl whose value is a single unsigned 32-bit integer
205+
*
206+
* Convenience wrapper around \ref cyw43_ioctl for the common case of an ioctl
207+
* that takes a 4-byte value, e.g. the various \c CYW43_IOCTL_SET_xxx commands.
208+
*
209+
* \param self the driver state object. This should always be \c &cyw43_state
210+
* \param cmd the ioctl command to send, e.g. one of the \c CYW43_IOCTL_SET_xxx defines
211+
* \param val the value to set
212+
* \param iface the interface to use, either \ref CYW43_ITF_STA or \ref CYW43_ITF_AP
213+
* \return 0 on success, negative error code on failure
214+
*/
215+
int cyw43_ioctl_set_u32(cyw43_t *self, uint32_t cmd, uint32_t val, uint32_t iface);
216+
217+
/*!
218+
* \brief Get an ioctl whose value is a single unsigned 32-bit integer
219+
*
220+
* Convenience wrapper around \ref cyw43_ioctl for the common case of an ioctl
221+
* that returns a 4-byte value, e.g. the various \c CYW43_IOCTL_GET_xxx commands.
222+
*
223+
* \param self the driver state object. This should always be \c &cyw43_state
224+
* \param cmd the ioctl command to send, e.g. one of the \c CYW43_IOCTL_GET_xxx defines
225+
* \param out_val Output: the value that was read. May be NULL.
226+
* \param iface the interface to use, either \ref CYW43_ITF_STA or \ref CYW43_ITF_AP
227+
* \return 0 on success, negative error code on failure
228+
*/
229+
int cyw43_ioctl_get_u32(cyw43_t *self, uint32_t cmd, uint32_t *out_val, uint32_t iface);
230+
189231
/*!
190232
* \brief Send a raw ethernet packet
191233
*
@@ -200,6 +242,35 @@ int cyw43_ioctl(cyw43_t *self, uint32_t cmd, size_t len, uint8_t *buf, uint32_t
200242
*/
201243
int cyw43_send_ethernet(cyw43_t *self, int itf, size_t len, const void *buf, bool is_pbuf);
202244

245+
/*!
246+
* \brief Set the WiFi interference mitigation mode
247+
*
248+
* Controls how aggressively the firmware attempts to mitigate interference
249+
* on the 2.4GHz band. Higher modes reduce interference at the cost of
250+
* RX sensitivity, which can be detrimental to long range operation.
251+
*
252+
* \note This setting applies globally to the radio and affects both the
253+
* STA and AP interfaces in apsta mode.
254+
*
255+
* \param self The driver state object, always \c &cyw43_state
256+
* \param mode Interference mitigation mode, one of \ref CYW43_IFMODE_
257+
* \return 0 on success, negative error code on failure
258+
*/
259+
static inline int cyw43_wifi_set_interference_mode(cyw43_t *self, uint32_t mode) {
260+
return cyw43_ioctl_set_u32(self, CYW43_IOCTL_SET_INTERFERENCE_MODE, mode, CYW43_ITF_STA);
261+
}
262+
263+
/*!
264+
* \brief Get the current WiFi interference mitigation mode
265+
*
266+
* \param self The driver state object, always \c &cyw43_state
267+
* \param mode Output: current interference mitigation mode, one of \ref CYW43_IFMODE_
268+
* \return 0 on success, negative error code on failure
269+
*/
270+
static inline int cyw43_wifi_get_interference_mode(cyw43_t *self, uint32_t *mode) {
271+
return cyw43_ioctl_get_u32(self, CYW43_IOCTL_GET_INTERFERENCE_MODE, mode, CYW43_ITF_STA);
272+
}
273+
203274
/*!
204275
* \brief Set the wifi power management mode
205276
*
@@ -360,6 +431,109 @@ int cyw43_wifi_join(cyw43_t *self, size_t ssid_len, const uint8_t *ssid, size_t
360431
*/
361432
int cyw43_wifi_leave(cyw43_t *self, int itf);
362433

434+
/*!
435+
* \brief Enable or disable WiFi roaming on the STA interface
436+
*
437+
* When disabled, the firmware will remain associated to its current AP
438+
* regardless of signal strength. When enabled, roaming behaviour is
439+
* governed by the parameters set in \c cyw43_wifi_set_roam_params.
440+
*
441+
* Roaming is enabled by default.
442+
*
443+
* \param self The driver state object, always \c &cyw43_state
444+
* \param enabled true to enable roaming, false to disable
445+
* \return 0 on success, negative error code on failure
446+
*/
447+
int cyw43_wifi_set_roam_enabled(cyw43_t *self, bool enabled);
448+
449+
/*!
450+
* \brief Query whether WiFi roaming is enabled on the STA interface
451+
*
452+
* Reads back the current state of the \c roam_off iovar.
453+
*
454+
* \param self The driver state object, always \c &cyw43_state
455+
* \param enabled Output: true if roaming is enabled, false if disabled
456+
* \return 0 on success, negative error code on failure
457+
*/
458+
int cyw43_wifi_get_roam_enabled(cyw43_t *self, bool *enabled);
459+
460+
/*!
461+
* \brief Configure WiFi roaming parameters for the STA interface
462+
*
463+
* Controls when the firmware will scan for and switch to a better AP.
464+
* Roaming is a STA-mode only feature - these parameters have no effect
465+
* when called on the AP interface.
466+
*
467+
* The firmware begins scanning for alternative APs when the current AP's
468+
* RSSI drops below \p trigger_dbm. A candidate AP must have an RSSI at
469+
* least \p candidate_delta_db higher than the current AP before the
470+
* firmware will roam to it. This prevents thrashing between APs of
471+
* similar signal strength.
472+
*
473+
* \param self The driver state object, always \c &cyw43_state
474+
* \param trigger_dbm RSSI threshold in dBm below which roam scanning begins.
475+
* Typical value: -75. Must be negative.
476+
* \param candidate_delta_db Minimum RSSI improvement in dB a candidate AP
477+
* must offer over the current AP to trigger a roam.
478+
* Typical value: 10.
479+
* \param scan_period_ms How often in milliseconds the firmware scans for better
480+
* APs while below the trigger threshold.
481+
* \return 0 on success, negative error code on failure
482+
*/
483+
static inline int cyw43_wifi_set_roam_params(cyw43_t *self, int trigger_dbm, int candidate_delta_db, int scan_period_ms) {
484+
int ret = cyw43_ioctl_set_u32(self, CYW43_IOCTL_SET_ROAM_TRIGGER, (uint32_t)trigger_dbm, CYW43_ITF_STA);
485+
if (ret) {
486+
return ret;
487+
}
488+
ret = cyw43_ioctl_set_u32(self, CYW43_IOCTL_SET_ROAM_DELTA, (uint32_t)candidate_delta_db, CYW43_ITF_STA);
489+
if (ret) {
490+
return ret;
491+
}
492+
return cyw43_ioctl_set_u32(self, CYW43_IOCTL_SET_ROAM_SCAN_PERIOD, (uint32_t)scan_period_ms, CYW43_ITF_STA);
493+
}
494+
495+
/*!
496+
* \brief Retrieve the current WiFi roaming parameters for the STA interface
497+
*
498+
* Reads back the roaming parameters that are currently set.
499+
* Any pointer may be NULL if that parameter is not required.
500+
*
501+
* \param self The driver state object, always \c &cyw43_state
502+
* \param trigger_dbm Output: RSSI threshold in dBm below which roam
503+
* scanning begins. Will be a negative value.
504+
* \param candidate_delta_db Output: Minimum RSSI improvement in dB a candidate
505+
* AP must offer over the current AP to trigger a roam.
506+
* \param scan_period_ms Output: How often in milliseconds the firmware scans
507+
* for better APs while below the trigger threshold.
508+
* \return 0 on success, negative error code on failure
509+
*/
510+
static inline int cyw43_wifi_get_roam_params(cyw43_t *self, int *trigger_dbm, int *candidate_delta_db, int *scan_period_ms) {
511+
uint32_t val;
512+
int ret;
513+
if (trigger_dbm != NULL) {
514+
ret = cyw43_ioctl_get_u32(self, CYW43_IOCTL_GET_ROAM_TRIGGER, &val, CYW43_ITF_STA);
515+
if (ret) {
516+
return ret;
517+
}
518+
*trigger_dbm = (int)(int32_t)val;
519+
}
520+
if (candidate_delta_db != NULL) {
521+
ret = cyw43_ioctl_get_u32(self, CYW43_IOCTL_GET_ROAM_DELTA, &val, CYW43_ITF_STA);
522+
if (ret) {
523+
return ret;
524+
}
525+
*candidate_delta_db = (int)(int32_t)val;
526+
}
527+
if (scan_period_ms != NULL) {
528+
ret = cyw43_ioctl_get_u32(self, CYW43_IOCTL_GET_ROAM_SCAN_PERIOD, &val, CYW43_ITF_STA);
529+
if (ret) {
530+
return ret;
531+
}
532+
*scan_period_ms = (int)(int32_t)val;
533+
}
534+
return 0;
535+
}
536+
363537
/*!
364538
* \brief Get the signal strength (RSSI) of the wifi network
365539
*

src/cyw43_ctrl.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,22 @@ int cyw43_ioctl(cyw43_t *self, uint32_t cmd, size_t len, uint8_t *buf, uint32_t
455455
return ret;
456456
}
457457

458+
int cyw43_ioctl_set_u32(cyw43_t *self, uint32_t cmd, uint32_t val, uint32_t iface) {
459+
// The wire format is little-endian; RP2 hosts are little-endian so the
460+
// value can be passed straight through (matching cyw43_wifi_get_rssi()).
461+
uint32_t buf = val;
462+
return cyw43_ioctl(self, cmd, sizeof(buf), (uint8_t *)&buf, iface);
463+
}
464+
465+
int cyw43_ioctl_get_u32(cyw43_t *self, uint32_t cmd, uint32_t *out_val, uint32_t iface) {
466+
uint32_t buf = 0;
467+
int ret = cyw43_ioctl(self, cmd, sizeof(buf), (uint8_t *)&buf, iface);
468+
if (ret == 0 && out_val != NULL) {
469+
*out_val = buf;
470+
}
471+
return ret;
472+
}
473+
458474
int cyw43_send_ethernet(cyw43_t *self, int itf, size_t len, const void *buf, bool is_pbuf) {
459475
CYW43_THREAD_ENTER;
460476
int ret = cyw43_ensure_up(self);
@@ -668,6 +684,46 @@ int cyw43_wifi_leave(cyw43_t *self, int itf) {
668684
return cyw43_ioctl(self, CYW43_IOCTL_SET_DISASSOC, 0, NULL, itf);
669685
}
670686

687+
int cyw43_wifi_set_roam_enabled(cyw43_t *self, bool enabled) {
688+
// "roam_off" is a string-named iovar rather than a numbered ioctl, so it is
689+
// sent via the WLC_SET_VAR ioctl with a "<name>\0<le32 value>" payload.
690+
static const char var[] = "roam_off";
691+
const size_t name_len = sizeof(var); // includes the NUL terminator
692+
uint8_t buf[sizeof(var) + 4];
693+
memcpy(buf, var, name_len);
694+
// roam_off == 1 disables roaming, 0 enables it. Only the low byte is
695+
// significant; store little-endian byte-wise to avoid unaligned access.
696+
buf[name_len + 0] = enabled ? 0 : 1;
697+
buf[name_len + 1] = 0;
698+
buf[name_len + 2] = 0;
699+
buf[name_len + 3] = 0;
700+
return cyw43_ioctl(self, CYW43_IOCTL_SET_VAR, name_len + 4, buf, CYW43_ITF_STA);
701+
}
702+
703+
int cyw43_wifi_get_roam_enabled(cyw43_t *self, bool *enabled) {
704+
if (enabled == NULL) {
705+
return -CYW43_EINVAL;
706+
}
707+
// Read the "roam_off" iovar back via the WLC_GET_VAR ioctl. The variable
708+
// name is written into the buffer and the firmware replaces it with the
709+
// little-endian u32 value on return (see how CYW43_IOCTL_GET_VAR is used).
710+
static const char var[] = "roam_off";
711+
const size_t name_len = sizeof(var); // includes the NUL terminator
712+
uint8_t buf[sizeof(var) + 4];
713+
memcpy(buf, var, name_len);
714+
buf[name_len + 0] = 0;
715+
buf[name_len + 1] = 0;
716+
buf[name_len + 2] = 0;
717+
buf[name_len + 3] = 0;
718+
int ret = cyw43_ioctl(self, CYW43_IOCTL_GET_VAR, name_len + 4, buf, CYW43_ITF_STA);
719+
if (ret == 0) {
720+
uint32_t roam_off = (uint32_t)buf[0] | ((uint32_t)buf[1] << 8) |
721+
((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24);
722+
// roam_off != 0 means roaming is disabled.
723+
*enabled = (roam_off == 0);
724+
}
725+
return ret;
726+
}
671727

672728
int cyw43_wifi_get_rssi(cyw43_t *self, int32_t *rssi) {
673729
if (!rssi || !CYW43_STA_IS_ACTIVE(self)) {

src/cyw43_ll.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,18 @@
5656
#define CYW43_IOCTL_GET_SSID (0x32)
5757
#define CYW43_IOCTL_GET_CHANNEL (0x3a)
5858
#define CYW43_IOCTL_SET_DISASSOC (0x69)
59+
#define CYW43_IOCTL_GET_ROAM_TRIGGER (0x6c)
60+
#define CYW43_IOCTL_SET_ROAM_TRIGGER (0x6f)
61+
#define CYW43_IOCTL_GET_ROAM_DELTA (0x70)
62+
#define CYW43_IOCTL_SET_ROAM_DELTA (0x73)
63+
#define CYW43_IOCTL_GET_ROAM_SCAN_PERIOD (0x74)
64+
#define CYW43_IOCTL_SET_ROAM_SCAN_PERIOD (0x77)
5965
#define CYW43_IOCTL_GET_ANTDIV (0x7e)
6066
#define CYW43_IOCTL_SET_ANTDIV (0x81)
6167
#define CYW43_IOCTL_SET_MONITOR (0xd9)
6268
#define CYW43_IOCTL_GET_RSSI (0xfe)
69+
#define CYW43_IOCTL_GET_INTERFERENCE_MODE (0x1a6)
70+
#define CYW43_IOCTL_SET_INTERFERENCE_MODE (0x1a9)
6371
#define CYW43_IOCTL_GET_VAR (0x20c)
6472
#define CYW43_IOCTL_SET_VAR (0x20f)
6573

0 commit comments

Comments
 (0)