]> Pileus Git - ~andy/linux/blobdiff - drivers/net/wireless/rndis_wlan.c
rndis_wlan: disable IWEVPMKIDCAND wireless event
[~andy/linux] / drivers / net / wireless / rndis_wlan.c
index ffb195dcf4f84d40c3eb864ba0220157749f2afd..9d9750dbec84e2ec91cc9717e6463abaacd10b85 100644 (file)
@@ -424,7 +424,7 @@ static const u32 rndis_cipher_suites[] = {
 
 struct rndis_wlan_encr_key {
        int len;
-       int cipher;
+       u32 cipher;
        u8 material[32];
        u8 bssid[ETH_ALEN];
        bool pairwise;
@@ -440,21 +440,18 @@ struct rndis_wlan_private {
        struct cfg80211_scan_request *scan_request;
 
        struct workqueue_struct *workqueue;
-       struct delayed_work stats_work;
+       struct delayed_work dev_poller_work;
        struct delayed_work scan_work;
        struct work_struct work;
        struct mutex command_lock;
-       spinlock_t stats_lock;
        unsigned long work_pending;
+       int last_qual;
 
        struct ieee80211_supported_band band;
        struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)];
        struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)];
        u32 cipher_suites[ARRAY_SIZE(rndis_cipher_suites)];
 
-       struct iw_statistics iwstats;
-       struct iw_statistics privstats;
-
        int caps;
        int multicast_size;
 
@@ -472,6 +469,7 @@ struct rndis_wlan_private {
        int radio_on;
        int infra_mode;
        bool connected;
+       u8 bssid[ETH_ALEN];
        struct ndis_80211_ssid essid;
        __le32 current_command_oid;
 
@@ -520,6 +518,22 @@ static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev);
 static int rndis_set_channel(struct wiphy *wiphy,
        struct ieee80211_channel *chan, enum nl80211_channel_type channel_type);
 
+static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
+                                       u8 key_index, const u8 *mac_addr,
+                                       struct key_params *params);
+
+static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev,
+                                       u8 key_index, const u8 *mac_addr);
+
+static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+                                                               u8 key_index);
+
+static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev,
+                                       u8 *mac, struct station_info *sinfo);
+
+static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                              int idx, u8 *mac, struct station_info *sinfo);
+
 static struct cfg80211_ops rndis_config_ops = {
        .change_virtual_intf = rndis_change_virtual_intf,
        .scan = rndis_scan,
@@ -531,6 +545,11 @@ static struct cfg80211_ops rndis_config_ops = {
        .join_ibss = rndis_join_ibss,
        .leave_ibss = rndis_leave_ibss,
        .set_channel = rndis_set_channel,
+       .add_key = rndis_add_key,
+       .del_key = rndis_del_key,
+       .set_default_key = rndis_set_default_key,
+       .get_station = rndis_get_station,
+       .dump_station = rndis_dump_station,
 };
 
 static void *rndis_wiphy_privid = &rndis_wiphy_privid;
@@ -1258,7 +1277,10 @@ static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len,
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ndis_80211_wep_key ndis_key;
-       int cipher, ret;
+       u32 cipher;
+       int ret;
+
+       devdbg(usbdev, "add_wep_key(idx: %d, len: %d)", index, key_len);
 
        if ((key_len != 5 && key_len != 13) || index < 0 || index > 3)
                return -EINVAL;
@@ -1302,8 +1324,8 @@ static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len,
 
 
 static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
-                       int index, const u8 *addr, const u8 *rx_seq, int cipher,
-                       int flags)
+                       int index, const u8 *addr, const u8 *rx_seq,
+                       int seq_len, u32 cipher, int flags)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ndis_80211_key ndis_key;
@@ -1319,10 +1341,18 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
                        key_len);
                return -EINVAL;
        }
-       if ((flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) && !rx_seq) {
-               devdbg(usbdev, "add_wpa_key: recv seq flag without buffer");
-               return -EINVAL;
+       if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) {
+               if (!rx_seq || seq_len <= 0) {
+                       devdbg(usbdev, "add_wpa_key: recv seq flag without"
+                                       "buffer");
+                       return -EINVAL;
+               }
+               if (rx_seq && seq_len > sizeof(ndis_key.rsc)) {
+                       devdbg(usbdev, "add_wpa_key: too big recv seq buffer");
+                       return -EINVAL;
+               }
        }
