diff options
author | Luciano Coelho <coelho@ti.com> | 2011-05-10 07:38:59 -0400 |
---|---|---|
committer | Luciano Coelho <coelho@ti.com> | 2011-05-12 17:06:33 -0400 |
commit | 95feadca6dca909ae0f6e65665b782c7ca9d5122 (patch) | |
tree | 5d76b5ef0e4c8e451a9e6009d3afde5c2ca70487 /drivers/net | |
parent | 6394c01b61f8ab66a6af1a24ff05f2429130afcd (diff) |
wl12xx: add scheduled scan structures and commands
Add firmware command structures, definitions and code to to configure,
start and stop scheduled scans.
Signed-off-by: Luciano Coelho <coelho@ti.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.c | 233 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.h | 114 |
2 files changed, 347 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 5d0544c8f3f5..d78044f00810 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c | |||
@@ -320,3 +320,236 @@ 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 | start = kzalloc(sizeof(*start), GFP_KERNEL); | ||
509 | if (!start) | ||
510 | return -ENOMEM; | ||
511 | |||
512 | start->tag = WL1271_SCAN_DEFAULT_TAG; | ||
513 | |||
514 | ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, | ||
515 | sizeof(*start), 0); | ||
516 | if (ret < 0) { | ||
517 | wl1271_error("failed to send scan start command"); | ||
518 | goto out_free; | ||
519 | } | ||
520 | |||
521 | out_free: | ||
522 | kfree(start); | ||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | void wl1271_scan_sched_scan_results(struct wl1271 *wl) | ||
527 | { | ||
528 | wl1271_debug(DEBUG_SCAN, "got periodic scan results"); | ||
529 | |||
530 | ieee80211_sched_scan_results(wl->hw); | ||
531 | } | ||
532 | |||
533 | void wl1271_scan_sched_scan_stop(struct wl1271 *wl) | ||
534 | { | ||
535 | struct wl1271_cmd_sched_scan_stop *stop; | ||
536 | int ret = 0; | ||
537 | |||
538 | wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); | ||
539 | |||
540 | /* FIXME: what to do if alloc'ing to stop fails? */ | ||
541 | stop = kzalloc(sizeof(*stop), GFP_KERNEL); | ||
542 | if (!stop) { | ||
543 | wl1271_error("failed to alloc memory to send sched scan stop"); | ||
544 | return; | ||
545 | } | ||
546 | |||
547 | stop->tag = WL1271_SCAN_DEFAULT_TAG; | ||
548 | |||
549 | ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, | ||
550 | sizeof(*stop), 0); | ||
551 | if (ret < 0) | ||
552 | wl1271_error("failed to send sched scan stop command"); | ||
553 | |||
554 | kfree(stop); | ||
555 | } | ||
diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h index 421a750add5a..c83319579ca3 100644 --- a/drivers/net/wireless/wl12xx/scan.h +++ b/drivers/net/wireless/wl12xx/scan.h | |||
@@ -33,6 +33,12 @@ int wl1271_scan_build_probe_req(struct wl1271 *wl, | |||
33 | const u8 *ie, size_t ie_len, u8 band); | 33 | const u8 *ie, size_t ie_len, u8 band); |
34 | void wl1271_scan_stm(struct wl1271 *wl); | 34 | void wl1271_scan_stm(struct wl1271 *wl); |
35 | void wl1271_scan_complete_work(struct work_struct *work); | 35 | void wl1271_scan_complete_work(struct work_struct *work); |
36 | int wl1271_scan_sched_scan_config(struct wl1271 *wl, | ||
37 | struct cfg80211_sched_scan_request *req, | ||
38 | struct ieee80211_sched_scan_ies *ies); | ||
39 | int wl1271_scan_sched_scan_start(struct wl1271 *wl); | ||
40 | void wl1271_scan_sched_scan_stop(struct wl1271 *wl); | ||
41 | void wl1271_scan_sched_scan_results(struct wl1271 *wl); | ||
36 | 42 | ||
37 | #define WL1271_SCAN_MAX_CHANNELS 24 | 43 | #define WL1271_SCAN_MAX_CHANNELS 24 |
38 | #define WL1271_SCAN_DEFAULT_TAG 1 | 44 | #define WL1271_SCAN_DEFAULT_TAG 1 |
@@ -106,4 +112,112 @@ struct wl1271_cmd_trigger_scan_to { | |||
106 | __le32 timeout; | 112 | __le32 timeout; |
107 | } __packed; | 113 | } __packed; |
108 | 114 | ||
115 | #define MAX_CHANNELS_ALL_BANDS 41 | ||
116 | #define SCAN_MAX_CYCLE_INTERVALS 16 | ||
117 | #define SCAN_MAX_BANDS 3 | ||
118 | |||
119 | enum { | ||
120 | SCAN_CHANNEL_TYPE_2GHZ_PASSIVE, | ||
121 | SCAN_CHANNEL_TYPE_2GHZ_ACTIVE, | ||
122 | SCAN_CHANNEL_TYPE_5GHZ_PASSIVE, | ||
123 | SCAN_CHANNEL_TYPE_5GHZ_ACTIVE, | ||
124 | SCAN_CHANNEL_TYPE_5GHZ_DFS, | ||
125 | }; | ||
126 | |||
127 | enum { | ||
128 | SCAN_SSID_FILTER_ANY = 0, | ||
129 | SCAN_SSID_FILTER_SPECIFIC = 1, | ||
130 | SCAN_SSID_FILTER_LIST = 2, | ||
131 | SCAN_SSID_FILTER_DISABLED = 3 | ||
132 | }; | ||
133 | |||
134 | enum { | ||
135 | SCAN_BSS_TYPE_INDEPENDENT, | ||
136 | SCAN_BSS_TYPE_INFRASTRUCTURE, | ||
137 | SCAN_BSS_TYPE_ANY, | ||
138 | }; | ||
139 | |||
140 | struct conn_scan_ch_params { | ||
141 | __le16 min_duration; | ||
142 | __le16 max_duration; | ||
143 | __le16 passive_duration; | ||
144 | |||
145 | u8 channel; | ||
146 | u8 tx_power_att; | ||
147 | |||
148 | /* bit 0: DFS channel; bit 1: DFS enabled */ | ||
149 | u8 flags; | ||
150 | |||
151 | u8 padding[3]; | ||
152 | } __packed; | ||
153 | |||
154 | struct wl1271_cmd_sched_scan_config { | ||
155 | struct wl1271_cmd_header header; | ||
156 | |||
157 | __le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; | ||
158 | |||
159 | s8 rssi_threshold; /* for filtering (in dBm) */ | ||
160 | s8 snr_threshold; /* for filtering (in dB) */ | ||
161 | |||
162 | u8 cycles; /* maximum number of scan cycles */ | ||
163 | u8 report_after; /* report when this number of results are received */ | ||
164 | u8 terminate; /* stop scanning after reporting */ | ||
165 | |||
166 | u8 tag; | ||
167 | u8 bss_type; /* for filtering */ | ||
168 | u8 filter_type; | ||
169 | |||
170 | u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ | ||
171 | u8 ssid[IW_ESSID_MAX_SIZE]; | ||
172 | |||
173 | u8 n_probe_reqs; /* Number of probes requests per channel */ | ||
174 | |||
175 | u8 passive[SCAN_MAX_BANDS]; | ||
176 | u8 active[SCAN_MAX_BANDS]; | ||
177 | |||
178 | u8 dfs; | ||
179 | |||
180 | u8 padding[3]; | ||
181 | |||
182 | struct conn_scan_ch_params channels[MAX_CHANNELS_ALL_BANDS]; | ||
183 | } __packed; | ||
184 | |||
185 | |||
186 | #define SCHED_SCAN_MAX_SSIDS 8 | ||
187 | |||
188 | enum { | ||
189 | SCAN_SSID_TYPE_PUBLIC = 0, | ||
190 | SCAN_SSID_TYPE_HIDDEN = 1, | ||
191 | }; | ||
192 | |||
193 | struct wl1271_ssid { | ||
194 | u8 type; | ||
195 | u8 len; | ||
196 | u8 ssid[IW_ESSID_MAX_SIZE]; | ||
197 | /* u8 padding[2]; */ | ||
198 | } __packed; | ||
199 | |||
200 | struct wl1271_cmd_sched_scan_ssid_list { | ||
201 | struct wl1271_cmd_header header; | ||
202 | |||
203 | u8 n_ssids; | ||
204 | struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS]; | ||
205 | u8 padding[3]; | ||
206 | } __packed; | ||
207 | |||
208 | struct wl1271_cmd_sched_scan_start { | ||
209 | struct wl1271_cmd_header header; | ||
210 | |||
211 | u8 tag; | ||
212 | u8 padding[3]; | ||
213 | } __packed; | ||
214 | |||
215 | struct wl1271_cmd_sched_scan_stop { | ||
216 | struct wl1271_cmd_header header; | ||
217 | |||
218 | u8 tag; | ||
219 | u8 padding[3]; | ||
220 | } __packed; | ||
221 | |||
222 | |||
109 | #endif /* __WL1271_SCAN_H__ */ | 223 | #endif /* __WL1271_SCAN_H__ */ |