net:wireless:bcmdhd: rename bcmsdh_remove & bcmsdh_probe
[linux-2.6.git] / drivers / net / wireless / rndis_wlan.c
index 518542b..d66e298 100644 (file)
 #include <linux/mii.h>
 #include <linux/usb.h>
 #include <linux/usb/cdc.h>
-#include <linux/wireless.h>
 #include <linux/ieee80211.h>
 #include <linux/if_arp.h>
 #include <linux/ctype.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
-#include <net/iw_handler.h>
 #include <net/cfg80211.h>
 #include <linux/usb/usbnet.h>
 #include <linux/usb/rndis_host.h>
@@ -246,6 +244,10 @@ enum ndis_80211_power_mode {
        NDIS_80211_POWER_MODE_FAST_PSP,
 };
 
+enum ndis_80211_pmkid_cand_list_flag_bits {
+       NDIS_80211_PMKID_CAND_PREAUTH = cpu_to_le32(1 << 0)
+};
+
 struct ndis_80211_auth_request {
        __le32 length;
        u8 bssid[6];
@@ -389,19 +391,17 @@ struct ndis_80211_capability {
 struct ndis_80211_bssid_info {
        u8 bssid[6];
        u8 pmkid[16];
-};
+} __packed;
 
 struct ndis_80211_pmkid {
        __le32 length;
        __le32 bssid_info_count;
        struct ndis_80211_bssid_info bssid_info[0];
-};
+} __packed;
 
 /*
  *  private data
  */
-#define NET_TYPE_11FB  0
-
 #define CAP_MODE_80211A                1
 #define CAP_MODE_80211B                2
 #define CAP_MODE_80211G                4
@@ -416,6 +416,7 @@ struct ndis_80211_pmkid {
 #define RNDIS_WLAN_ALG_TKIP    (1<<1)
 #define RNDIS_WLAN_ALG_CCMP    (1<<2)
 
+#define RNDIS_WLAN_NUM_KEYS            4
 #define RNDIS_WLAN_KEY_MGMT_NONE       0
 #define RNDIS_WLAN_KEY_MGMT_802_1X     (1<<0)
 #define RNDIS_WLAN_KEY_MGMT_PSK                (1<<1)
@@ -517,8 +518,8 @@ struct rndis_wlan_private {
        __le32 current_command_oid;
 
        /* encryption stuff */
-       int  encr_tx_key_index;
-       struct rndis_wlan_encr_key encr_keys[4];
+       u8 encr_tx_key_index;
+       struct rndis_wlan_encr_key encr_keys[RNDIS_WLAN_NUM_KEYS];
        int  wpa_version;
 
        u8 command_buffer[COMMAND_BUFFER_SIZE];
@@ -633,7 +634,7 @@ static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv)
        }
 }
 
-static bool is_wpa_key(struct rndis_wlan_private *priv, int idx)
+static bool is_wpa_key(struct rndis_wlan_private *priv, u8 idx)
 {
        int cipher = priv->encr_keys[idx].cipher;
 
@@ -1348,9 +1349,35 @@ static int set_channel(struct usbnet *usbdev, int channel)
        return ret;
 }
 
+static struct ieee80211_channel *get_current_channel(struct usbnet *usbdev,
+                                                    u32 *beacon_period)
+{
+       struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+       struct ieee80211_channel *channel;
+       struct ndis_80211_conf config;
+       int len, ret;
+
+       /* Get channel and beacon interval */
+       len = sizeof(config);
+       ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
+       netdev_dbg(usbdev->net, "%s(): OID_802_11_CONFIGURATION -> %d\n",
+                               __func__, ret);
+       if (ret < 0)
+               return NULL;
+
+       channel = ieee80211_get_channel(priv->wdev.wiphy,
+                               KHZ_TO_MHZ(le32_to_cpu(config.ds_config)));
+       if (!channel)
+               return NULL;
+
+       if (beacon_period)
+               *beacon_period = le32_to_cpu(config.beacon_period);
+       return channel;
+}
+
 /* index must be 0 - N, as per NDIS  */
 static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len,