+
        is_addr_ok = addr && !is_zero_ether_addr(addr) &&
                                        !is_broadcast_ether_addr(addr);
        if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !is_addr_ok) {
@@ -1353,7 +1383,7 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
                memcpy(ndis_key.material, key, key_len);
 
        if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ)
-               memcpy(ndis_key.rsc, rx_seq, 6);
+               memcpy(ndis_key.rsc, rx_seq, seq_len);
 
        if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) {
                /* pairwise key */
@@ -1392,31 +1422,17 @@ static int restore_key(struct usbnet *usbdev, int key_idx)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct rndis_wlan_encr_key key;
-       int flags;
+
+       if (is_wpa_key(priv, key_idx))
+               return 0;
 
        key = priv->encr_keys[key_idx];
 
-       devdbg(usbdev, "restore_key: %i:%s:%i", key_idx,
-               is_wpa_key(priv, key_idx) ? "wpa" : "wep",
-               key.len);
+       devdbg(usbdev, "restore_key: %i:%i", key_idx, key.len);
 
        if (key.len == 0)
                return 0;
 
-       if (is_wpa_key(priv, key_idx)) {
-               flags = 0;
-
-               /*if (priv->encr_tx_key_index == key_idx)
-                       flags |= NDIS_80211_ADDKEY_TRANSMIT_KEY;*/
-
-               if (!is_zero_ether_addr(key.bssid) &&
-                               !is_broadcast_ether_addr(key.bssid))
-                       flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY;
-
-               return add_wpa_key(usbdev, key.material, key.len, key_idx,
-                                       key.bssid, NULL, key.cipher, flags);
-       }
-
        return add_wep_key(usbdev, key.material, key.len, key_idx);
 }
 
@@ -1437,7 +1453,7 @@ static void clear_key(struct rndis_wlan_private *priv, int idx)
 
 
 /* remove_key is for both wep and wpa */
-static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
+static int remove_key(struct usbnet *usbdev, int index, const u8 *bssid)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ndis_80211_remove_key remove_key;
@@ -1921,6 +1937,7 @@ static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev,
        devdbg(usbdev, "cfg80211.disconnect(%d)", reason_code);
 
        priv->connected = false;
+       memset(priv->bssid, 0, ETH_ALEN);
 
        return deauthenticate(usbdev);
 }
@@ -2027,6 +2044,7 @@ static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
        devdbg(usbdev, "cfg80211.leave_ibss()");
 
        priv->connected = false;
+       memset(priv->bssid, 0, ETH_ALEN);
 
        return deauthenticate(usbdev);
 }
@@ -2041,17 +2059,126 @@ static int rndis_set_channel(struct wiphy *wiphy,
                        ieee80211_frequency_to_channel(chan->center_freq));
 }
 
