size_t ssid_len;
};
+struct t2lm_mapping {
+ /**
+ * downlink - Bitmap of TIDs mapped with a link in downlink direction
+ */
+ u8 downlink;
+
+ /**
+ * uplink - Bitmap of TIDs mapped with a link in uplink direction
+ */
+ u8 uplink;
+};
+
/**
* struct wpa_driver_scan_params - Scan parameters
* Data for struct wpa_driver_ops::scan2().
};
struct driver_sta_mlo_info {
+ bool default_map;
u16 req_links; /* bitmap of requested link IDs */
u16 valid_links; /* bitmap of accepted link IDs */
u8 assoc_link_id;
u8 addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
unsigned int freq;
+ struct t2lm_mapping t2lmap;
} links[MAX_NUM_MLD_LINKS];
};
* Described in wpa_event_data.ch_switch.
*/
EVENT_LINK_CH_SWITCH_STARTED,
+
+ /**
+ * EVENT_TID_LINK_MAP - MLD event to set TID-to-link mapping
+ *
+ * This event is used by the driver to indicate the received TID-to-link
+ * mapping response from the associated AP MLD.
+ *
+ * Described in wpa_event_data.t2l_map_info.
+ */
+ EVENT_TID_LINK_MAP,
};
const u8 *td_bitmap;
size_t td_bitmap_len;
} port_authorized;
+
+ /**
+ * struct tid_link_map_info - Data for EVENT_TID_LINK_MAP
+ */
+ struct tid_link_map_info {
+ bool default_map;
+ u8 valid_links;
+ struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
+ } t2l_map_info;
};
/**
#ifdef CONFIG_DRIVER_NL80211_QCA
os_free(drv->pending_roam_data);
drv->pending_roam_data = NULL;
+ os_free(drv->pending_t2lm_data);
+ drv->pending_t2lm_data = NULL;
#endif /* CONFIG_DRIVER_NL80211_QCA */
drv->auth_mld = false;
struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
res = os_snprintf(pos, end - pos,
- "ap_mld_addr=" MACSTR "\n",
- MAC2STR(mlo->ap_mld_addr));
+ "ap_mld_addr=" MACSTR "\n"
+ "default_map=%d\n",
+ MAC2STR(mlo->ap_mld_addr),
+ mlo->default_map);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
+
+ if (!mlo->default_map) {
+ res = os_snprintf(
+ pos, end - pos,
+ "uplink_map[%u]=%x\n"
+ "downlink_map[%u]=%x\n",
+ i, mlo->links[i].t2lmap.uplink,
+ i, mlo->links[i].t2lmap.downlink);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
}
}
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static void
+qca_nl80211_tid_to_link_map_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_MAX + 1];
+ struct nlattr *tids;
+ union wpa_event_data event;
+ u8 *ap_mld;
+ int i, rem, tidnum = 0;
+
+ os_memset(&event, 0, sizeof(event));
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_AP_MLD_ADDR])
+ return;
+
+ ap_mld = nla_data(tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_AP_MLD_ADDR]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
+ " received in TID to link mapping event", MAC2STR(ap_mld));
+ if (!drv->sta_mlo_info.valid_links ||
+ os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+ if (drv->pending_t2lm_data == data) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop pending TID-to-link mapping event since AP MLD not matched even after new connect/roam event");
+ os_free(drv->pending_t2lm_data);
+ drv->pending_t2lm_data = NULL;
+ return;
+ }
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Cache new TID-to-link map event until the next connect/roam event");
+ if (drv->pending_t2lm_data) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Override old TID-to-link map event data");
+ os_free(drv->pending_t2lm_data);
+ }
+ drv->pending_t2lm_data = os_memdup(data, len);
+ if (!drv->pending_t2lm_data)
+ return;
+ drv->pending_t2lm_data_len = len;
+ return;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_STATUS]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Default TID-to-link map");
+ event.t2l_map_info.default_map = true;
+ goto out;
+ }
+
+ event.t2l_map_info.default_map = false;
+
+ nla_for_each_nested(tids,
+ tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_STATUS],
+ rem) {
+ u16 uplink, downlink;
+ struct nlattr *tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_MAX + 1];
+
+ if (nla_parse_nested(
+ tid, QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_MAX,
+ tids, NULL)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: nla_parse_nested() failed");
+ return;
+ }
+
+ if (!tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_UPLINK]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: uplink not present for tid: %d",
+ tidnum);
+ return;
+ }
+ uplink = nla_get_u16(tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_UPLINK]);
+
+ if (!tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_DOWNLINK]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: downlink not present for tid: %d",
+ tidnum);
+ return;
+ }
+ downlink = nla_get_u16(tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_DOWNLINK]);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: Received uplink %x downlink %x",
+ uplink, downlink);
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+ continue;
+ if (uplink & BIT(i))
+ event.t2l_map_info.t2lmap[i].uplink |=
+ BIT(tidnum);
+ if (downlink & BIT(i))
+ event.t2l_map_info.t2lmap[i].downlink |=
+ BIT(tidnum);
+ }
+
+ tidnum++;
+ }
+
+out:
+ drv->sta_mlo_info.default_map = event.t2l_map_info.default_map;
+
+ event.t2l_map_info.valid_links = drv->sta_mlo_info.valid_links;
+ for (i = 0; i < MAX_NUM_MLD_LINKS && !drv->sta_mlo_info.default_map;
+ i++) {
+ if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+ continue;
+
+ drv->sta_mlo_info.links[i].t2lmap.uplink =
+ event.t2l_map_info.t2lmap[i].uplink;
+ drv->sta_mlo_info.links[i].t2lmap.downlink =
+ event.t2l_map_info.t2lmap[i].downlink;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_TID_LINK_MAP, &event);
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
enum nl80211_commands cmd, bool qca_roam_auth,
struct nlattr *status,
* operation that happened in parallel with the disconnection request.
*/
drv->ignore_next_local_disconnect = 0;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (drv->pending_t2lm_data)
+ qca_nl80211_tid_to_link_map_event(drv, drv->pending_t2lm_data,
+ drv->pending_t2lm_data_len);
+ else
+ drv->sta_mlo_info.default_map = true;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
}
qca_nl80211_pasn_auth(drv, data, len);
break;
#endif /* CONFIG_PASN */
+ case QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP:
+ qca_nl80211_tid_to_link_map_event(drv, data, len);
+ break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
default:
wpa_printf(MSG_DEBUG,
}
+static const char * bitmap_to_str(u8 value, char *buf)
+{
+ char *pos = buf;
+ int i, k = 0;
+
+ for (i = 7; i >= 0; i--)
+ pos[k++] = (value & BIT(i)) ? '1' : '0';
+
+ pos[8] = '\0';
+ return pos;
+}
+
+
+static void wpas_tid_link_map(struct wpa_supplicant *wpa_s,
+ struct tid_link_map_info *info)
+{
+ char map_info[1000], *pos, *end;
+ int res, i;
+
+ pos = map_info;
+ end = pos + sizeof(map_info);
+ res = os_snprintf(map_info, sizeof(map_info), "default=%d",
+ info->default_map);
+ if (os_snprintf_error(end - pos, res))
+ return;
+ pos += res;
+
+ if (!info->default_map) {
+ for (i = 0; i < MAX_NUM_MLD_LINKS && end > pos; i++) {
+ char uplink_map_str[9];
+ char downlink_map_str[9];
+
+ if (!(info->valid_links & BIT(i)))
+ continue;
+
+ bitmap_to_str(info->t2lmap[i].uplink, uplink_map_str);
+ bitmap_to_str(info->t2lmap[i].downlink,
+ downlink_map_str);
+
+ res = os_snprintf(pos, end - pos,
+ " link_id=%d up_link=%s down_link=%s",
+ i, uplink_map_str,
+ downlink_map_str);
+ if (os_snprintf_error(end - pos, res))
+ return;
+ pos += res;
+ }
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_T2LM_UPDATE "%s", map_info);
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
wpas_dpp_tx_wait_expire(wpa_s);
#endif /* CONFIG_DPP */
break;
+ case EVENT_TID_LINK_MAP:
+ if (data)
+ wpas_tid_link_map(wpa_s, &data->t2l_map_info);
+ break;
default:
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break;