-                                                               int index)
+                                                               u8 index)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ndis_80211_wep_key ndis_key;
@@ -1360,13 +1387,15 @@ static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len,
        netdev_dbg(usbdev->net, "%s(idx: %d, len: %d)\n",
                   __func__, index, key_len);
 
-       if ((key_len != 5 && key_len != 13) || index < 0 || index > 3)
+       if (index >= RNDIS_WLAN_NUM_KEYS)
                return -EINVAL;
 
        if (key_len == 5)
                cipher = WLAN_CIPHER_SUITE_WEP40;
-       else
+       else if (key_len == 13)
                cipher = WLAN_CIPHER_SUITE_WEP104;
+       else
+               return -EINVAL;
 
        memset(&ndis_key, 0, sizeof(ndis_key));
 
@@ -1401,7 +1430,7 @@ 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,
+                       u8 index, const u8 *addr, const u8 *rx_seq,
                        int seq_len, u32 cipher, __le32 flags)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
@@ -1409,7 +1438,7 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
        bool is_addr_ok;
        int ret;
 
-       if (index < 0 || index >= 4) {
+       if (index >= RNDIS_WLAN_NUM_KEYS) {
                netdev_dbg(usbdev->net, "%s(): index out of range (%i)\n",
                           __func__, index);
                return -EINVAL;
@@ -1497,7 +1526,7 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
        return 0;
 }
 
-static int restore_key(struct usbnet *usbdev, int key_idx)
+static int restore_key(struct usbnet *usbdev, u8 key_idx)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct rndis_wlan_encr_key key;
@@ -1523,13 +1552,13 @@ static void restore_keys(struct usbnet *usbdev)
                restore_key(usbdev, i);
 }
 
-static void clear_key(struct rndis_wlan_private *priv, int idx)
+static void clear_key(struct rndis_wlan_private *priv, u8 idx)
 {
        memset(&priv->encr_keys[idx], 0, sizeof(priv->encr_keys[idx]));
 }
 
 /* remove_key is for both wep and wpa */
-static int remove_key(struct usbnet *usbdev, int index, const u8 *bssid)
+static int remove_key(struct usbnet *usbdev, u8 index, const u8 *bssid)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ndis_80211_remove_key remove_key;
@@ -1537,6 +1566,9 @@ static int remove_key(struct usbnet *usbdev, int index, const u8 *bssid)
        bool is_wpa;
        int ret;
 
+       if (index >= RNDIS_WLAN_NUM_KEYS)
+               return -ENOENT;
+
        if (priv->encr_keys[index].len == 0)
                return 0;
 
@@ -1760,9 +1792,9 @@ static struct ndis_80211_pmkid *remove_pmkid(struct usbnet *usbdev,
                                                struct cfg80211_pmksa *pmksa,
                                                int max_pmkids)
 {
-       int i, len, count, newlen, err;
+       int i, newlen, err;
+       unsigned int count;
 
-       len = le32_to_cpu(pmkids->length);
        count = le32_to_cpu(pmkids->bssid_info_count);
 
        if (count > max_pmkids)
@@ -1801,9 +1833,9 @@ static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev,
                                                struct cfg80211_pmksa *pmksa,
                                                int max_pmkids)
 {
-       int i, err, len, count, newlen;
+       int i, err, newlen;
+       unsigned int count;
 
-       len = le32_to_cpu(pmkids->length);
        count = le32_to_cpu(pmkids->bssid_info_count);
 
        if (count > max_pmkids)
@@ -1974,11 +2006,12 @@ static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
        return ret;
 }
 
-static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,
-                                       struct ndis_80211_bssid_ex *bssid)
+static bool rndis_bss_info_update(struct usbnet *usbdev,
+                                 struct ndis_80211_bssid_ex *bssid)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ieee80211_channel *channel;
+       struct cfg80211_bss *bss;
        s32 signal;
        u64 timestamp;
        u16 capability;
@@ -2017,9 +2050,12 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,
        capability = le16_to_cpu(fixed->capabilities);
        beacon_interval = le16_to_cpu(fixed->beacon_interval);
 
-       return cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac,
+       bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac,
                timestamp, capability, beacon_interval, ie, ie_len, signal,
                GFP_KERNEL);
