void orinoco_add_extscan_result(struct orinoco_private *priv, struct agere_ext_scan_info *bss, size_t len) { struct wiphy *wiphy = priv_to_wiphy(priv); struct ieee80211_channel *channel; struct cfg80211_bss *cbss; const u8 *ie; u64 timestamp; s32 signal; u16 capability; u16 beacon_interval; size_t ie_len; int chan, freq; ie_len = len - sizeof(*bss); ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); chan = ie ? ie[2] : 0; freq = ieee80211_dsss_chan_to_freq(chan); channel = ieee80211_get_channel(wiphy, freq); timestamp = le64_to_cpu(bss->timestamp); capability = le16_to_cpu(bss->capabilities); beacon_interval = le16_to_cpu(bss->beacon_interval); ie = bss->data; signal = SIGNAL_TO_MBM(bss->level); cbss = cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, capability, beacon_interval, ie, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(cbss); }
static void orinoco_add_hostscan_result(struct orinoco_private *priv, const union hermes_scan_info *bss) { struct wiphy *wiphy = priv_to_wiphy(priv); struct ieee80211_channel *channel; struct cfg80211_bss *cbss; u8 *ie; u8 ie_buf[46]; u64 timestamp; s32 signal; u16 capability; u16 beacon_interval; int ie_len; int freq; int len; len = le16_to_cpu(bss->a.essid_len); /* Reconstruct SSID and bitrate IEs to pass up */ ie_buf[0] = WLAN_EID_SSID; ie_buf[1] = len; memcpy(&ie_buf[2], bss->a.essid, len); ie = ie_buf + len + 2; ie_len = ie_buf[1] + 2; switch (priv->firmware_type) { case FIRMWARE_TYPE_SYMBOL: ie_len += symbol_build_supp_rates(ie, bss->s.rates); break; case FIRMWARE_TYPE_INTERSIL: ie_len += prism_build_supp_rates(ie, bss->p.rates); break; case FIRMWARE_TYPE_AGERE: default: break; } freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel)); channel = ieee80211_get_channel(wiphy, freq); if (!channel) { printk(KERN_DEBUG "Invalid channel designation %04X(%04X)", bss->a.channel, freq); return; /* Then ignore it for now */ } timestamp = 0; capability = le16_to_cpu(bss->a.capabilities); beacon_interval = le16_to_cpu(bss->a.beacon_interv); signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); cbss = cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, capability, beacon_interval, ie_buf, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(cbss); }
static void qtnf_mac_init_primary_intf(struct qtnf_wmac *mac) { struct qtnf_vif *vif = &mac->iflist[QTNF_PRIMARY_VIF_IDX]; vif->wdev.iftype = NL80211_IFTYPE_AP; vif->bss_priority = QTNF_DEF_BSS_PRIORITY; vif->wdev.wiphy = priv_to_wiphy(mac); INIT_WORK(&vif->reset_work, qtnf_vif_reset_handler); vif->cons_tx_timeout_cnt = 0; }
static void orinoco_pci_remove_one(struct pci_dev *pdev) { struct orinoco_private *priv = pci_get_drvdata(pdev); orinoco_if_del(priv); wiphy_unregister(priv_to_wiphy(priv)); free_irq(pdev->irq, priv); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_release_regions(pdev); pci_disable_device(pdev); }
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; }
static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid) { struct qtnf_wmac *mac; struct wiphy *wiphy; struct qtnf_vif *vif; unsigned int i; enum nl80211_band band; mac = bus->mac[macid]; if (!mac) return; wiphy = priv_to_wiphy(mac); for (i = 0; i < QTNF_MAX_INTF; i++) { vif = &mac->iflist[i]; rtnl_lock(); if (vif->netdev && vif->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) { qtnf_virtual_intf_cleanup(vif->netdev); qtnf_del_virtual_intf(wiphy, &vif->wdev); } rtnl_unlock(); qtnf_sta_list_free(&vif->sta_list); } if (mac->wiphy_registered) wiphy_unregister(wiphy); for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; ++band) { if (!wiphy->bands[band]) continue; kfree(wiphy->bands[band]->channels); wiphy->bands[band]->n_channels = 0; kfree(wiphy->bands[band]); wiphy->bands[band] = NULL; } kfree(mac->macinfo.limits); kfree(wiphy->iface_combinations); wiphy_free(wiphy); bus->mac[macid] = NULL; }
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif, const char *name, unsigned char name_assign_type, enum nl80211_iftype iftype) { struct wiphy *wiphy = priv_to_wiphy(mac); struct net_device *dev; void *qdev_vif; int ret; dev = alloc_netdev_mqs(sizeof(struct qtnf_vif *), name, name_assign_type, ether_setup, 1, 1); if (!dev) { memset(&vif->wdev, 0, sizeof(vif->wdev)); vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; return -ENOMEM; } vif->netdev = dev; dev->netdev_ops = &qtnf_netdev_ops; dev->needs_free_netdev = true; dev_net_set(dev, wiphy_net(wiphy)); dev->ieee80211_ptr = &vif->wdev; dev->ieee80211_ptr->iftype = iftype; ether_addr_copy(dev->dev_addr, vif->mac_addr); SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); dev->flags |= IFF_BROADCAST | IFF_MULTICAST; dev->watchdog_timeo = QTNF_DEF_WDOG_TIMEOUT; dev->tx_queue_len = 100; qdev_vif = netdev_priv(dev); *((void **)qdev_vif) = vif; SET_NETDEV_DEV(dev, mac->bus->dev); ret = register_netdevice(dev); if (ret) { free_netdev(dev); vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; } return ret; }
static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, struct regulatory_request *req) { struct qtnf_wmac *mac = wiphy_priv(wiphy_in); struct qtnf_bus *bus = mac->bus; struct wiphy *wiphy; unsigned int mac_idx; enum nl80211_band band; int ret; pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, req->alpha2[0], req->alpha2[1]); ret = qtnf_cmd_reg_notify(bus, req); if (ret) { if (ret != -EOPNOTSUPP && ret != -EALREADY) pr_err("failed to update reg domain to %c%c\n", req->alpha2[0], req->alpha2[1]); return; } for (mac_idx = 0; mac_idx < QTNF_MAX_MAC; ++mac_idx) { if (!(bus->hw_info.mac_bitmap & (1 << mac_idx))) continue; mac = bus->mac[mac_idx]; wiphy = priv_to_wiphy(mac); for (band = 0; band < NUM_NL80211_BANDS; ++band) { if (!wiphy->bands[band]) continue; ret = qtnf_cmd_get_mac_chan_info(mac, wiphy->bands[band]); if (ret) pr_err("failed to get chan info for mac %u band %u\n", mac_idx, band); } } }
static int qtnf_mac_init_bands(struct qtnf_wmac *mac) { struct wiphy *wiphy = priv_to_wiphy(mac); int ret = 0; if (mac->macinfo.bands_cap & QLINK_BAND_2GHZ) { ret = qtnf_mac_init_single_band(wiphy, mac, NL80211_BAND_2GHZ); if (ret) goto out; } if (mac->macinfo.bands_cap & QLINK_BAND_5GHZ) { ret = qtnf_mac_init_single_band(wiphy, mac, NL80211_BAND_5GHZ); if (ret) goto out; } if (mac->macinfo.bands_cap & QLINK_BAND_60GHZ) ret = qtnf_mac_init_single_band(wiphy, mac, NL80211_BAND_60GHZ); out: return ret; }
static int qtnf_event_handle_external_auth(struct qtnf_vif *vif, const struct qlink_event_external_auth *ev, u16 len) { struct cfg80211_external_auth_params auth = {0}; struct wiphy *wiphy = priv_to_wiphy(vif->mac); int ret; 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; if (ev->ssid_len) { memcpy(auth.ssid.ssid, ev->ssid, ev->ssid_len); auth.ssid.ssid_len = ev->ssid_len; } auth.key_mgmt_suite = le32_to_cpu(ev->akm_suite); ether_addr_copy(auth.bssid, ev->bssid); auth.action = ev->action; pr_info("%s: external auth bss=%pM action=%u akm=%u\n", vif->netdev->name, auth.bssid, auth.action, auth.key_mgmt_suite); ret = cfg80211_external_auth_request(vif->netdev, &auth, GFP_KERNEL); if (ret) pr_warn("failed to offload external auth request\n"); return ret; }
static int orinoco_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int err; struct orinoco_private *priv; struct orinoco_pci_card *card; void __iomem *hermes_io; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR PFX "Cannot enable PCI device\n"); return err; } err = pci_request_regions(pdev, DRIVER_NAME); if (err) { printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); goto fail_resources; } hermes_io = pci_iomap(pdev, 0, 0); if (!hermes_io) { printk(KERN_ERR PFX "Cannot remap chipset registers\n"); err = -EIO; goto fail_map_hermes; } /* Allocate network device */ priv = alloc_orinocodev(sizeof(*card), &pdev->dev, orinoco_pci_cor_reset, NULL); if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } card = priv->card; hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; goto fail_irq; } err = orinoco_pci_cor_reset(priv); if (err) { printk(KERN_ERR PFX "Initial reset failed\n"); goto fail; } err = orinoco_init(priv); if (err) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail_wiphy; } pci_set_drvdata(pdev, priv); return 0; fail_wiphy: wiphy_unregister(priv_to_wiphy(priv)); fail: free_irq(pdev->irq, priv); fail_irq: free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); fail_map_hermes: pci_release_regions(pdev); fail_resources: pci_disable_device(pdev); return err; }
int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) { struct wiphy *wiphy = priv_to_wiphy(mac); struct ieee80211_iface_combination *iface_comb = NULL; int ret; if (!wiphy) { pr_err("invalid wiphy pointer\n"); return -EFAULT; } iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL); if (!iface_comb) return -ENOMEM; ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo); if (ret) goto out; pr_info("MAC%u: phymode=%#x radar=%#x\n", mac->macid, mac->macinfo.phymode_cap, mac->macinfo.radar_detect_widths); wiphy->frag_threshold = mac->macinfo.frag_thr; wiphy->rts_threshold = mac->macinfo.rts_thr; wiphy->retry_short = mac->macinfo.sretry_limit; wiphy->retry_long = mac->macinfo.lretry_limit; wiphy->coverage_class = mac->macinfo.coverage_class; wiphy->max_scan_ssids = QTNF_MAX_SSID_LIST_LENGTH; wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN; wiphy->mgmt_stypes = qtnf_mgmt_stypes; wiphy->max_remain_on_channel_duration = 5000; wiphy->iface_combinations = iface_comb; wiphy->n_iface_combinations = 1; wiphy->max_num_csa_counters = 2; /* Initialize cipher suits */ wiphy->cipher_suites = qtnf_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_CHANNEL_SWITCH; wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; wiphy->available_antennas_tx = mac->macinfo.num_tx_chain; wiphy->available_antennas_rx = mac->macinfo.num_rx_chain; wiphy->max_ap_assoc_sta = mac->macinfo.max_ap_assoc_sta; ether_addr_copy(wiphy->perm_addr, mac->macaddr); if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) { wiphy->regulatory_flags |= REGULATORY_STRICT_REG | REGULATORY_CUSTOM_REG; wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; wiphy_apply_custom_regulatory(wiphy, hw_info->rd); } else { wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; } ret = wiphy_register(wiphy); if (ret < 0) goto out; if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd); else if (isalpha(hw_info->rd->alpha2[0]) && isalpha(hw_info->rd->alpha2[1])) ret = regulatory_hint(wiphy, hw_info->rd->alpha2); out: if (ret) { kfree(iface_comb); return ret; } 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_scan_results(struct qtnf_vif *vif, const struct qlink_event_scan_result *sr, u16 len) { struct cfg80211_bss *bss; struct ieee80211_channel *channel; struct wiphy *wiphy = priv_to_wiphy(vif->mac); enum cfg80211_bss_frame_type frame_type = CFG80211_BSS_FTYPE_UNKNOWN; size_t payload_len; u16 tlv_type; u16 tlv_value_len; size_t tlv_full_len; const struct qlink_tlv_hdr *tlv; const u8 *ies = NULL; size_t ies_len = 0; if (len < sizeof(*sr)) { pr_err("VIF%u.%u: payload is too short\n", vif->mac->macid, vif->vifid); return -EINVAL; } channel = ieee80211_get_channel(wiphy, le16_to_cpu(sr->freq)); if (!channel) { pr_err("VIF%u.%u: channel at %u MHz not found\n", vif->mac->macid, vif->vifid, le16_to_cpu(sr->freq)); return -EINVAL; } payload_len = len - sizeof(*sr); tlv = (struct qlink_tlv_hdr *)sr->payload; 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 (tlv_full_len > payload_len) return -EINVAL; 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)) return -EINVAL; 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_BEACON_IES: frame_type = CFG80211_BSS_FTYPE_BEACON; break; case QLINK_IE_SET_PROBE_RESP_IES: frame_type = CFG80211_BSS_FTYPE_PRESP; break; default: frame_type = CFG80211_BSS_FTYPE_UNKNOWN; } if (ie_len) { ies = ie_set->ie_data; ies_len = ie_len; } } payload_len -= tlv_full_len; tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); } if (payload_len) return -EINVAL; bss = cfg80211_inform_bss(wiphy, channel, frame_type, sr->bssid, get_unaligned_le64(&sr->tsf), le16_to_cpu(sr->capab), le16_to_cpu(sr->bintval), ies, ies_len, DBM_TO_MBM(sr->sig_dbm), GFP_KERNEL); if (!bss) return -ENOMEM; cfg80211_put_bss(wiphy, bss); 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; }