#include "utils/list.h"
+/**
+ * PR_MAX_OP_CLASSES - Maximum number of operating classes
+ */
+#define PR_MAX_OP_CLASSES 15
+
+/**
+ * PR_MAX_OP_CLASS_CHANNELS - Maximum number of channels per operating class
+ */
+#define PR_MAX_OP_CLASS_CHANNELS 60
+
/**
* PR_MAX_PEER - Maximum number of Proximity Ranging peers that device can store
*/
#define PR_MAX_PEER 100
+/**
+ * struct pr_channels - List of supported channels
+ */
+struct pr_channels {
+ /**
+ * struct pr_op_class - Supported operating class
+ */
+ struct pr_op_class {
+ /**
+ * op_class - Operating class
+ */
+ u8 op_class;
+
+ /**
+ * channel - Supported channels
+ */
+ u8 channel[PR_MAX_OP_CLASS_CHANNELS];
+
+ /**
+ * channels - Number of channel entries in use
+ */
+ size_t channels;
+ } op_class[PR_MAX_OP_CLASSES];
+
+ /**
+ * op_classes - Number of op_class entries in use
+ */
+ size_t op_classes;
+};
+
+/**
+ * Format and Bandwidth values for EDCA based ranging with range of 10-16
+ * from IEEE Std 802.11-2024, 9.4.2.166 (FTM Parameters element), Table 9-325
+ * (Format And Bandwidth subfield) as specified in Proximity Ranging
+ * Implementation Considerations for P2P Operation, Draft 1.8, Table 8
+ * (Proximity Ranging EDCA Capability Attribute format) for the the Ranging
+ * Parameters field B0-B3.
+ */
+enum edca_format_and_bw_value {
+ EDCA_FORMAT_AND_BW_VHT20 = 10,
+ EDCA_FORMAT_AND_BW_HT40 = 11,
+ EDCA_FORMAT_AND_BW_VHT40 = 12,
+ EDCA_FORMAT_AND_BW_VHT80 = 13,
+ EDCA_FORMAT_AND_BW_VHT80P80 = 14,
+ EDCA_FORMAT_AND_BW_VHT160_DUAL_LO = 15,
+ EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO = 16,
+};
+
+/**
+ * Format and Bandwidth values for NTB based ranging as from IEEE Std
+ * 802.11-2024, 9.4.2.300 (Ranging Parameters element), Table 9-412 (Format And
+ * Bandwidth subfield) as specified in Proximity Ranging Implementation
+ * Considerations for P2P Operation, Draft 1.8, Table 9 (Proximity Ranging 11az
+ * NTB Capability Attribute format) for the Ranging Parameter field B0-B2.
+ */
+enum ntb_format_and_bw_value {
+ NTB_FORMAT_AND_BW_HE20 = 0,
+ NTB_FORMAT_AND_BW_HE40 = 1,
+ NTB_FORMAT_AND_BW_HE80 = 2,
+ NTB_FORMAT_AND_BW_HE80P80 = 3,
+ NTB_FORMAT_AND_BW_HE160_DUAL_LO = 4,
+ NTB_FORMAT_AND_BW_HE160_SINGLE_LO = 5,
+};
+
/**
* struct pr_device_info - Proximity ranging peer information
*/
u8 max_rx_antenna;
+ struct pr_channels edca_channels;
+
bool ntb_ista_support;
bool ntb_rsta_support;
u8 ntb_format_and_bw;
+ struct pr_channels ntb_channels;
+
bool support_6ghz;
/**
#include "pr_supplicant.h"
+static int wpas_pr_edca_get_bw(enum edca_format_and_bw_value format_and_bw)
+{
+ switch (format_and_bw) {
+ case EDCA_FORMAT_AND_BW_VHT20:
+ return 20;
+ case EDCA_FORMAT_AND_BW_HT40:
+ case EDCA_FORMAT_AND_BW_VHT40:
+ return 40;
+ case EDCA_FORMAT_AND_BW_VHT80:
+ return 80;
+ case EDCA_FORMAT_AND_BW_VHT80P80:
+ case EDCA_FORMAT_AND_BW_VHT160_DUAL_LO:
+ case EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO:
+ return 160;
+ default:
+ return 0;
+ }
+}
+
+
+static int wpas_pr_ntb_get_bw(enum ntb_format_and_bw_value format_and_bw)
+{
+ switch (format_and_bw) {
+ case NTB_FORMAT_AND_BW_HE20:
+ return 20;
+ case NTB_FORMAT_AND_BW_HE40:
+ return 40;
+ case NTB_FORMAT_AND_BW_HE80:
+ return 80;
+ case NTB_FORMAT_AND_BW_HE80P80:
+ case NTB_FORMAT_AND_BW_HE160_DUAL_LO:
+ case NTB_FORMAT_AND_BW_HE160_SINGLE_LO:
+ return 160;
+ default:
+ return 0;
+ }
+}
+
+
+static bool
+wpas_pr_edca_is_valid_op_class(enum edca_format_and_bw_value format_and_bw,
+ const struct oper_class_map *op_class_map)
+{
+ int bw = 0, op_class_bw = 0;
+
+ if (!op_class_map)
+ return false;
+
+ op_class_bw = oper_class_bw_to_int(op_class_map);
+ bw = wpas_pr_edca_get_bw(format_and_bw);
+
+ if (!op_class_bw || !bw)
+ return false;
+
+ if (format_and_bw <= EDCA_FORMAT_AND_BW_VHT80 &&
+ format_and_bw >= EDCA_FORMAT_AND_BW_VHT20 &&
+ op_class_bw <= bw)
+ return true;
+
+ if (format_and_bw == EDCA_FORMAT_AND_BW_VHT80P80 &&
+ (op_class_bw < bw || op_class_map->bw == BW80P80))
+ return true;
+
+ if ((format_and_bw == EDCA_FORMAT_AND_BW_VHT160_DUAL_LO ||
+ format_and_bw == EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO) &&
+ (op_class_bw < bw || op_class_map->bw == BW160))
+ return true;
+
+ return false;
+}
+
+
+static bool
+wpas_pr_ntb_is_valid_op_class(enum ntb_format_and_bw_value format_and_bw,
+ const struct oper_class_map *op_class_map)
+{
+ int bw = 0, op_class_bw = 0;
+
+ if (!op_class_map)
+ return false;
+
+ op_class_bw = oper_class_bw_to_int(op_class_map);
+ bw = wpas_pr_ntb_get_bw(format_and_bw);
+
+ if (!op_class_bw || !bw)
+ return false;
+
+ if (format_and_bw <= NTB_FORMAT_AND_BW_HE80 &&
+ format_and_bw >= NTB_FORMAT_AND_BW_HE20 &&
+ op_class_bw <= bw)
+ return true;
+
+ if (format_and_bw == NTB_FORMAT_AND_BW_HE80P80 &&
+ (op_class_bw < bw || op_class_map->bw == BW80P80))
+ return true;
+
+ if ((format_and_bw == NTB_FORMAT_AND_BW_HE160_DUAL_LO ||
+ format_and_bw == NTB_FORMAT_AND_BW_HE160_SINGLE_LO) &&
+ (op_class_bw < bw || op_class_map->bw == BW160))
+ return true;
+
+ return false;
+}
+
+
+static void
+wpas_pr_setup_edca_channels(struct wpa_supplicant *wpa_s,
+ struct pr_channels *chan,
+ enum edca_format_and_bw_value format_and_bw)
+{
+ struct hostapd_hw_modes *mode;
+ int cla = 0, i;
+
+ for (i = 0; global_op_class[i].op_class; i++) {
+ unsigned int ch;
+ struct pr_op_class *op = NULL;
+ const struct oper_class_map *o = &global_op_class[i];
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode,
+ is_6ghz_op_class(o->op_class));
+ if (!mode || is_6ghz_op_class(o->op_class) ||
+ !wpas_pr_edca_is_valid_op_class(format_and_bw, o))
+ continue;
+
+ for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ enum chan_allowed res;
+
+ /* Check for non-continuous jump in channel index
+ * increment.
+ */
+ if (o->op_class >= 128 && o->op_class <= 130 &&
+ ch < 149 && ch + o->inc > 149)
+ ch = 149;
+
+ res = verify_channel(mode, o->op_class, ch, o->bw);
+
+ if (res == ALLOWED) {
+ if (!op) {
+ if (cla == PR_MAX_OP_CLASSES)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "PR: Add operating class: %u (EDCA)",
+ o->op_class);
+ op = &chan->op_class[cla];
+ cla++;
+ op->op_class = o->op_class;
+ }
+ if (op->channels == PR_MAX_OP_CLASS_CHANNELS)
+ continue;
+ op->channel[op->channels] = ch;
+ op->channels++;
+ }
+ }
+
+ if (op)
+ wpa_hexdump(MSG_DEBUG, "PR: Channels (EDCA)",
+ op->channel, op->channels);
+ }
+
+ chan->op_classes = cla;
+}
+
+
+static void
+wpas_pr_setup_ntb_channels(struct wpa_supplicant *wpa_s,
+ struct pr_channels *chan,
+ enum ntb_format_and_bw_value format_and_bw,
+ bool allow_6ghz)
+{
+ int cla = 0, i;
+ struct hostapd_hw_modes *mode;
+
+ for (i = 0; global_op_class[i].op_class; i++) {
+ unsigned int ch;
+ struct pr_op_class *op = NULL;
+ const struct oper_class_map *o = &global_op_class[i];
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode,
+ is_6ghz_op_class(o->op_class));
+ if (!mode || (!allow_6ghz && is_6ghz_op_class(o->op_class)) ||
+ !wpas_pr_ntb_is_valid_op_class(format_and_bw, o))
+ continue;
+
+ for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ enum chan_allowed res;
+
+ /* Check for non-continuous jump in channel index
+ * increment.
+ */
+ if (o->op_class >= 128 && o->op_class <= 130 &&
+ ch < 149 && ch + o->inc > 149)
+ ch = 149;
+
+ res = verify_channel(mode, o->op_class, ch, o->bw);
+
+ if (res == ALLOWED) {
+ if (!op) {
+ if (cla == PR_MAX_OP_CLASSES)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "PR: Add operating class: %u (NTB)",
+ o->op_class);
+ op = &chan->op_class[cla];
+ cla++;
+ op->op_class = o->op_class;
+ }
+ if (op->channels == PR_MAX_OP_CLASS_CHANNELS)
+ continue;
+ op->channel[op->channels] = ch;
+ op->channels++;
+ }
+ }
+ if (op) {
+ wpa_hexdump(MSG_DEBUG, "PR: Channels (NTB)",
+ op->channel, op->channels);
+ }
+ }
+
+ chan->op_classes = cla;
+}
+
+
int wpas_pr_init(struct wpa_global *global, struct wpa_supplicant *wpa_s,
const struct wpa_driver_capa *capa)
{
pr.max_rx_antenna = capa->max_rx_antenna;
pr.max_tx_antenna = capa->max_tx_antenna;
+ wpas_pr_setup_edca_channels(wpa_s, &pr.edca_channels,
+ pr.edca_format_and_bw);
+
pr.ntb_ista_support = wpa_s->drv_flags2 &
WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_INITIATOR;
pr.ntb_rsta_support = wpa_s->drv_flags2 &
pr.secure_he_ltf = wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+ wpas_pr_setup_ntb_channels(wpa_s, &pr.ntb_channels,
+ pr.ntb_format_and_bw,
+ pr.support_6ghz);
+
if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
os_memcpy(pr.country, wpa_s->conf->country, 2);
pr.country[2] = 0x04;