+       cfg80211_put_bss(bss);
+
+       return (bss != NULL);
 }
 
 static struct ndis_80211_bssid_ex *next_bssid_list_item(
@@ -2453,6 +2489,9 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
 
        netdev_dbg(usbdev->net, "%s(%i)\n", __func__, key_index);
 
+       if (key_index >= RNDIS_WLAN_NUM_KEYS)
+               return -ENOENT;
+
        priv->encr_tx_key_index = key_index;
 
        if (is_wpa_key(priv, key_index))
@@ -2641,12 +2680,12 @@ static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid,
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ieee80211_channel *channel;
-       struct ndis_80211_conf config;
        struct ndis_80211_ssid ssid;
+       struct cfg80211_bss *bss;
        s32 signal;
        u64 timestamp;
        u16 capability;
-       u16 beacon_interval;
+       u32 beacon_period = 0;
        __le32 rssi;
        u8 ie_buf[34];
        int len, ret, ie_len;
@@ -2671,22 +2710,10 @@ static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid,
        }
 
        /* Get channel and beacon interval */
-       len = sizeof(config);
-       ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
-       netdev_dbg(usbdev->net, "%s(): OID_802_11_CONFIGURATION -> %d\n",
-                               __func__, ret);
-       if (ret >= 0) {
-               beacon_interval = le16_to_cpu(config.beacon_period);
-               channel = ieee80211_get_channel(priv->wdev.wiphy,
-                               KHZ_TO_MHZ(le32_to_cpu(config.ds_config)));
-               if (!channel) {
-                       netdev_warn(usbdev->net, "%s(): could not get channel."
-                                                "\n", __func__);
-                       return;
-               }
-       } else {
-               netdev_warn(usbdev->net, "%s(): could not get configuration.\n",
-                                        __func__);
+       channel = get_current_channel(usbdev, &beacon_period);
+       if (!channel) {
+               netdev_warn(usbdev->net, "%s(): could not get channel.\n",
+                                       __func__);
                return;
        }
 
@@ -2713,12 +2740,13 @@ static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid,
        netdev_dbg(usbdev->net, "%s(): channel:%d(freq), bssid:[%pM], tsf:%d, "
                "capa:%x, beacon int:%d, resp_ie(len:%d, essid:'%.32s'), "
                "signal:%d\n", __func__, (channel ? channel->center_freq : -1),
-               bssid, (u32)timestamp, capability, beacon_interval, ie_len,
+               bssid, (u32)timestamp, capability, beacon_period, ie_len,
                ssid.essid, signal);
 
-       cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid,
-               timestamp, capability, beacon_interval, ie_buf, ie_len,
+       bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid,
+               timestamp, capability, beacon_period, ie_buf, ie_len,
                signal, GFP_KERNEL);