-/*
- * wireless extension handlers
- */
+static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
+                                       u8 key_index, const u8 *mac_addr,
+                                       struct key_params *params)
+{
+       struct rndis_wlan_private *priv = wiphy_priv(wiphy);
+       struct usbnet *usbdev = priv->usbdev;
+       int flags;
 
-static int rndis_iw_commit(struct net_device *dev,
-    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+       devdbg(usbdev, "rndis_add_key(%i, %pM, %08x)", key_index, mac_addr,
+                                                       params->cipher);
+
+       switch (params->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               return add_wep_key(usbdev, params->key, params->key_len,
+                                                               key_index);
+       case WLAN_CIPHER_SUITE_TKIP:
+       case WLAN_CIPHER_SUITE_CCMP:
+               flags = 0;
+
+               if (params->seq && params->seq_len > 0)
+                       flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ;
+               if (mac_addr)
+                       flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY |
+                                       NDIS_80211_ADDKEY_TRANSMIT_KEY;
+
+               return add_wpa_key(usbdev, params->key, params->key_len,
+                               key_index, mac_addr, params->seq,
+                               params->seq_len, params->cipher, flags);
+       default:
+               devdbg(usbdev, "rndis_add_key: unsupported cipher %08x",
+                                                       params->cipher);
+               return -ENOTSUPP;
+       }
+}
+
+static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev,
+                                       u8 key_index, const u8 *mac_addr)
+{
+       struct rndis_wlan_private *priv = wiphy_priv(wiphy);
+       struct usbnet *usbdev = priv->usbdev;
+
+       devdbg(usbdev, "rndis_del_key(%i, %pM)", key_index, mac_addr);
+
+       return remove_key(usbdev, key_index, mac_addr);
+}
+
+static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+                                                               u8 key_index)
+{
+       struct rndis_wlan_private *priv = wiphy_priv(wiphy);
+       struct usbnet *usbdev = priv->usbdev;
+       struct rndis_wlan_encr_key key;
+
+       devdbg(usbdev, "rndis_set_default_key(%i)", key_index);
+
+       priv->encr_tx_key_index = key_index;
+
+       key = priv->encr_keys[key_index];
+
+       return add_wep_key(usbdev, key.material, key.len, key_index);
+}
+
+static void rndis_fill_station_info(struct usbnet *usbdev,
+                                               struct station_info *sinfo)
+{
+       __le32 linkspeed, rssi;
+       int ret, len;
+
+       memset(sinfo, 0, sizeof(*sinfo));
+
+       len = sizeof(linkspeed);
+       ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &linkspeed, &len);
+       if (ret == 0) {
+               sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000;
+               sinfo->filled |= STATION_INFO_TX_BITRATE;
+       }
+
+       len = sizeof(rssi);
+       ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len);
+       if (ret == 0) {
+               sinfo->signal = level_to_qual(le32_to_cpu(rssi));
+               sinfo->filled |= STATION_INFO_SIGNAL;
+       }
+}
+
+static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev,
+                                       u8 *mac, struct station_info *sinfo)
+{
+       struct rndis_wlan_private *priv = wiphy_priv(wiphy);
+       struct usbnet *usbdev = priv->usbdev;
+
+       if (compare_ether_addr(priv->bssid, mac))
+               return -ENOENT;
+
+       rndis_fill_station_info(usbdev, sinfo);
+
+       return 0;
+}
+
+static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                              int idx, u8 *mac, struct station_info *sinfo)
 {
-       /* dummy op */
+       struct rndis_wlan_private *priv = wiphy_priv(wiphy);
+       struct usbnet *usbdev = priv->usbdev;
+
+       if (idx != 0)
+               return -ENOENT;
+
+       memcpy(mac, priv->bssid, ETH_ALEN);
+
+       rndis_fill_station_info(usbdev, sinfo);
+
        return 0;
 }
 
