diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/scan.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 5d0544c8f3f..f37e5a39197 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c | |||
@@ -320,3 +320,246 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, | |||
320 | 320 | ||
321 | return 0; | 321 | return 0; |
322 | } | 322 | } |
323 | |||
324 | static int | ||
325 | wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, | ||
326 | struct cfg80211_sched_scan_request *req, | ||
327 | struct conn_scan_ch_params *channels, | ||
328 | u32 band, bool radar, bool passive, | ||
329 | int start) | ||
330 | { | ||
331 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | ||
332 | int i, j; | ||
333 | u32 flags; | ||
334 | |||
335 | for (i = 0, j = start; | ||
336 | i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS; | ||
337 | i++) { | ||
338 | flags = req->channels[i]->flags; | ||
339 | |||
340 | if (!(flags & IEEE80211_CHAN_DISABLED) && | ||
341 | ((flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive) && | ||
342 | ((flags & IEEE80211_CHAN_RADAR) == radar) && | ||
343 | (req->channels[i]->band == band)) { | ||
344 | wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", | ||
345 | req->channels[i]->band, | ||
346 | req->channels[i]->center_freq); | ||
347 | wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", | ||
348 | req->channels[i]->hw_value, | ||
349 | req->channels[i]->flags); | ||
350 | wl1271_debug(DEBUG_SCAN, "max_power %d", | ||
351 | req->channels[i]->max_power); | ||
352 | |||
353 | if (flags & IEEE80211_CHAN_PASSIVE_SCAN) { | ||
354 | channels[j].passive_duration = | ||
355 | cpu_to_le16(c->dwell_time_passive); | ||
356 | } else { | ||
357 | channels[j].min_duration = | ||
358 | cpu_to_le16(c->min_dwell_time_active); | ||
359 | channels[j].max_duration = | ||
360 | cpu_to_le16(c->max_dwell_time_active); | ||
361 | } | ||
362 | channels[j].tx_power_att = req->channels[j]->max_power; | ||
363 | channels[j].channel = req->channels[i]->hw_value; | ||
364 | |||
365 | j++; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | return j - start; | ||
370 | } | ||
371 | |||
372 | static int | ||
373 | wl1271_scan_sched_scan_channels(struct wl1271 *wl, | ||
374 | struct cfg80211_sched_scan_request *req, | ||
375 | struct wl1271_cmd_sched_scan_config *cfg) | ||
376 | { | ||
377 | int idx = 0; | ||
378 | |||
379 | cfg->passive[0] = | ||
380 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
381 | IEEE80211_BAND_2GHZ, | ||
382 | false, true, idx); | ||
383 | idx += cfg->passive[0]; | ||
384 | |||
385 | cfg->active[0] = | ||
386 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
387 | IEEE80211_BAND_2GHZ, | ||
388 | false, false, idx); | ||
389 | idx += cfg->active[0]; | ||
390 | |||
391 | cfg->passive[1] = | ||
392 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
393 | IEEE80211_BAND_5GHZ, | ||
394 | false, true, idx); | ||
395 | idx += cfg->passive[1]; | ||
396 | |||
397 | cfg->active[1] = | ||
398 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
399 | IEEE80211_BAND_5GHZ, | ||
400 | false, false, 14); | ||
401 | idx += cfg->active[1]; | ||
402 | |||
403 | cfg->dfs = | ||
404 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
405 | IEEE80211_BAND_5GHZ, | ||
406 | true, false, idx); | ||
407 | idx += cfg->dfs; | ||
408 | |||
409 | wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", | ||
410 | cfg->active[0], cfg->passive[0]); | ||
411 | wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", | ||
412 | cfg->active[1], cfg->passive[1]); | ||
413 | |||
414 | return idx; | ||
415 | } | ||
416 | |||
417 | int wl1271_scan_sched_scan_config(struct wl1271 *wl, | ||
418 | struct cfg80211_sched_scan_request *req, | ||
419 | struct ieee80211_sched_scan_ies *ies) | ||
420 | { | ||
421 | struct wl1271_cmd_sched_scan_config *cfg = NULL; | ||
422 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | ||
423 | int i, total_channels, ret; | ||
424 | |||
425 | wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); | ||
426 | |||
427 | cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); | ||
428 | if (!cfg) | ||
429 | return -ENOMEM; | ||
430 | |||
431 | cfg->rssi_threshold = c->rssi_threshold; | ||
432 | cfg->snr_threshold = c->snr_threshold; | ||
433 | cfg->n_probe_reqs = c->num_probe_reqs; | ||
434 | /* cycles set to 0 it means infinite (until manually stopped) */ | ||
435 | cfg->cycles = 0; | ||
436 | /* report APs when at least 1 is found */ | ||
437 | cfg->report_after = 1; | ||
438 | /* don't stop scanning automatically when something is found */ | ||
439 | cfg->terminate = 0; | ||
440 | cfg->tag = WL1271_SCAN_DEFAULT_TAG; | ||
441 | /* don't filter on BSS type */ | ||
442 | cfg->bss_type = SCAN_BSS_TYPE_ANY; | ||
443 | /* currently NL80211 supports only a single interval */ | ||
444 | for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) | ||
445 | cfg->intervals[i] = cpu_to_le32(req->interval); | ||
446 | |||
447 | if (req->ssids[0].ssid_len && req->ssids[0].ssid) { | ||
448 | cfg->filter_type = SCAN_SSID_FILTER_SPECIFIC; | ||
449 | cfg->ssid_len = req->ssids[0].ssid_len; | ||
450 | memcpy(cfg->ssid, req->ssids[0].ssid, | ||
451 | req->ssids[0].ssid_len); | ||
452 | } else { | ||
453 | cfg->filter_type = SCAN_SSID_FILTER_ANY; | ||
454 | cfg->ssid_len = 0; | ||
455 | } | ||
456 | |||
457 | total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg); | ||
458 | if (total_channels == 0) { | ||
459 | wl1271_error("scan channel list is empty"); | ||
460 | ret = -EINVAL; | ||
461 | goto out; | ||
462 | } | ||
463 | |||
464 | if (cfg->active[0]) { | ||
465 | ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, | ||
466 | req->ssids[0].ssid_len, | ||
467 | ies->ie[IEEE80211_BAND_2GHZ], | ||
468 | ies->len[IEEE80211_BAND_2GHZ], | ||
469 | IEEE80211_BAND_2GHZ); | ||
470 | if (ret < 0) { | ||
471 | wl1271_error("2.4GHz PROBE request template failed"); | ||
472 | goto out; | ||
473 | } | ||
474 | } | ||
475 | |||
476 | if (cfg->active[1]) { | ||
477 | ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, | ||
478 | req->ssids[0].ssid_len, | ||
479 | ies->ie[IEEE80211_BAND_5GHZ], | ||
480 | ies->len[IEEE80211_BAND_5GHZ], | ||
481 | IEEE80211_BAND_5GHZ); | ||
482 | if (ret < 0) { | ||
483 | wl1271_error("5GHz PROBE request template failed"); | ||
484 | goto out; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); | ||
489 | |||
490 | ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, | ||
491 | sizeof(*cfg), 0); | ||
492 | if (ret < 0) { | ||
493 | wl1271_error("SCAN configuration failed"); | ||
494 | goto out; | ||
495 | } | ||
496 | out: | ||
497 | kfree(cfg); | ||
498 | return ret; | ||
499 | } | ||
500 | |||
501 | int wl1271_scan_sched_scan_start(struct wl1271 *wl) | ||
502 | { | ||
503 | struct wl1271_cmd_sched_scan_start *start; | ||
504 | int ret = 0; | ||
505 | |||
506 | wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); | ||
507 | |||
508 | if (wl->bss_type != BSS_TYPE_STA_BSS) | ||
509 | return -EOPNOTSUPP; | ||
510 | |||
511 | if (!test_bit(WL1271_FLAG_IDLE, &wl->flags)) | ||
512 | return -EBUSY; | ||
513 | |||
514 | start = kzalloc(sizeof(*start), GFP_KERNEL); | ||
515 | if (!start) | ||
516 | return -ENOMEM; | ||
517 | |||
518 | start->tag = WL1271_SCAN_DEFAULT_TAG; | ||
519 | |||
520 | ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, | ||
521 | sizeof(*start), 0); | ||
522 | if (ret < 0) { | ||
523 | wl1271_error("failed to send scan start command"); | ||
524 | goto out_free; | ||
525 | } | ||
526 | |||
527 | out_free: | ||
528 | kfree(start); | ||
529 | return ret; | ||
530 | } | ||
531 | |||
532 | void wl1271_scan_sched_scan_results(struct wl1271 *wl) | ||
533 | { | ||
534 | wl1271_debug(DEBUG_SCAN, "got periodic scan results"); | ||
535 | |||
536 | ieee80211_sched_scan_results(wl->hw); | ||
537 | } | ||
538 | |||
539 | void wl1271_scan_sched_scan_stop(struct wl1271 *wl) | ||
540 | { | ||
541 | struct wl1271_cmd_sched_scan_stop *stop; | ||
542 | int ret = 0; | ||
543 | |||
544 | wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); | ||
545 | |||
546 | /* FIXME: what to do if alloc'ing to stop fails? */ | ||
547 | stop = kzalloc(sizeof(*stop), GFP_KERNEL); | ||
548 | if (!stop) { | ||
549 | wl1271_error("failed to alloc memory to send sched scan stop"); | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | stop->tag = WL1271_SCAN_DEFAULT_TAG; | ||
554 | |||
555 | ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, | ||
556 | sizeof(*stop), 0); | ||
557 | if (ret < 0) { | ||
558 | wl1271_error("failed to send sched scan stop command"); | ||
559 | goto out_free; | ||
560 | } | ||
561 | wl->sched_scanning = false; | ||
562 | |||
563 | out_free: | ||
564 | kfree(stop); | ||
565 | } | ||