static int r92su_stop(struct net_device *ndev) { struct r92su *r92su = ndev->ml_priv; struct cfg80211_bss *tmp_bss; struct llist_node *node; int err = -EINVAL, i; mutex_lock(&r92su->lock); if (r92su_is_connected(r92su)) { err = __r92su_disconnect(r92su); WARN_ONCE(err, "disconnect failed"); } r92su_set_power(r92su, false); if (r92su_is_initializing(r92su)) { err = r92su_hw_mac_deinit(r92su); WARN_ONCE(err, "failed to deinitilize MAC"); } if (r92su_is_initializing(r92su)) r92su_set_state(r92su, R92SU_STOP); if (r92su->scan_request) cfg80211_scan_done(r92su->scan_request, true); tmp_bss = r92su->want_connect_bss; r92su->want_connect_bss = NULL; r92su_bss_free(r92su, tmp_bss); r92su->scan_request = NULL; for (i = 0; i < MAX_STA; i++) r92su_sta_del(r92su, i); mutex_unlock(&r92su->lock); cancel_delayed_work_sync(&r92su->survey_done_work); cancel_delayed_work_sync(&r92su->service_work); cancel_work_sync(&r92su->add_bss_work); cancel_work_sync(&r92su->connect_bss_work); cancel_work_sync(&r92su->disconnect_work); node = llist_del_all(&r92su->add_bss_list); while (node) { struct r92su_add_bss *bss_priv = llist_entry(node, struct r92su_add_bss, head); node = ACCESS_ONCE(node->next); kfree(bss_priv); } /* wait for keys and stas to be freed */ synchronize_rcu(); rcu_barrier(); return err; }
static void r92su_bss_free_connected(struct r92su *r92su, bool locally_generated) { struct cfg80211_bss *old_bss; if (r92su_is_connected(r92su)) r92su_set_state(r92su, R92SU_OPEN); rcu_read_lock(); old_bss = rcu_dereference(r92su->connect_bss); rcu_assign_pointer(r92su->connect_bss, NULL); if (old_bss) { switch (r92su->wdev.iftype) { case NL80211_IFTYPE_STATION: /* cfg80211 doesn't like it when cfg80211_disconnected * is called without reason. So check if we were really * connected. */ cfg80211_disconnected(r92su->wdev.netdev, WLAN_STATUS_UNSPECIFIED_FAILURE, NULL, 0, locally_generated, GFP_ATOMIC); break; case NL80211_IFTYPE_ADHOC: cfg80211_unlink_bss(r92su->wdev.wiphy, old_bss); break; default: WARN(1, "unsupported network type %d\n", r92su->wdev.iftype); break; } r92su_bss_free(r92su, old_bss); } rcu_read_unlock(); }
static void r92su_bss_connect_work(struct work_struct *work) { struct r92su *r92su; struct c2h_join_bss_event *join_bss = NULL; struct cfg80211_bss *cfg_bss = NULL; struct r92su_bss_priv *bss_priv; u8 *resp_ie = NULL; unsigned int resp_ie_len = 0; u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE; r92su = container_of(work, struct r92su, connect_bss_work); mutex_lock(&r92su->lock); if (!r92su_is_open(r92su)) goto out; cfg_bss = r92su->want_connect_bss; join_bss = r92su->connect_result; if (!cfg_bss || !join_bss) goto out; bss_priv = r92su_get_bss_priv(cfg_bss); r92su->connect_result = NULL; if (le32_to_cpu(join_bss->bss.ie_length) < 12) goto report_cfg80211; if (join_bss->join_result) { struct r92su_bss_priv *bss_priv = r92su_get_bss_priv(cfg_bss); struct r92su_sta *sta; sta = r92su_sta_alloc(r92su, join_bss->bss.bssid, BSS_MACID, le32_to_cpu(join_bss->aid), GFP_KERNEL); if (!sta) goto report_cfg80211; resp_ie = join_bss->bss.ies.ie; resp_ie_len = le32_to_cpu(join_bss->bss.ie_length) - 12; sta->enc_sta = le32_to_cpu(join_bss->bss.privacy) ? true : false; sta->qos_sta = r92su_parse_wmm_cap_ie(r92su, resp_ie, resp_ie_len); /* The 802.11-2012 spec says that a HT STA has to be QoS STA * as well. So in theory we should do instead: * sta->qos_sta |= sta->ht_sta; * However, the QoS parameters are needed for legacy STAs as * well. Therefore, there's no excuse for a HT STA to forget * the WMM IE! */ if (sta->qos_sta) sta->ht_sta = r92su_parse_ht_cap_ie(r92su, resp_ie, resp_ie_len); status = WLAN_STATUS_SUCCESS; bss_priv->sta = sta; rcu_assign_pointer(r92su->connect_bss, cfg_bss); r92su->want_connect_bss = NULL; r92su_set_state(r92su, R92SU_CONNECTED); } report_cfg80211: switch (r92su->wdev.iftype) { case NL80211_IFTYPE_STATION: cfg80211_connect_result(r92su->wdev.netdev, join_bss->bss.bssid, bss_priv->assoc_ie, bss_priv->assoc_ie_len, resp_ie, resp_ie_len, status, GFP_KERNEL); if (status == WLAN_STATUS_SUCCESS) r92su_set_power(r92su, true); break; case NL80211_IFTYPE_ADHOC: if (status == WLAN_STATUS_SUCCESS) { cfg80211_ibss_joined(r92su->wdev.netdev, join_bss->bss.bssid, cfg_bss->channel, GFP_KERNEL); } break; default: WARN(1, "unsupported network type %d\n", r92su->wdev.iftype); break; } kfree(bss_priv->assoc_ie); bss_priv->assoc_ie = NULL; out: mutex_unlock(&r92su->lock); kfree(join_bss); if (status == WLAN_STATUS_SUCCESS) { netif_tx_start_all_queues(r92su->wdev.netdev); netif_carrier_on(r92su->wdev.netdev); } else { r92su_bss_free(r92su, cfg_bss); } }
static void r92su_survey_done_work(struct work_struct *work) { struct cfg80211_scan_request *req; struct r92su *r92su = container_of(work, struct r92su, survey_done_work.work); mutex_lock(&r92su->lock); if (!r92su_is_open(r92su)) goto out; req = r92su->scan_request; r92su->scan_request = NULL; if (req) { struct cfg80211_scan_info info = { .aborted = false, }; cfg80211_scan_done(req, &info); } r92su->scanned = true; complete(&r92su->scan_done); out: mutex_unlock(&r92su->lock); } static int r92su_stop(struct net_device *ndev) { struct r92su *r92su = ndev->ml_priv; struct cfg80211_bss *tmp_bss; struct llist_node *node; int err = -EINVAL, i; mutex_lock(&r92su->lock); if (r92su_is_connected(r92su)) { err = __r92su_disconnect(r92su); WARN_ONCE(err, "disconnect failed"); } r92su_set_power(r92su, false); if (r92su_is_initializing(r92su)) { err = r92su_hw_mac_deinit(r92su); WARN_ONCE(err, "failed to deinitilize MAC"); } if (r92su_is_initializing(r92su)) r92su_set_state(r92su, R92SU_STOP); if (r92su->scan_request) { struct cfg80211_scan_info info = { .aborted = true, }; cfg80211_scan_done(r92su->scan_request, &info); } tmp_bss = r92su->want_connect_bss; r92su->want_connect_bss = NULL; r92su_bss_free(r92su, tmp_bss); r92su->scan_request = NULL; for (i = 0; i < MAX_STA; i++) r92su_sta_del(r92su, i); mutex_unlock(&r92su->lock); cancel_delayed_work_sync(&r92su->survey_done_work); cancel_delayed_work_sync(&r92su->service_work); cancel_work_sync(&r92su->add_bss_work); cancel_work_sync(&r92su->connect_bss_work); cancel_work_sync(&r92su->disconnect_work); node = llist_del_all(&r92su->add_bss_list); while (node) { struct r92su_add_bss *bss_priv = llist_entry(node, struct r92su_add_bss, head); node = ACCESS_ONCE(node->next); kfree(bss_priv); } /* wait for keys and stas to be freed */ synchronize_rcu(); rcu_barrier(); return err; } static netdev_tx_t r92su_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct r92su *r92su = ndev->ml_priv; switch (r92su->wdev.iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: if (skb->len >= ETH_ALEN + ETH_ALEN + 2) r92su_tx(r92su, skb, false); break; case NL80211_IFTYPE_MONITOR: r92su_tx_monitor(r92su, skb); break; default: dev_kfree_skb_any(skb); break; } return NETDEV_TX_OK; } static const struct net_device_ops r92su_netdevice_ops = { .ndo_open = r92su_open, .ndo_stop = r92su_stop, .ndo_start_xmit = r92su_start_xmit, .ndo_set_mac_address = eth_mac_addr, .ndo_set_rx_mode = r92su_set_rx_mode, .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; static void *devm_dup(struct device *dev, void *src, size_t len) { void *tmp; tmp = devm_kzalloc(dev, len, GFP_KERNEL); if (tmp) memcpy(tmp, src, len); return tmp; } static int r92su_init_band(struct r92su *r92su) { struct ieee80211_supported_band *band; band = &r92su->band_2GHZ; band->channels = devm_dup(&r92su->wdev.wiphy->dev, r92su_channeltable, sizeof(r92su_channeltable)); if (!band->channels) return -ENOMEM; band->bitrates = devm_dup(&r92su->wdev.wiphy->dev, r92su_ratetable, sizeof(r92su_ratetable)); if (!band->bitrates) return -ENOMEM; band->n_channels = ARRAY_SIZE(r92su_channeltable); band->n_bitrates = ARRAY_SIZE(r92su_ratetable); memcpy(&band->ht_cap, &r92su_ht_info, sizeof(r92su_ht_info)); band->ht_cap.ht_supported = !r92su->disable_ht; switch (r92su->rf_type) { case R92SU_1T1R: /* nothing needs to be done. The default ht_cap * contains all the necessary bits for just 1T1R * devices */ break; case R92SU_1T2R: case R92SU_2T2R: band->ht_cap.mcs.rx_mask[1] = 0xff; band->ht_cap.mcs.rx_highest = cpu_to_le16(300); break; } r92su->wdev.wiphy->bands[NL80211_BAND_2GHZ] = &r92su->band_2GHZ; return 0; } static const struct ieee80211_txrx_stypes r92su_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_ADHOC] = { .tx = 0xffff, .rx = 0, },