+/*
+ * wireless extension handlers
+ */
+
 #if 0
 /* Commented code out instead of removing to have more sane patch for review.
  * Will be removed later in the set.
@@ -2268,7 +2395,6 @@ static int rndis_iw_get_auth(struct net_device *dev,
        }
        return 0;
 }
-#endif
 
 
 static int rndis_iw_set_encode(struct net_device *dev,
@@ -2419,12 +2545,12 @@ static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev)
 
        return &priv->iwstats;
 }
+#endif
 
 
 #define IW_IOCTL(x) [(x) - SIOCSIWCOMMIT]
 static const iw_handler rndis_iw_handler[] =
 {
-       IW_IOCTL(SIOCSIWCOMMIT)    = rndis_iw_commit,
        IW_IOCTL(SIOCGIWNAME)      = (iw_handler) cfg80211_wext_giwname,
        IW_IOCTL(SIOCSIWFREQ)      = (iw_handler) cfg80211_wext_siwfreq,
        IW_IOCTL(SIOCGIWFREQ)      = (iw_handler) cfg80211_wext_giwfreq,
@@ -2437,15 +2563,15 @@ static const iw_handler rndis_iw_handler[] =
        IW_IOCTL(SIOCGIWSCAN)      = (iw_handler) cfg80211_wext_giwscan,
        IW_IOCTL(SIOCSIWESSID)     = (iw_handler) cfg80211_wext_siwessid,
        IW_IOCTL(SIOCGIWESSID)     = (iw_handler) cfg80211_wext_giwessid,
-       IW_IOCTL(SIOCGIWRATE)      = rndis_iw_get_rate,
+       IW_IOCTL(SIOCGIWRATE)      = (iw_handler) cfg80211_wext_giwrate,
        IW_IOCTL(SIOCSIWRTS)       = (iw_handler) cfg80211_wext_siwrts,
        IW_IOCTL(SIOCGIWRTS)       = (iw_handler) cfg80211_wext_giwrts,
        IW_IOCTL(SIOCSIWFRAG)      = (iw_handler) cfg80211_wext_siwfrag,
        IW_IOCTL(SIOCGIWFRAG)      = (iw_handler) cfg80211_wext_giwfrag,
        IW_IOCTL(SIOCSIWTXPOW)     = (iw_handler) cfg80211_wext_siwtxpower,
        IW_IOCTL(SIOCGIWTXPOW)     = (iw_handler) cfg80211_wext_giwtxpower,
-       IW_IOCTL(SIOCSIWENCODE)    = rndis_iw_set_encode,
-       IW_IOCTL(SIOCSIWENCODEEXT) = rndis_iw_set_encode_ext,
+       IW_IOCTL(SIOCSIWENCODE)    = (iw_handler) cfg80211_wext_siwencode,
+       IW_IOCTL(SIOCSIWENCODEEXT) = (iw_handler) cfg80211_wext_siwencodeext,
        IW_IOCTL(SIOCSIWAUTH)      = (iw_handler) cfg80211_wext_siwauth,
        IW_IOCTL(SIOCGIWAUTH)      = (iw_handler) cfg80211_wext_giwauth,
        IW_IOCTL(SIOCSIWGENIE)     = (iw_handler) cfg80211_wext_siwgenie,
@@ -2466,7 +2592,7 @@ static const struct iw_handler_def rndis_iw_handlers = {
        .standard = (iw_handler *)rndis_iw_handler,
        .private  = (iw_handler *)rndis_wlan_private_handler,
        .private_args = (struct iw_priv_args *)rndis_wlan_private_args,
-       .get_wireless_stats = rndis_get_wireless_stats,
+       .get_wireless_stats = cfg80211_wireless_stats,
 };
 
 
@@ -2541,6 +2667,7 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
                cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
 
        priv->connected = true;
+       memcpy(priv->bssid, bssid, ETH_ALEN);
 
        usbnet_resume_rx(usbdev);
        netif_carrier_on(usbdev->net);
@@ -2593,9 +2720,10 @@ static void rndis_wlan_auth_indication(struct usbnet *usbdev,
 {
        u8 *buf;
        const char *type;
-       int flags, buflen;
+       int flags, buflen, key_id;
        bool pairwise_error, group_error;
        struct ndis_80211_auth_request *auth_req;
+       enum nl80211_key_type key_type;
 
        /* must have at least one array entry */
        if (len < offsetof(struct ndis_80211_status_indication, u) +
@@ -2631,23 +2759,24 @@ static void rndis_wlan_auth_indication(struct usbnet *usbdev,
                devinfo(usbdev, "authentication indication: %s (0x%08x)", type,
                                le32_to_cpu(auth_req->flags));
 
-               if (pairwise_error || group_error) {
-                       union iwreq_data wrqu;
-                       struct iw_michaelmicfailure micfailure;
+               if (pairwise_error) {
+                       key_type = NL80211_KEYTYPE_PAIRWISE;
+                       key_id = -1;
 
-                       memset(&micfailure, 0, sizeof(micfailure));
-                       if (pairwise_error)
-                               micfailure.flags |= IW_MICFAILURE_PAIRWISE;
-                       if (group_error)
-                               micfailure.flags |= IW_MICFAILURE_GROUP;
+                       cfg80211_michael_mic_failure(usbdev->net,
+                                                       auth_req->bssid,
+                                                       key_type, key_id, NULL,
+                                                       GFP_KERNEL);
+               }
 
-                       memcpy(micfailure.src_addr.sa_data, auth_req->bssid,
-                               ETH_ALEN);
+               if (group_error) {
+                       key_type = NL80211_KEYTYPE_GROUP;
+                       key_id = -1;
 
-                       memset(&wrqu, 0, sizeof(wrqu));
-                       wrqu.data.length = sizeof(micfailure);
-                       wireless_send_event(usbdev->net, IWEVMICHAELMICFAILURE,
-                                               &wrqu, (u8 *)&micfailure);
+                       cfg80211_michael_mic_failure(usbdev->net,
+                                                       auth_req->bssid,
+                                                       key_type, key_id, NULL,
+                                                       GFP_KERNEL);
                }
 
                buflen -= le32_to_cpu(auth_req->length);
@@ -2692,14 +2821,16 @@ static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev,
                return;
 
        for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) {
-               struct iw_pmkid_cand pcand;
-               union iwreq_data wrqu;
                struct ndis_80211_pmkid_candidate *cand =
                                                &cand_list->candidate_list[i];
 
                devdbg(usbdev, "cand[%i]: flags: 0x%08x, bssid: %pM",
                                i, le32_to_cpu(cand->flags), cand->bssid);
 
+#if 0
+               struct iw_pmkid_cand pcand;
+               union iwreq_data wrqu;
+
                memset(&pcand, 0, sizeof(pcand));
                if (le32_to_cpu(cand->flags) & 0x01)
                        pcand.flags |= IW_PMKID_CAND_PREAUTH;
@@ -2710,6 +2841,7 @@ static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev,
                wrqu.data.length = sizeof(pcand);
                wireless_send_event(usbdev->net, IWEVPMKIDCAND, &wrqu,
                                                                (u8 *)&pcand);
+#endif
        }
 }
 
@@ -2849,77 +2981,44 @@ static int rndis_wlan_get_caps(struct usbnet *usbdev)
 }
 
 
