diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-10 00:05:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-10 00:05:52 -0400 |
commit | f6cec0ae58c17522a7bc4e2f39dae19f199ab534 (patch) | |
tree | 496cf6f53b0c75d9ae57bd0e411c5d2f6cea5cbb /drivers/net/wireless/libertas | |
parent | 0fcf12d510b6d1b1b090a090c62009310eca4be4 (diff) | |
parent | c4e9b56e24422e71424b24eee27c2b134a191d7b (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.c | 214 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/dev.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 1 |
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 */ | ||
261 | static u8 * | ||
262 | add_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) | |||
264 | static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) | 287 | static 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 | ||
729 | static 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 | ||
690 | static int lbs_cfg_scan(struct wiphy *wiphy, | 747 | static 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 | ||
1180 | static 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 | ||
1129 | static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, | 1237 | static 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 | ||
166 | extern struct cmd_confirm_sleep confirm_sleep; | 171 | extern 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; |