aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/libertas
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-08-04 01:43:47 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-08-05 16:05:27 -0400
commitcc02681923ce09a7c8cfacc6855de259b9d4ef87 (patch)
tree2275e6f20587b958136b5ee67cb3049f437c726a /drivers/net/wireless/libertas
parent197575393713eba035925e4bfdee407f473e5057 (diff)
libertas: scan before assocation if no BSSID was given
Fix this leftover TODO from the cfg80211 conversion by doing a scan if cfg80211 didn't pass in the BSSID for us. Since the scan code uses so much of the cfg80211_scan_request structure to build up the firmware command, we just fake one when the scan request is triggered internally. But we need to make sure that internal 'fake' cfg82011 scan request does not get back to cfg82011 via cfg80211_scan_done(). Signed-off-by: Dan Williams <dcbw@redhat.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r--drivers/net/wireless/libertas/cfg.c148
-rw-r--r--drivers/net/wireless/libertas/dev.h5
-rw-r--r--drivers/net/wireless/libertas/main.c1
3 files changed, 121 insertions, 33 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 6b35d057426d..8e9fbfd804b6 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -699,8 +699,13 @@ static void lbs_scan_worker(struct work_struct *work)
699 699
700 if (priv->scan_channel >= priv->scan_req->n_channels) { 700 if (priv->scan_channel >= priv->scan_req->n_channels) {
701 /* Mark scan done */ 701 /* Mark scan done */
702 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
703 priv->scan_req = NULL; 707 priv->scan_req = NULL;
708 priv->last_scan = jiffies;
704 } 709 }
705 710
706 /* Restart network */ 711 /* Restart network */
@@ -711,10 +716,33 @@ static void lbs_scan_worker(struct work_struct *work)
711 716
712 kfree(scan_cmd); 717 kfree(scan_cmd);
713 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
714 out_no_scan_cmd: 725 out_no_scan_cmd:
715 lbs_deb_leave(LBS_DEB_SCAN); 726 lbs_deb_leave(LBS_DEB_SCAN);
716} 727}
717 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}
718 746
719static int lbs_cfg_scan(struct wiphy *wiphy, 747static int lbs_cfg_scan(struct wiphy *wiphy,
720 struct net_device *dev, 748 struct net_device *dev,
@@ -731,18 +759,11 @@ static int lbs_cfg_scan(struct wiphy *wiphy,
731 goto out; 759 goto out;
732 } 760 }
733 761
734 lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", 762 _internal_start_scan(priv, false, request);
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 763
741 if (priv->surpriseremoved) 764 if (priv->surpriseremoved)
742 ret = -EIO; 765 ret = -EIO;
743 766
744 priv->scan_req = request;
745
746 out: 767 out:
747 lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); 768 lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
748 return ret; 769 return ret;
@@ -1156,7 +1177,62 @@ done:
1156 return ret; 1177 return ret;
1157} 1178}
1158 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 }
1159 1233
1234 return creq;
1235}
1160 1236
1161static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, 1237static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
1162 struct cfg80211_connect_params *sme) 1238 struct cfg80211_connect_params *sme)
@@ -1168,37 +1244,43 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
1168 1244
1169 lbs_deb_enter(LBS_DEB_CFG80211); 1245 lbs_deb_enter(LBS_DEB_CFG80211);
1170 1246
1171 if (sme->bssid) { 1247 if (!sme->bssid) {
1172 bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, 1248 /* Run a scan if one isn't in-progress already and if the last
1173 sme->ssid, sme->ssid_len, 1249 * scan was done more than 2 seconds ago.
1174 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
1175 } else {
1176 /*
1177 * Here we have an impedance mismatch. The firmware command
1178 * CMD_802_11_ASSOCIATE always needs a BSSID, it cannot
1179 * connect otherwise. However, for the connect-API of
1180 * cfg80211 the bssid is purely optional. We don't get one,
1181 * except the user specifies one on the "iw" command line.
1182 *
1183 * If we don't got one, we could initiate a scan and look
1184 * for the best matching cfg80211_bss entry.
1185 *
1186 * Or, better yet, net/wireless/sme.c get's rewritten into
1187 * something more generally useful.
1188 */ 1250 */
1189 lbs_pr_err("TODO: no BSS specified\n"); 1251 if (priv->scan_req == NULL &&
1190 ret = -ENOTSUPP; 1252 time_after(jiffies, priv->last_scan + (2 * HZ))) {
1191 goto done; 1253 struct cfg80211_scan_request *creq;
1192 }
1193 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 }
1194 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);
1195 if (!bss) { 1277 if (!bss) {
1196 lbs_pr_err("assicate: bss %pM not in scan results\n", 1278 lbs_pr_err("assoc: bss %pM not in scan results\n",
1197 sme->bssid); 1279 sme->bssid);
1198 ret = -ENOENT; 1280 ret = -ENOENT;
1199 goto done; 1281 goto done;
1200 } 1282 }
1201 lbs_deb_assoc("trying %pM", sme->bssid); 1283 lbs_deb_assoc("trying %pM\n", bss->bssid);
1202 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",
1203 sme->crypto.cipher_group, 1285 sme->crypto.cipher_group,
1204 sme->key_idx, sme->key_len); 1286 sme->key_idx, sme->key_len);
@@ -1261,7 +1343,7 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
1261 lbs_set_radio(priv, preamble, 1); 1343 lbs_set_radio(priv, preamble, 1);
1262 1344
1263 /* Do the actual association */ 1345 /* Do the actual association */
1264 lbs_associate(priv, bss, sme); 1346 ret = lbs_associate(priv, bss, sme);
1265 1347
1266 done: 1348 done:
1267 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;