static int r92su_get_station(struct wiphy *wiphy, struct net_device *ndev, const u8 *mac, struct station_info *sinfo) { struct r92su *r92su = wiphy_priv(wiphy); struct r92su_sta *sta; int err = -ENOENT; mutex_lock(&r92su->lock); if (!r92su_is_connected(r92su)) { err = -ENODEV; goto out; } rcu_read_lock(); sta = r92su_sta_get(r92su, mac); if (sta) { r92su_sta_set_sinfo(r92su, sta, sinfo); err = 0; } rcu_read_unlock(); out: mutex_unlock(&r92su->lock); return err; }
static int r92su_set_default_key(struct wiphy *wiphy, struct net_device *ndev, u8 idx, bool unicast, bool multicast) { struct r92su *r92su = wiphy_priv(wiphy); struct cfg80211_bss *bss; struct r92su_bss_priv *bss_priv; int err = -EAGAIN; mutex_lock(&r92su->lock); if (!r92su_is_connected(r92su)) goto out; rcu_read_lock(); bss = rcu_dereference(r92su->connect_bss); if (!bss) goto out_rcu; bss_priv = r92su_get_bss_priv(bss); if (unicast) bss_priv->def_uni_key_idx = idx; if (multicast) bss_priv->def_multi_key_idx = idx; err = 0; out_rcu: rcu_read_unlock(); out: mutex_unlock(&r92su->lock); return err; }
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 int __r92su_disconnect(struct r92su *r92su) { int err = 0; if (r92su_is_connected(r92su)) err = r92su_h2c_disconnect(r92su); /* always free the connected bss */ r92su_bss_free_connected(r92su, true); return err; }
static int r92su_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 idx, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct r92su *r92su = wiphy_priv(wiphy); int err; struct cfg80211_bss *bss; mutex_lock(&r92su->lock); if (!r92su_is_connected(r92su)) { err = -EAGAIN; goto out_unlock; } bss = rcu_dereference_protected(r92su->connect_bss, lockdep_is_held(&r92su->lock)); err = r92su_internal_add_key(r92su, bss, params->cipher, idx, pairwise, mac_addr, params->key, false); out_unlock: mutex_unlock(&r92su->lock); 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 int r92su_del_key(struct wiphy *wiphy, struct net_device *ndev, u8 idx, bool pairwise, const u8 *mac_addr) { static const enum r92su_enc_alg no_key = NO_ENCRYPTION; struct r92su *r92su = wiphy_priv(wiphy); struct r92su_key *old_key = NULL; int err = -EAGAIN; mutex_lock(&r92su->lock); if (!r92su_is_connected(r92su)) goto out; if (pairwise) { struct r92su_sta *sta; rcu_read_lock(); sta = r92su_sta_get(r92su, mac_addr); if (sta) { old_key = rcu_dereference(sta->sta_key); /* check if key was uploaded ? */ if (old_key && old_key->uploaded == false) { err = 0; goto out_free; } } rcu_read_unlock(); err = r92su_h2c_set_sta_key(r92su, no_key, mac_addr, NULL); if (err) goto out; rcu_read_lock(); sta = r92su_sta_get(r92su, mac_addr); if (!sta) goto out_free; old_key = rcu_dereference(sta->sta_key); if (old_key) { WARN(!old_key->uploaded, "pairwise-key for station %pM was ninja-deleted", mac_addr); old_key->uploaded = false; } rcu_assign_pointer(sta->sta_key, NULL); } else { struct cfg80211_bss *bss; struct r92su_bss_priv *bss_priv; rcu_read_lock(); bss = rcu_dereference(r92su->connect_bss); if (bss) { bss_priv = r92su_get_bss_priv(bss); old_key = rcu_dereference(bss_priv->group_key[idx]); if (old_key && old_key->uploaded == false) { err = 0; goto out_free; } } rcu_read_unlock(); err = r92su_h2c_set_key(r92su, no_key, idx, !pairwise, NULL); if (err) goto out; rcu_read_lock(); bss = rcu_dereference(r92su->connect_bss); if (bss) { bss_priv = r92su_get_bss_priv(bss); old_key = rcu_dereference(bss_priv->group_key[idx]); if (old_key) old_key->uploaded = false; rcu_assign_pointer(bss_priv->group_key[idx], NULL); } else { /* BSS which held the key is already gone! */ } } out_free: r92su_key_free(old_key); rcu_read_unlock(); out: mutex_unlock(&r92su->lock); return err; }
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, },