aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/libertas
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-08-10 00:05:52 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-10 00:05:52 -0400
commitf6cec0ae58c17522a7bc4e2f39dae19f199ab534 (patch)
tree496cf6f53b0c75d9ae57bd0e411c5d2f6cea5cbb /drivers/net/wireless/libertas
parent0fcf12d510b6d1b1b090a090c62009310eca4be4 (diff)
parentc4e9b56e24422e71424b24eee27c2b134a191d7b (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (59 commits) igbvf.txt: Add igbvf Documentation igb.txt: Add igb documentation e100/e1000*/igb*/ixgb*: Add missing read memory barrier ixgbe: fix build error with FCOE_CONFIG without DCB_CONFIG netxen: protect tx timeout recovery by rtnl lock isdn: gigaset: use after free isdn: gigaset: add missing unlock solos-pci: Fix race condition in tasklet RX handling pkt_sched: Fix sch_sfq vs tcf_bind_filter oops net: disable preemption before call smp_processor_id() tcp: no md5sig option size check bug iwlwifi: fix locking assertions iwlwifi: fix TX tracer isdn: fix information leak net: Fix napi_gro_frags vs netpoll path usbnet: remove noisy and hardly useful printk rtl8180: avoid potential NULL deref in rtl8180_beacon_work ath9k: Remove myself from the MAINTAINERS list libertas: scan before assocation if no BSSID was given libertas: fix association with some APs by using extended rates ...
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r--drivers/net/wireless/libertas/cfg.c214
-rw-r--r--drivers/net/wireless/libertas/dev.h5
-rw-r--r--drivers/net/wireless/libertas/main.c1
3 files changed, 170 insertions, 50 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 25f902760980..8e9fbfd804b6 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -257,6 +257,29 @@ static int lbs_add_supported_rates_tlv(u8 *tlv)
257 return sizeof(rate_tlv->header) + i; 257 return sizeof(rate_tlv->header) + i;
258} 258}
259 259
260/* Add common rates from a TLV and return the new end of the TLV */
261static u8 *
262add_ie_rates(u8 *tlv, const u8 *ie, int *nrates)
263{
264 int hw, ap, ap_max = ie[1];
265 u8 hw_rate;
266
267 /* Advance past IE header */
268 ie += 2;
269
270 lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max);
271
272 for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
273 hw_rate = lbs_rates[hw].bitrate / 5;
274 for (ap = 0; ap < ap_max; ap++) {
275 if (hw_rate == (ie[ap] & 0x7f)) {
276 *tlv++ = ie[ap];
277 *nrates = *nrates + 1;
278 }
279 }
280 }
281 return tlv;
282}
260 283
261/* 284/*
262 * Adds a TLV with all rates the hardware *and* BSS supports. 285 * Adds a TLV with all rates the hardware *and* BSS supports.
@@ -264,8 +287,11 @@ static int lbs_add_supported_rates_tlv(u8 *tlv)
264static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) 287static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
265{ 288{
266 struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; 289 struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
267 const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); 290 const u8 *rates_eid, *ext_rates_eid;
268 int n; 291 int n = 0;
292
293 rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
294 ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
269 295
270 /* 296 /*
271 * 01 00 TLV_TYPE_RATES 297 * 01 00 TLV_TYPE_RATES
@@ -275,26 +301,21 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
275 rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); 301 rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
276 tlv += sizeof(rate_tlv->header); 302 tlv += sizeof(rate_tlv->header);
277 303
278 if (!rates_eid) { 304 /* Add basic rates */
305 if (rates_eid) {
306 tlv = add_ie_rates(tlv, rates_eid, &n);
307
308 /* Add extended rates, if any */
309 if (ext_rates_eid)
310 tlv = add_ie_rates(tlv, ext_rates_eid, &n);
311 } else {
312 lbs_deb_assoc("assoc: bss had no basic rate IE\n");
279 /* Fallback: add basic 802.11b rates */ 313 /* Fallback: add basic 802.11b rates */
280 *tlv++ = 0x82; 314 *tlv++ = 0x82;
281 *tlv++ = 0x84; 315 *tlv++ = 0x84;
282 *tlv++ = 0x8b; 316 *tlv++ = 0x8b;
283 *tlv++ = 0x96; 317 *tlv++ = 0x96;
284 n = 4; 318 n = 4;
285 } else {
286 int hw, ap;
287 u8 ap_max = rates_eid[1];
288 n = 0;
289 for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
290 u8 hw_rate = lbs_rates[hw].bitrate / 5;
291 for (ap = 0; ap < ap_max; ap++) {
292 if (hw_rate == (rates_eid[ap+2] & 0x7f)) {
293 *tlv++ = rates_eid[ap+2];
294 n++;
295 }
296 }
297 }
298 } 319 }
299 320
300 rate_tlv->header.len = cpu_to_le16(n); 321 rate_tlv->header.len = cpu_to_le16(n);
@@ -465,7 +486,15 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
465 lbs_deb_enter(LBS_DEB_CFG80211); 486 lbs_deb_enter(LBS_DEB_CFG80211);
466 487
467 bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); 488 bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
468 nr_sets = le16_to_cpu(resp->size); 489 nr_sets = le16_to_cpu(scanresp->nr_sets);
490
491 lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
492 nr_sets, bsssize, le16_to_cpu(resp->size));
493
494 if (nr_sets == 0) {
495 ret = 0;
496 goto done;
497 }
469 498
470 /* 499 /*
471 * The general layout of the scan response is described in chapter 500 * The general layout of the scan response is described in chapter
@@ -670,8 +699,13 @@ static void lbs_scan_worker(struct work_struct *work)
670 699
671 if (priv->scan_channel >= priv->scan_req->n_channels) { 700 if (priv->scan_channel >= priv->scan_req->n_channels) {
672 /* Mark scan done */ 701 /* Mark scan done */
673 cfg80211_scan_done(priv->scan_req, false); 702 if (priv->internal_scan)
703 kfree(priv->scan_req);
704 else
705 cfg80211_scan_done(priv->scan_req, false);
706
674 priv->scan_req = NULL; 707 priv->scan_req = NULL;
708 priv->last_scan = jiffies;
675 } 709 }
676 710
677 /* Restart network */ 711 /* Restart network */
@@ -682,10 +716,33 @@ static void lbs_scan_worker(struct work_struct *work)
682 716
683 kfree(scan_cmd); 717 kfree(scan_cmd);
684 718
719 /* Wake up anything waiting on scan completion */
720 if (priv->scan_req == NULL) {
721 lbs_deb_scan("scan: waking up waiters\n");
722 wake_up_all(&priv->scan_q);
723 }
724
685 out_no_scan_cmd: 725 out_no_scan_cmd:
686 lbs_deb_leave(LBS_DEB_SCAN); 726 lbs_deb_leave(LBS_DEB_SCAN);
687} 727}
688 728
729static void _internal_start_scan(struct lbs_private *priv, bool internal,
730 struct cfg80211_scan_request *request)
731{
732 lbs_deb_enter(LBS_DEB_CFG80211);
733
734 lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
735 request->n_ssids, request->n_channels, request->ie_len);
736
737 priv->scan_channel = 0;
738 queue_delayed_work(priv->work_thread, &priv->scan_work,
739 msecs_to_jiffies(50));
740
741 priv->scan_req = request;
742 priv->internal_scan = internal;
743
744 lbs_deb_leave(LBS_DEB_CFG80211);
745}
689 746
690static int lbs_cfg_scan(struct wiphy *wiphy, 747static int lbs_cfg_scan(struct wiphy *wiphy,
691 struct net_device *dev, 748 struct net_device *dev,
@@ -702,18 +759,11 @@ static int lbs_cfg_scan(struct wiphy *wiphy,
702 goto out; 759 goto out;
703 } 760 }
704 761
705 lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", 762 _internal_start_scan(priv, false, request);
706 request->n_ssids, request->n_channels, request->ie_len);
707
708 priv->scan_channel = 0;
709 queue_delayed_work(priv->work_thread, &priv->scan_work,
710 msecs_to_jiffies(50));
711 763
712 if (priv->surpriseremoved) 764 if (priv->surpriseremoved)
713 ret = -EIO; 765 ret = -EIO;
714 766
715 priv->scan_req = request;
716
717 out: 767 out:
718 lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); 768 lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
719 return ret; 769 return ret;
@@ -1000,6 +1050,7 @@ static int lbs_associate(struct lbs_private *priv,
1000 int status; 1050 int status;
1001 int ret; 1051 int ret;
1002 u8 *pos = &(cmd->iebuf[0]); 1052 u8 *pos = &(cmd->iebuf[0]);
1053 u8 *tmp;
1003 1054
1004 lbs_deb_enter(LBS_DEB_CFG80211); 1055 lbs_deb_enter(LBS_DEB_CFG80211);
1005 1056
@@ -1044,7 +1095,9 @@ static int lbs_associate(struct lbs_private *priv,
1044 pos += lbs_add_cf_param_tlv(pos); 1095 pos += lbs_add_cf_param_tlv(pos);
1045 1096
1046 /* add rates TLV */ 1097 /* add rates TLV */
1098 tmp = pos + 4; /* skip Marvell IE header */
1047 pos += lbs_add_common_rates_tlv(pos, bss); 1099 pos += lbs_add_common_rates_tlv(pos, bss);
1100 lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp);
1048 1101
1049 /* add auth type TLV */ 1102 /* add auth type TLV */
1050 if (priv->fwrelease >= 0x09000000) 1103 if (priv->fwrelease >= 0x09000000)
@@ -1124,7 +1177,62 @@ done:
1124 return ret; 1177 return ret;
1125} 1178}
1126 1179
1180static struct cfg80211_scan_request *
1181_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme)
1182{
1183 struct cfg80211_scan_request *creq = NULL;
1184 int i, n_channels = 0;
1185 enum ieee80211_band band;
1186
1187 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
1188 if (wiphy->bands[band])
1189 n_channels += wiphy->bands[band]->n_channels;
1190 }
1191
1192 creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
1193 n_channels * sizeof(void *),
1194 GFP_ATOMIC);
1195 if (!creq)
1196 return NULL;
1197
1198 /* SSIDs come after channels */
1199 creq->ssids = (void *)&creq->channels[n_channels];
1200 creq->n_channels = n_channels;
1201 creq->n_ssids = 1;
1202
1203 /* Scan all available channels */
1204 i = 0;
1205 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
1206 int j;
1207
1208 if (!wiphy->bands[band])
1209 continue;
1210
1211 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
1212 /* ignore disabled channels */
1213 if (wiphy->bands[band]->channels[j].flags &
1214 IEEE80211_CHAN_DISABLED)
1215 continue;
1216
1217 creq->channels[i] = &wiphy->bands[band]->channels[j];
1218 i++;
1219 }
1220 }
1221 if (i) {
1222 /* Set real number of channels specified in creq->channels[] */
1223 creq->n_channels = i;
1224
1225 /* Scan for the SSID we're going to connect to */
1226 memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len);
1227 creq->ssids[0].ssid_len = sme->ssid_len;
1228 } else {
1229 /* No channels found... */
1230 kfree(creq);
1231 creq = NULL;
1232 }
1127 1233
1234 return creq;
1235}
1128 1236
1129static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, 1237static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
1130 struct cfg80211_connect_params *sme) 1238 struct cfg80211_connect_params *sme)
@@ -1136,37 +1244,43 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
1136 1244
1137 lbs_deb_enter(LBS_DEB_CFG80211); 1245 lbs_deb_enter(LBS_DEB_CFG80211);
1138 1246
1139 if (sme->bssid) { 1247 if (!sme->bssid) {
1140 bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, 1248 /* Run a scan if one isn't in-progress already and if the last
1141 sme->ssid, sme->ssid_len, 1249 * scan was done more than 2 seconds ago.
1142 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
1143 } else {
1144 /*
1145 * Here we have an impedance mismatch. The firmware command
1146 * CMD_802_11_ASSOCIATE always needs a BSSID, it cannot
1147 * connect otherwise. However, for the connect-API of
1148 * cfg80211 the bssid is purely optional. We don't get one,
1149 * except the user specifies one on the "iw" command line.
1150 *
1151 * If we don't got one, we could initiate a scan and look
1152 * for the best matching cfg80211_bss entry.
1153 *
1154 * Or, better yet, net/wireless/sme.c get's rewritten into
1155 * something more generally useful.
1156 */ 1250 */
1157 lbs_pr_err("TODO: no BSS specified\n"); 1251 if (priv->scan_req == NULL &&
1158 ret = -ENOTSUPP; 1252 time_after(jiffies, priv->last_scan + (2 * HZ))) {
1159 goto done; 1253 struct cfg80211_scan_request *creq;
1160 }
1161 1254
1255 creq = _new_connect_scan_req(wiphy, sme);
1256 if (!creq) {
1257 ret = -EINVAL;
1258 goto done;
1259 }
1260
1261 lbs_deb_assoc("assoc: scanning for compatible AP\n");
1262 _internal_start_scan(priv, true, creq);
1263 }
1264
1265 /* Wait for any in-progress scan to complete */
1266 lbs_deb_assoc("assoc: waiting for scan to complete\n");
1267 wait_event_interruptible_timeout(priv->scan_q,
1268 (priv->scan_req == NULL),
1269 (15 * HZ));
1270 lbs_deb_assoc("assoc: scanning competed\n");
1271 }
1162 1272
1273 /* Find the BSS we want using available scan results */
1274 bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
1275 sme->ssid, sme->ssid_len,
1276 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
1163 if (!bss) { 1277 if (!bss) {
1164 lbs_pr_err("assicate: bss %pM not in scan results\n", 1278 lbs_pr_err("assoc: bss %pM not in scan results\n",
1165 sme->bssid); 1279 sme->bssid);
1166 ret = -ENOENT; 1280 ret = -ENOENT;
1167 goto done; 1281 goto done;
1168 } 1282 }
1169 lbs_deb_assoc("trying %pM", sme->bssid); 1283 lbs_deb_assoc("trying %pM\n", bss->bssid);
1170 lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", 1284 lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n",
1171 sme->crypto.cipher_group, 1285 sme->crypto.cipher_group,
1172 sme->key_idx, sme->key_len); 1286 sme->key_idx, sme->key_len);
@@ -1229,7 +1343,7 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
1229 lbs_set_radio(priv, preamble, 1); 1343 lbs_set_radio(priv, preamble, 1);
1230 1344
1231 /* Do the actual association */ 1345 /* Do the actual association */
1232 lbs_associate(priv, bss, sme); 1346 ret = lbs_associate(priv, bss, sme);
1233 1347
1234 done: 1348 done:
1235 if (bss) 1349 if (bss)
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 3c7e255e18c7..f062ed583901 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -161,6 +161,11 @@ struct lbs_private {
161 /** Scanning */ 161 /** Scanning */
162 struct delayed_work scan_work; 162 struct delayed_work scan_work;
163 int scan_channel; 163 int scan_channel;
164 /* Queue of things waiting for scan completion */
165 wait_queue_head_t scan_q;
166 /* Whether the scan was initiated internally and not by cfg80211 */
167 bool internal_scan;
168 unsigned long last_scan;
164}; 169};
165 170
166extern struct cmd_confirm_sleep confirm_sleep; 171extern struct cmd_confirm_sleep confirm_sleep;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 258967144b96..24958a86747b 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -719,6 +719,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
719 priv->deep_sleep_required = 0; 719 priv->deep_sleep_required = 0;
720 priv->wakeup_dev_required = 0; 720 priv->wakeup_dev_required = 0;
721 init_waitqueue_head(&priv->ds_awake_q); 721 init_waitqueue_head(&priv->ds_awake_q);
722 init_waitqueue_head(&priv->scan_q);
722 priv->authtype_auto = 1; 723 priv->authtype_auto = 1;
723 priv->is_host_sleep_configured = 0; 724 priv->is_host_sleep_configured = 0;
724 priv->is_host_sleep_activated = 0; 725 priv->is_host_sleep_activated = 0;