-#define STATS_UPDATE_JIFFIES (HZ)
-static void rndis_update_wireless_stats(struct work_struct *work)
+#define DEVICE_POLLER_JIFFIES (HZ)
+static void rndis_device_poller(struct work_struct *work)
 {
        struct rndis_wlan_private *priv =
-               container_of(work, struct rndis_wlan_private, stats_work.work);
+               container_of(work, struct rndis_wlan_private,
+                                                       dev_poller_work.work);
        struct usbnet *usbdev = priv->usbdev;
-       struct iw_statistics iwstats;
        __le32 rssi, tmp;
        int len, ret, j;
-       unsigned long flags;
-       int update_jiffies = STATS_UPDATE_JIFFIES;
+       int update_jiffies = DEVICE_POLLER_JIFFIES;
        void *buf;
 
-       spin_lock_irqsave(&priv->stats_lock, flags);
-       memcpy(&iwstats, &priv->privstats, sizeof(iwstats));
-       spin_unlock_irqrestore(&priv->stats_lock, flags);
-
-       /* only update stats when connected */
-       if (!is_associated(usbdev)) {
-               iwstats.qual.qual = 0;
-               iwstats.qual.level = 0;
-               iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
-                               | IW_QUAL_LEVEL_UPDATED
-                               | IW_QUAL_NOISE_INVALID
-                               | IW_QUAL_QUAL_INVALID
-                               | IW_QUAL_LEVEL_INVALID;
+       /* Only check/do workaround when connected. Calling is_associated()
+        * also polls device with rndis_command() and catches for media link
+        * indications.
+        */
+       if (!is_associated(usbdev))
                goto end;
-       }
 
        len = sizeof(rssi);
        ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len);
-
-       devdbg(usbdev, "stats: OID_802_11_RSSI -> %d, rssi:%d", ret,
-                                                       le32_to_cpu(rssi));
-       if (ret == 0) {
-               memset(&iwstats.qual, 0, sizeof(iwstats.qual));
-               iwstats.qual.qual  = level_to_qual(le32_to_cpu(rssi));
-               iwstats.qual.level = level_to_qual(le32_to_cpu(rssi));
-               iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
-                               | IW_QUAL_LEVEL_UPDATED
-                               | IW_QUAL_NOISE_INVALID;
-       }
-
-       memset(&iwstats.discard, 0, sizeof(iwstats.discard));
-
-       len = sizeof(tmp);
-       ret = rndis_query_oid(usbdev, OID_GEN_XMIT_ERROR, &tmp, &len);
        if (ret == 0)
-               iwstats.discard.misc += le32_to_cpu(tmp);
+               priv->last_qual = level_to_qual(le32_to_cpu(rssi));
 
-       len = sizeof(tmp);
-       ret = rndis_query_oid(usbdev, OID_GEN_RCV_ERROR, &tmp, &len);
-       if (ret == 0)
-               iwstats.discard.misc += le32_to_cpu(tmp);
-
-       len = sizeof(tmp);
-       ret = rndis_query_oid(usbdev, OID_GEN_RCV_NO_BUFFER, &tmp, &len);
-       if (ret == 0)
-               iwstats.discard.misc += le32_to_cpu(tmp);
+       devdbg(usbdev, "dev-poller: OID_802_11_RSSI -> %d, rssi:%d, qual: %d",
+               ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi)));
 
        /* Workaround transfer stalls on poor quality links.
         * TODO: find right way to fix these stalls (as stalls do not happen
         * with ndiswrapper/windows driver). */
