cfg80211/mac80211: allow per-station GTKs
[linux-2.6.git] / drivers / net / wireless / rndis_wlan.c
index 2d28908..71b5971 100644 (file)
@@ -238,19 +238,19 @@ struct ndis_80211_auth_request {
        u8 bssid[6];
        u8 padding[2];
        __le32 flags;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_pmkid_candidate {
        u8 bssid[6];
        u8 padding[2];
        __le32 flags;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_pmkid_cand_list {
        __le32 version;
        __le32 num_candidates;
        struct ndis_80211_pmkid_candidate candidate_list[0];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_status_indication {
        __le32 status_type;
@@ -260,19 +260,19 @@ struct ndis_80211_status_indication {
                struct ndis_80211_auth_request          auth_request[0];
                struct ndis_80211_pmkid_cand_list       cand_list;
        } u;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_ssid {
        __le32 length;
        u8 essid[NDIS_802_11_LENGTH_SSID];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_conf_freq_hop {
        __le32 length;
        __le32 hop_pattern;
        __le32 hop_set;
        __le32 dwell_time;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_conf {
        __le32 length;
@@ -280,7 +280,7 @@ struct ndis_80211_conf {
        __le32 atim_window;
        __le32 ds_config;
        struct ndis_80211_conf_freq_hop fh_config;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_bssid_ex {
        __le32 length;
@@ -295,25 +295,25 @@ struct ndis_80211_bssid_ex {
        u8 rates[NDIS_802_11_LENGTH_RATES_EX];
        __le32 ie_length;
        u8 ies[0];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_bssid_list_ex {
        __le32 num_items;
        struct ndis_80211_bssid_ex bssid[0];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_fixed_ies {
        u8 timestamp[8];
        __le16 beacon_interval;
        __le16 capabilities;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_wep_key {
        __le32 size;
        __le32 index;
        __le32 length;
        u8 material[32];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_key {
        __le32 size;
@@ -323,14 +323,14 @@ struct ndis_80211_key {
        u8 padding[6];
        u8 rsc[8];
        u8 material[32];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_remove_key {
        __le32 size;
        __le32 index;
        u8 bssid[6];
        u8 padding[2];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_config_param {
        __le32 name_offs;
@@ -338,7 +338,7 @@ struct ndis_config_param {
        __le32 type;
        __le32 value_offs;
        __le32 value_length;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_assoc_info {
        __le32 length;
@@ -358,12 +358,12 @@ struct ndis_80211_assoc_info {
        } resp_ie;
        __le32 resp_ie_length;
        __le32 offset_resp_ies;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_auth_encr_pair {
        __le32 auth_mode;
        __le32 encr_mode;
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_capability {
        __le32 length;
@@ -371,7 +371,7 @@ struct ndis_80211_capability {
        __le32 num_pmkids;
        __le32 num_auth_encr_pair;
        struct ndis_80211_auth_encr_pair auth_encr_pair[0];
-} __attribute__((packed));
+} __packed;
 
 struct ndis_80211_bssid_info {
        u8 bssid[6];
@@ -520,8 +520,9 @@ static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
 
 static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed);
 
-static int rndis_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type,
-                               int dbm);
+static int rndis_set_tx_power(struct wiphy *wiphy,
+                             enum nl80211_tx_power_setting type,
+                             int mbm);
 static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm);
 
 static int rndis_connect(struct wiphy *wiphy, struct net_device *dev,
@@ -539,11 +540,11 @@ static int rndis_set_channel(struct wiphy *wiphy, struct net_device *dev,
        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);
+                        u8 key_index, bool pairwise, 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);
+                        u8 key_index, bool pairwise, const u8 *mac_addr);
 
 static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
                                                                u8 key_index);
@@ -1856,20 +1857,25 @@ static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed)
        return 0;
 }
 
-static int rndis_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type,
-                               int dbm)
+static int rndis_set_tx_power(struct wiphy *wiphy,
+                             enum nl80211_tx_power_setting type,
+                             int mbm)
 {
        struct rndis_wlan_private *priv = wiphy_priv(wiphy);
        struct usbnet *usbdev = priv->usbdev;
 
-       netdev_dbg(usbdev->net, "%s(): type:0x%x dbm:%i\n",
-                  __func__, type, dbm);
+       netdev_dbg(usbdev->net, "%s(): type:0x%x mbm:%i\n",
+                  __func__, type, mbm);
+
+       if (mbm < 0 || (mbm % 100))
+               return -ENOTSUPP;
 
        /* Device doesn't support changing txpower after initialization, only
         * turn off/on radio. Support 'auto' mode and setting same dBm that is
         * currently used.
         */
-       if (type == TX_POWER_AUTOMATIC || dbm == get_bcm4320_power_dbm(priv)) {
+       if (type == NL80211_TX_POWER_AUTOMATIC ||
+           MBM_TO_DBM(mbm) == get_bcm4320_power_dbm(priv)) {
                if (!priv->radio_on)
                        disassociate(usbdev, true); /* turn on radio */
 
@@ -2302,8 +2308,8 @@ static int rndis_set_channel(struct wiphy *wiphy, struct net_device *netdev,
 }
 
 static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
-                                       u8 key_index, const u8 *mac_addr,
-                                       struct key_params *params)
+                        u8 key_index, bool pairwise, const u8 *mac_addr,
+                        struct key_params *params)
 {
        struct rndis_wlan_private *priv = wiphy_priv(wiphy);
        struct usbnet *usbdev = priv->usbdev;
@@ -2338,7 +2344,7 @@ static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
 }
 
 static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev,
-                                       u8 key_index, const u8 *mac_addr)
+                        u8 key_index, bool pairwise, const u8 *mac_addr)
 {
        struct rndis_wlan_private *priv = wiphy_priv(wiphy);
        struct usbnet *usbdev = priv->usbdev;
@@ -2495,8 +2501,7 @@ static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
 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;
-       u8 assoc_buf[sizeof(*info) + IW_CUSTOM_MAX + 32];
+       struct ndis_80211_assoc_info *info = NULL;
        u8 bssid[ETH_ALEN];
        int resp_ie_len, req_ie_len;
        u8 *req_ie, *resp_ie;
@@ -2515,23 +2520,43 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
        resp_ie = NULL;
 
        if (priv->infra_mode == NDIS_80211_INFRA_INFRA) {
-               memset(assoc_buf, 0, sizeof(assoc_buf));
-               info = (void *)assoc_buf;
+               info = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
+               if (!info) {
+                       /* No memory? Try resume work later */
+                       set_bit(WORK_LINK_UP, &priv->work_pending);
+                       queue_work(priv->workqueue, &priv->work);
+                       return;
+               }
 
-               /* Get association info IEs from device and send them back to
-                * userspace. */
-               ret = get_association_info(usbdev, info, sizeof(assoc_buf));
+               /* Get association info IEs from device. */
+               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) {
                                offset = le32_to_cpu(info->offset_req_ies);
+
+                               if (offset > CONTROL_BUFFER_SIZE)
+                                       offset = CONTROL_BUFFER_SIZE;
+
                                req_ie = (u8 *)info + offset;
+
+                               if (offset + req_ie_len > CONTROL_BUFFER_SIZE)
+                                       req_ie_len =
+                                               CONTROL_BUFFER_SIZE - offset;
                        }
 
                        resp_ie_len = le32_to_cpu(info->resp_ie_length);
                        if (resp_ie_len > 0) {
                                offset = le32_to_cpu(info->offset_resp_ies);
+
+                               if (offset > CONTROL_BUFFER_SIZE)
+                                       offset = CONTROL_BUFFER_SIZE;
+
                                resp_ie = (u8 *)info + offset;
+
+                               if (offset + resp_ie_len > CONTROL_BUFFER_SIZE)
+                                       resp_ie_len =
+                                               CONTROL_BUFFER_SIZE - offset;
                        }
                }
        } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC))
@@ -2563,6 +2588,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
        } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
                cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
 
+       if (info != NULL)
+               kfree(info);
+
        priv->connected = true;
        memcpy(priv->bssid, bssid, ETH_ALEN);
 
@@ -2572,14 +2600,18 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
 
 static void rndis_wlan_do_link_down_work(struct usbnet *usbdev)
 {
-       union iwreq_data evt;
+       struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 
-       netif_carrier_off(usbdev->net);
+       if (priv->connected) {
+               priv->connected = false;
+               memset(priv->bssid, 0, ETH_ALEN);
 
-       evt.data.flags = 0;
-       evt.data.length = 0;
-       memset(evt.ap_addr.sa_data, 0, ETH_ALEN);
-       wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL);
+               deauthenticate(usbdev);
+
+               cfg80211_disconnected(usbdev->net, 0, NULL, 0, GFP_KERNEL);
+       }
+
+       netif_carrier_off(usbdev->net);
 }
 
 static void rndis_wlan_worker(struct work_struct *work)