+       cfg80211_put_bss(bss);
 }
 
 /*
@@ -2729,9 +2757,10 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
        struct ndis_80211_assoc_info *info = NULL;
        u8 bssid[ETH_ALEN];
-       int resp_ie_len, req_ie_len;
+       unsigned int resp_ie_len, req_ie_len;
+       unsigned int offset;
        u8 *req_ie, *resp_ie;
-       int ret, offset;
+       int ret;
        bool roamed = false;
        bool match_bss;
 
@@ -2759,7 +2788,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
                ret = get_association_info(usbdev, info, CONTROL_BUFFER_SIZE);
                if (!ret) {
                        req_ie_len = le32_to_cpu(info->req_ie_length);
-                       if (req_ie_len > 0) {
+                       if (req_ie_len > CONTROL_BUFFER_SIZE)
+                               req_ie_len = CONTROL_BUFFER_SIZE;
+                       if (req_ie_len != 0) {
                                offset = le32_to_cpu(info->offset_req_ies);
 
                                if (offset > CONTROL_BUFFER_SIZE)
@@ -2773,7 +2804,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
                        }
 
                        resp_ie_len = le32_to_cpu(info->resp_ie_length);
-                       if (resp_ie_len > 0) {
+                       if (resp_ie_len > CONTROL_BUFFER_SIZE)
+                               resp_ie_len = CONTROL_BUFFER_SIZE;
+                       if (resp_ie_len != 0) {
                                offset = le32_to_cpu(info->offset_resp_ies);
 
                                if (offset > CONTROL_BUFFER_SIZE)
@@ -2830,7 +2863,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
                                                req_ie_len, resp_ie,
                                                resp_ie_len, 0, GFP_KERNEL);
                else
-                       cfg80211_roamed(usbdev->net, bssid, req_ie, req_ie_len,
+                       cfg80211_roamed(usbdev->net,
+                                       get_current_channel(usbdev, NULL),
+                                       bssid, req_ie, req_ie_len,
                                        resp_ie, resp_ie_len, GFP_KERNEL);
        } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
                cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
@@ -2996,25 +3031,13 @@ static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev,
        for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) {
                struct ndis_80211_pmkid_candidate *cand =
                                                &cand_list->candidate_list[i];
+               bool preauth = !!(cand->flags & NDIS_80211_PMKID_CAND_PREAUTH);
 
-               netdev_dbg(usbdev->net, "cand[%i]: flags: 0x%08x, bssid: %pM\n",
-                          i, le32_to_cpu(cand->flags), cand->bssid);
-
-#if 0
-               struct iw_pmkid_cand pcand;
-               union iwreq_data wrqu;
+               netdev_dbg(usbdev->net, "cand[%i]: flags: 0x%08x, preauth: %d, bssid: %pM\n",
+                          i, le32_to_cpu(cand->flags), preauth, cand->bssid);
 
-               memset(&pcand, 0, sizeof(pcand));
-               if (le32_to_cpu(cand->flags) & 0x01)
-                       pcand.flags |= IW_PMKID_CAND_PREAUTH;
-               pcand.index = i;
-               memcpy(pcand.bssid.sa_data, cand->bssid, ETH_ALEN);
-
-               memset(&wrqu, 0, sizeof(wrqu));
-               wrqu.data.length = sizeof(pcand);
-               wireless_send_event(usbdev->net, IWEVPMKIDCAND, &wrqu,
-                                                               (u8 *)&pcand);
-#endif
+               cfg80211_pmksa_candidate_notify(usbdev->net, i, cand->bssid,
+                                               preauth, GFP_ATOMIC);
        }
 }
 
@@ -3022,7 +3045,7 @@ static void rndis_wlan_media_specific_indication(struct usbnet *usbdev,
                        struct rndis_indicate *msg, int buflen)
 {
        struct ndis_80211_status_indication *indication;
-       int len, offset;
+       unsigned int len, offset;
 
        offset = offsetof(struct rndis_indicate, status) +
                        le32_to_cpu(msg->offset);
@@ -3034,7 +3057,7 @@ static void rndis_wlan_media_specific_indication(struct usbnet *usbdev,
                return;
        }
 
-       if (offset + len > buflen) {
+       if (len > buflen || offset > buflen || offset + len > buflen) {
                netdev_info(usbdev->net, "media specific indication, too large to fit to buffer (%i > %i)\n",
                            offset + len, buflen);
                return;
@@ -3391,7 +3414,7 @@ static const struct net_device_ops rndis_wlan_netdev_ops = {
        .ndo_tx_timeout         = usbnet_tx_timeout,
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
-       .ndo_set_multicast_list = rndis_wlan_set_multicast_list,
+       .ndo_set_rx_mode        = rndis_wlan_set_multicast_list,
 };
 
 static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)
@@ -3755,17 +3778,7 @@ static struct usb_driver rndis_wlan_driver = {
        .resume =       usbnet_resume,
 };
 
-static int __init rndis_wlan_init(void)
-{
-       return usb_register(&rndis_wlan_driver);
-}
-module_init(rndis_wlan_init);
-
-static void __exit rndis_wlan_exit(void)
-{
-       usb_deregister(&rndis_wlan_driver);
-}
-module_exit(rndis_wlan_exit);
+module_usb_driver(rndis_wlan_driver);
 
 MODULE_AUTHOR("Bjorge Dijkstra");
 MODULE_AUTHOR("Jussi Kivilinna");