-       if (iwstats.qual.qual <= 25) {
+       if (priv->last_qual <= 25) {
                /* Decrease stats worker interval to catch stalls.
                 * faster. Faster than 400-500ms causes packet loss,
                 * Slower doesn't catch stalls fast enough.
                 */
                j = msecs_to_jiffies(priv->param_workaround_interval);
-               if (j > STATS_UPDATE_JIFFIES)
-                       j = STATS_UPDATE_JIFFIES;
+               if (j > DEVICE_POLLER_JIFFIES)
+                       j = DEVICE_POLLER_JIFFIES;
                else if (j <= 0)
                        j = 1;
                update_jiffies = j;
@@ -2939,11 +3038,8 @@ static void rndis_update_wireless_stats(struct work_struct *work)
                rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
                kfree(buf);
        }
-end:
-       spin_lock_irqsave(&priv->stats_lock, flags);
-       memcpy(&priv->privstats, &iwstats, sizeof(iwstats));
-       spin_unlock_irqrestore(&priv->stats_lock, flags);
 
+end:
        if (update_jiffies >= HZ)
                update_jiffies = round_jiffies_relative(update_jiffies);
        else {
@@ -2952,7 +3048,8 @@ end:
                        update_jiffies = j;
        }
 
-       queue_delayed_work(priv->workqueue, &priv->stats_work, update_jiffies);
+       queue_delayed_work(priv->workqueue, &priv->dev_poller_work,
+                                                               update_jiffies);
 }
 
 
@@ -3073,12 +3170,11 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)
        priv->usbdev = usbdev;
 
        mutex_init(&priv->command_lock);
-       spin_lock_init(&priv->stats_lock);
 
        /* because rndis_command() sleeps we need to use workqueue */
        priv->workqueue = create_singlethread_workqueue("rndis_wlan");
        INIT_WORK(&priv->work, rndis_wlan_worker);
-       INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
+       INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller);
        INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results);
 
        /* try bind rndis_host */
@@ -3110,14 +3206,6 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)
        else
                usbdev->net->flags &= ~IFF_MULTICAST;
 
-       priv->iwstats.qual.qual = 0;
-       priv->iwstats.qual.level = 0;
-       priv->iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
-                                       | IW_QUAL_LEVEL_UPDATED
-                                       | IW_QUAL_NOISE_INVALID
-                                       | IW_QUAL_QUAL_INVALID
-                                       | IW_QUAL_LEVEL_INVALID;
-
        /* fill-out wiphy structure and register w/ cfg80211 */
        memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN);
        wiphy->privid = rndis_wiphy_privid;
@@ -3163,7 +3251,7 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)
        return 0;
 
 fail:
-       cancel_delayed_work_sync(&priv->stats_work);
+       cancel_delayed_work_sync(&priv->dev_poller_work);
        cancel_delayed_work_sync(&priv->scan_work);
        cancel_work_sync(&priv->work);
        flush_workqueue(priv->workqueue);
@@ -3181,7 +3269,7 @@ static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf)
        /* turn radio off */
        disassociate(usbdev, 0);
 
-       cancel_delayed_work_sync(&priv->stats_work);
+       cancel_delayed_work_sync(&priv->dev_poller_work);
        cancel_delayed_work_sync(&priv->scan_work);
        cancel_work_sync(&priv->work);
        flush_workqueue(priv->workqueue);
@@ -3212,8 +3300,8 @@ static int rndis_wlan_reset(struct usbnet *usbdev)
           (set_multicast_list() also turns on current packet filter) */
        set_multicast_list(usbdev);
 
-       queue_delayed_work(priv->workqueue, &priv->stats_work,
-               round_jiffies_relative(STATS_UPDATE_JIFFIES));
+       queue_delayed_work(priv->workqueue, &priv->dev_poller_work,
+               round_jiffies_relative(DEVICE_POLLER_JIFFIES));
 
        return deauthenticate(usbdev);
 }
@@ -3230,7 +3318,7 @@ static int rndis_wlan_stop(struct usbnet *usbdev)
        retval = disassociate(usbdev, 0);
 
        priv->work_pending = 0;
-       cancel_delayed_work_sync(&priv->stats_work);
+       cancel_delayed_work_sync(&priv->dev_poller_work);
        cancel_delayed_work_sync(&priv->scan_work);
        cancel_work_sync(&priv->work);
        flush_workqueue(priv->workqueue);