static int qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef) { struct net_device *ndev = wdev->netdev; struct qtnf_vif *vif; int ret; if (!ndev) return -ENODEV; vif = qtnf_netdev_get_priv(wdev->netdev); ret = qtnf_cmd_get_channel(vif, chandef); if (ret) { pr_err("%s: failed to get channel: %d\n", ndev->name, ret); goto out; } if (!cfg80211_chandef_valid(chandef)) { pr_err("%s: bad chan freq1=%u freq2=%u bw=%u\n", ndev->name, chandef->center_freq1, chandef->center_freq2, chandef->width); ret = -ENODATA; } out: return ret; }
static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params) { struct qtnf_wmac *mac = wiphy_priv(wiphy); struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); int ret; pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, params->chandef.chan->hw_value, params->count, params->radar_required, params->block_tx); switch (vif->wdev.iftype) { case NL80211_IFTYPE_AP: if (!(vif->bss_status & QTNF_STATE_AP_START)) { pr_warn("AP not started on %s\n", dev->name); return -ENOTCONN; } break; default: pr_err("unsupported vif type (%d) on %s\n", vif->wdev.iftype, dev->name); return -EOPNOTSUPP; } if (vif->vifid != 0) { if (!(mac->status & QTNF_MAC_CSA_ACTIVE)) return -EOPNOTSUPP; if (!cfg80211_chandef_identical(¶ms->chandef, &mac->csa_chandef)) return -EINVAL; return 0; } if (!cfg80211_chandef_valid(¶ms->chandef)) { pr_err("%s: invalid channel\n", dev->name); return -EINVAL; } if (cfg80211_chandef_identical(¶ms->chandef, &mac->chandef)) { pr_err("%s: switch request to the same channel\n", dev->name); return -EALREADY; } ret = qtnf_cmd_send_chan_switch(mac, params); if (ret) pr_warn("%s: failed to switch to channel (%u)\n", dev->name, params->chandef.chan->hw_value); return ret; }
static int qtnf_event_handle_freq_change(struct qtnf_wmac *mac, const struct qlink_event_freq_change *data, u16 len) { struct wiphy *wiphy = priv_to_wiphy(mac); struct cfg80211_chan_def chandef; struct qtnf_vif *vif; int i; if (len < sizeof(*data)) { pr_err("MAC%u: payload is too short\n", mac->macid); return -EINVAL; } if (!wiphy->registered) return 0; qlink_chandef_q2cfg(wiphy, &data->chan, &chandef); if (!cfg80211_chandef_valid(&chandef)) { pr_err("MAC%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n", mac->macid, chandef.chan->center_freq, chandef.center_freq1, chandef.center_freq2, chandef.width); return -EINVAL; } pr_debug("MAC%d: new channel ieee=%u freq1=%u freq2=%u bw=%u\n", mac->macid, chandef.chan->hw_value, chandef.center_freq1, chandef.center_freq2, chandef.width); for (i = 0; i < QTNF_MAX_INTF; i++) { vif = &mac->iflist[i]; if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) continue; if (vif->netdev) { mutex_lock(&vif->wdev.mtx); cfg80211_ch_switch_notify(vif->netdev, &chandef); mutex_unlock(&vif->wdev.mtx); } } return 0; }
void cfg80211_set_dfs_state(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state) { int width; if (WARN_ON(!cfg80211_chandef_valid(chandef))) return; width = cfg80211_chandef_get_width(chandef); if (width < 0) return; cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1, width, dfs_state); if (!chandef->center_freq2) return; cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2, width, dfs_state); }
static int qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef) { struct qtnf_wmac *mac = wiphy_priv(wiphy); struct net_device *ndev = wdev->netdev; struct qtnf_vif *vif; if (!ndev) return -ENODEV; vif = qtnf_netdev_get_priv(wdev->netdev); switch (vif->wdev.iftype) { case NL80211_IFTYPE_STATION: if (vif->sta_state == QTNF_STA_DISCONNECTED) { pr_warn("%s: STA disconnected\n", ndev->name); return -ENODATA; } break; case NL80211_IFTYPE_AP: if (!(vif->bss_status & QTNF_STATE_AP_START)) { pr_warn("%s: AP not started\n", ndev->name); return -ENODATA; } break; default: pr_err("unsupported vif type (%d)\n", vif->wdev.iftype); return -ENODATA; } if (!cfg80211_chandef_valid(&mac->chandef)) { pr_err("invalid channel settings on %s\n", ndev->name); return -ENODATA; } memcpy(chandef, &mac->chandef, sizeof(*chandef)); return 0; }
static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params) { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); int ret; pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, params->chandef.chan->hw_value, params->count, params->radar_required, params->block_tx); if (!cfg80211_chandef_valid(¶ms->chandef)) { pr_err("%s: invalid channel\n", dev->name); return -EINVAL; } ret = qtnf_cmd_send_chan_switch(vif, params); if (ret) pr_warn("%s: failed to switch to channel (%u)\n", dev->name, params->chandef.chan->hw_value); return ret; }
int cfg80211_chandef_dfs_required(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef) { int width; int r; if (WARN_ON(!cfg80211_chandef_valid(chandef))) return -EINVAL; width = cfg80211_chandef_get_width(chandef); if (width < 0) return -EINVAL; r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1, width); if (r) return r; if (!chandef->center_freq2) return 0; return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, width); }
bool cfg80211_chandef_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, u32 prohibited_flags) { struct ieee80211_sta_ht_cap *ht_cap; struct ieee80211_sta_vht_cap *vht_cap; u32 width, control_freq; if (WARN_ON(!cfg80211_chandef_valid(chandef))) return false; ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; control_freq = chandef->chan->center_freq; switch (chandef->width) { case NL80211_CHAN_WIDTH_5: width = 5; break; case NL80211_CHAN_WIDTH_10: width = 10; break; case NL80211_CHAN_WIDTH_20: if (!ht_cap->ht_supported) return false; case NL80211_CHAN_WIDTH_20_NOHT: width = 20; break; case NL80211_CHAN_WIDTH_40: width = 40; if (!ht_cap->ht_supported) return false; if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) return false; if (chandef->center_freq1 < control_freq && chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) return false; if (chandef->center_freq1 > control_freq && chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) return false; break; case NL80211_CHAN_WIDTH_80P80: if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) return false; case NL80211_CHAN_WIDTH_80: if (!vht_cap->vht_supported) return false; prohibited_flags |= IEEE80211_CHAN_NO_80MHZ; width = 80; break; case NL80211_CHAN_WIDTH_160: if (!vht_cap->vht_supported) return false; if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) return false; prohibited_flags |= IEEE80211_CHAN_NO_160MHZ; width = 160; break; default: WARN_ON_ONCE(1); return false; } /* * TODO: What if there are only certain 80/160/80+80 MHz channels * allowed by the driver, or only certain combinations? * For 40 MHz the driver can set the NO_HT40 flags, but for * 80/160 MHz and in particular 80+80 MHz this isn't really * feasible and we only have NO_80MHZ/NO_160MHZ so far but * no way to cover 80+80 MHz or more complex restrictions. * Note that such restrictions also need to be advertised to * userspace, for example for P2P channel selection. */ if (width > 20) prohibited_flags |= IEEE80211_CHAN_NO_OFDM; /* 5 and 10 MHz are only defined for the OFDM PHY */ if (width < 20) prohibited_flags |= IEEE80211_CHAN_NO_OFDM; if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1, width, prohibited_flags)) return false; if (!chandef->center_freq2) return true; return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2, width, prohibited_flags); }
static int qtnf_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); struct qtnf_wmac *mac = wiphy_priv(wiphy); struct cfg80211_chan_def chandef; struct qtnf_bss_config *bss_cfg; int ret; if (vif->wdev.iftype != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; if (vif->sta_state != QTNF_STA_DISCONNECTED) return -EBUSY; bss_cfg = &vif->bss_cfg; memset(bss_cfg, 0, sizeof(*bss_cfg)); if (sme->channel) { /* FIXME: need to set proper nl80211_channel_type value */ cfg80211_chandef_create(&chandef, sme->channel, NL80211_CHAN_HT20); /* fall-back to minimal safe chandef description */ if (!cfg80211_chandef_valid(&chandef)) cfg80211_chandef_create(&chandef, sme->channel, NL80211_CHAN_HT20); memcpy(&mac->chandef, &chandef, sizeof(mac->chandef)); } bss_cfg->ssid_len = sme->ssid_len; memcpy(&bss_cfg->ssid, sme->ssid, bss_cfg->ssid_len); bss_cfg->auth_type = sme->auth_type; bss_cfg->privacy = sme->privacy; bss_cfg->mfp = sme->mfp; if ((sme->bg_scan_period > 0) && (sme->bg_scan_period <= QTNF_MAX_BG_SCAN_PERIOD)) bss_cfg->bg_scan_period = sme->bg_scan_period; else if (sme->bg_scan_period == -1) bss_cfg->bg_scan_period = QTNF_DEFAULT_BG_SCAN_PERIOD; else bss_cfg->bg_scan_period = 0; /* disabled */ bss_cfg->connect_flags = 0; if (sme->flags & ASSOC_REQ_DISABLE_HT) bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; if (sme->flags & ASSOC_REQ_DISABLE_VHT) bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; if (sme->flags & ASSOC_REQ_USE_RRM) bss_cfg->connect_flags |= QLINK_STA_CONNECT_USE_RRM; memcpy(&bss_cfg->crypto, &sme->crypto, sizeof(bss_cfg->crypto)); if (sme->bssid) ether_addr_copy(bss_cfg->bssid, sme->bssid); else eth_zero_addr(bss_cfg->bssid); ret = qtnf_cmd_send_connect(vif, sme); if (ret) { pr_err("VIF%u.%u: failed to connect\n", vif->mac->macid, vif->vifid); return ret; } vif->sta_state = QTNF_STA_CONNECTING; return 0; }
static int qtnf_event_handle_radar(struct qtnf_vif *vif, const struct qlink_event_radar *ev, u16 len) { struct wiphy *wiphy = priv_to_wiphy(vif->mac); struct cfg80211_chan_def chandef; if (len < sizeof(*ev)) { pr_err("MAC%u: payload is too short\n", vif->mac->macid); return -EINVAL; } if (!wiphy->registered || !vif->netdev) return 0; qlink_chandef_q2cfg(wiphy, &ev->chan, &chandef); if (!cfg80211_chandef_valid(&chandef)) { pr_err("MAC%u: bad channel f1=%u f2=%u bw=%u\n", vif->mac->macid, chandef.center_freq1, chandef.center_freq2, chandef.width); return -EINVAL; } pr_info("%s: radar event=%u f1=%u f2=%u bw=%u\n", vif->netdev->name, ev->event, chandef.center_freq1, chandef.center_freq2, chandef.width); switch (ev->event) { case QLINK_RADAR_DETECTED: cfg80211_radar_event(wiphy, &chandef, GFP_KERNEL); break; case QLINK_RADAR_CAC_FINISHED: if (!vif->wdev.cac_started) break; cfg80211_cac_event(vif->netdev, &chandef, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); break; case QLINK_RADAR_CAC_ABORTED: if (!vif->wdev.cac_started) break; cfg80211_cac_event(vif->netdev, &chandef, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); break; case QLINK_RADAR_CAC_STARTED: if (vif->wdev.cac_started) break; if (!wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) break; cfg80211_cac_event(vif->netdev, &chandef, NL80211_RADAR_CAC_STARTED, GFP_KERNEL); break; default: pr_warn("%s: unhandled radar event %u\n", vif->netdev->name, ev->event); break; } return 0; }
static int qtnf_event_handle_bss_join(struct qtnf_vif *vif, const struct qlink_event_bss_join *join_info, u16 len) { struct wiphy *wiphy = priv_to_wiphy(vif->mac); enum ieee80211_statuscode status = le16_to_cpu(join_info->status); struct cfg80211_chan_def chandef; struct cfg80211_bss *bss = NULL; u8 *ie = NULL; size_t payload_len; u16 tlv_type; u16 tlv_value_len; size_t tlv_full_len; const struct qlink_tlv_hdr *tlv; const u8 *rsp_ies = NULL; size_t rsp_ies_len = 0; if (unlikely(len < sizeof(*join_info))) { pr_err("VIF%u.%u: payload is too short (%u < %zu)\n", vif->mac->macid, vif->vifid, len, sizeof(struct qlink_event_bss_join)); return -EINVAL; } if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { pr_err("VIF%u.%u: BSS_JOIN event when not in STA mode\n", vif->mac->macid, vif->vifid); return -EPROTO; } pr_debug("VIF%u.%u: BSSID:%pM status:%u\n", vif->mac->macid, vif->vifid, join_info->bssid, status); if (status != WLAN_STATUS_SUCCESS) goto done; qlink_chandef_q2cfg(wiphy, &join_info->chan, &chandef); if (!cfg80211_chandef_valid(&chandef)) { pr_warn("MAC%u.%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n", vif->mac->macid, vif->vifid, chandef.chan->center_freq, chandef.center_freq1, chandef.center_freq2, chandef.width); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } bss = cfg80211_get_bss(wiphy, chandef.chan, join_info->bssid, NULL, 0, IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); if (!bss) { pr_warn("VIF%u.%u: add missing BSS:%pM chan:%u\n", vif->mac->macid, vif->vifid, join_info->bssid, chandef.chan->hw_value); if (!vif->wdev.ssid_len) { pr_warn("VIF%u.%u: SSID unknown for BSS:%pM\n", vif->mac->macid, vif->vifid, join_info->bssid); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } ie = kzalloc(2 + vif->wdev.ssid_len, GFP_KERNEL); if (!ie) { pr_warn("VIF%u.%u: IE alloc failed for BSS:%pM\n", vif->mac->macid, vif->vifid, join_info->bssid); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } ie[0] = WLAN_EID_SSID; ie[1] = vif->wdev.ssid_len; memcpy(ie + 2, vif->wdev.ssid, vif->wdev.ssid_len); bss = cfg80211_inform_bss(wiphy, chandef.chan, CFG80211_BSS_FTYPE_UNKNOWN, join_info->bssid, 0, WLAN_CAPABILITY_ESS, 100, ie, 2 + vif->wdev.ssid_len, 0, GFP_KERNEL); if (!bss) { pr_warn("VIF%u.%u: can't connect to unknown BSS: %pM\n", vif->mac->macid, vif->vifid, join_info->bssid); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } } payload_len = len - sizeof(*join_info); tlv = (struct qlink_tlv_hdr *)join_info->ies; while (payload_len >= sizeof(struct qlink_tlv_hdr)) { tlv_type = le16_to_cpu(tlv->type); tlv_value_len = le16_to_cpu(tlv->len); tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); if (payload_len < tlv_full_len) { pr_warn("invalid %u TLV\n", tlv_type); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } if (tlv_type == QTN_TLV_ID_IE_SET) { const struct qlink_tlv_ie_set *ie_set; unsigned int ie_len; if (payload_len < sizeof(*ie_set)) { pr_warn("invalid IE_SET TLV\n"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } ie_set = (const struct qlink_tlv_ie_set *)tlv; ie_len = tlv_value_len - (sizeof(*ie_set) - sizeof(ie_set->hdr)); switch (ie_set->type) { case QLINK_IE_SET_ASSOC_RESP: if (ie_len) { rsp_ies = ie_set->ie_data; rsp_ies_len = ie_len; } break; default: pr_warn("unexpected IE type: %u\n", ie_set->type); break; } } payload_len -= tlv_full_len; tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); } if (payload_len) pr_warn("VIF%u.%u: unexpected remaining payload: %zu\n", vif->mac->macid, vif->vifid, payload_len); done: cfg80211_connect_result(vif->netdev, join_info->bssid, NULL, 0, rsp_ies, rsp_ies_len, status, GFP_KERNEL); if (bss) { if (!ether_addr_equal(vif->bssid, join_info->bssid)) ether_addr_copy(vif->bssid, join_info->bssid); cfg80211_put_bss(wiphy, bss); } if (status == WLAN_STATUS_SUCCESS) netif_carrier_on(vif->netdev); kfree(ie); return 0; }