hw_features.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. /*
  2. * hostapd / Hardware feature query and different modes
  3. * Copyright 2002-2003, Instant802 Networks, Inc.
  4. * Copyright 2005-2006, Devicescape Software, Inc.
  5. * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. * Alternatively, this software may be distributed under the terms of BSD
  12. * license.
  13. *
  14. * See README and COPYING for more details.
  15. */
  16. #include "includes.h"
  17. #include "hostapd.h"
  18. #include "hw_features.h"
  19. #include "driver_i.h"
  20. #include "config.h"
  21. void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
  22. size_t num_hw_features)
  23. {
  24. size_t i;
  25. if (hw_features == NULL)
  26. return;
  27. for (i = 0; i < num_hw_features; i++) {
  28. os_free(hw_features[i].channels);
  29. os_free(hw_features[i].rates);
  30. }
  31. os_free(hw_features);
  32. }
  33. int hostapd_get_hw_features(struct hostapd_iface *iface)
  34. {
  35. struct hostapd_data *hapd = iface->bss[0];
  36. int ret = 0, i, j;
  37. u16 num_modes, flags;
  38. struct hostapd_hw_modes *modes;
  39. if (hostapd_drv_none(hapd))
  40. return -1;
  41. modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
  42. if (modes == NULL) {
  43. hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
  44. HOSTAPD_LEVEL_DEBUG,
  45. "Fetching hardware channel/rate support not "
  46. "supported.");
  47. return -1;
  48. }
  49. iface->hw_flags = flags;
  50. hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
  51. iface->hw_features = modes;
  52. iface->num_hw_features = num_modes;
  53. for (i = 0; i < num_modes; i++) {
  54. struct hostapd_hw_modes *feature = &modes[i];
  55. /* set flag for channels we can use in current regulatory
  56. * domain */
  57. for (j = 0; j < feature->num_channels; j++) {
  58. /*
  59. * Disable all channels that are marked not to allow
  60. * IBSS operation or active scanning. In addition,
  61. * disable all channels that require radar detection,
  62. * since that (in addition to full DFS) is not yet
  63. * supported.
  64. */
  65. if (feature->channels[j].flag &
  66. (HOSTAPD_CHAN_NO_IBSS |
  67. HOSTAPD_CHAN_PASSIVE_SCAN |
  68. HOSTAPD_CHAN_RADAR))
  69. feature->channels[j].flag |=
  70. HOSTAPD_CHAN_DISABLED;
  71. if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
  72. continue;
  73. wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
  74. "chan=%d freq=%d MHz max_tx_power=%d dBm",
  75. feature->mode,
  76. feature->channels[j].chan,
  77. feature->channels[j].freq,
  78. feature->channels[j].max_tx_power);
  79. }
  80. }
  81. return ret;
  82. }
  83. static int hostapd_prepare_rates(struct hostapd_data *hapd,
  84. struct hostapd_hw_modes *mode)
  85. {
  86. int i, num_basic_rates = 0;
  87. int basic_rates_a[] = { 60, 120, 240, -1 };
  88. int basic_rates_b[] = { 10, 20, -1 };
  89. int basic_rates_g[] = { 10, 20, 55, 110, -1 };
  90. int *basic_rates;
  91. if (hapd->iconf->basic_rates)
  92. basic_rates = hapd->iconf->basic_rates;
  93. else switch (mode->mode) {
  94. case HOSTAPD_MODE_IEEE80211A:
  95. basic_rates = basic_rates_a;
  96. break;
  97. case HOSTAPD_MODE_IEEE80211B:
  98. basic_rates = basic_rates_b;
  99. break;
  100. case HOSTAPD_MODE_IEEE80211G:
  101. basic_rates = basic_rates_g;
  102. break;
  103. default:
  104. return -1;
  105. }
  106. if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates,
  107. basic_rates, mode->mode)) {
  108. wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel "
  109. "module");
  110. }
  111. os_free(hapd->iface->current_rates);
  112. hapd->iface->num_rates = 0;
  113. hapd->iface->current_rates =
  114. os_malloc(mode->num_rates * sizeof(struct hostapd_rate_data));
  115. if (!hapd->iface->current_rates) {
  116. wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
  117. "table.");
  118. return -1;
  119. }
  120. for (i = 0; i < mode->num_rates; i++) {
  121. struct hostapd_rate_data *rate;
  122. if (hapd->iconf->supported_rates &&
  123. !hostapd_rate_found(hapd->iconf->supported_rates,
  124. mode->rates[i].rate))
  125. continue;
  126. rate = &hapd->iface->current_rates[hapd->iface->num_rates];
  127. os_memcpy(rate, &mode->rates[i],
  128. sizeof(struct hostapd_rate_data));
  129. if (hostapd_rate_found(basic_rates, rate->rate)) {
  130. rate->flags |= HOSTAPD_RATE_BASIC;
  131. num_basic_rates++;
  132. } else
  133. rate->flags &= ~HOSTAPD_RATE_BASIC;
  134. wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
  135. hapd->iface->num_rates, rate->rate, rate->flags);
  136. hapd->iface->num_rates++;
  137. }
  138. if (hapd->iface->num_rates == 0 || num_basic_rates == 0) {
  139. wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
  140. "rate sets (%d,%d).",
  141. hapd->iface->num_rates, num_basic_rates);
  142. return -1;
  143. }
  144. return 0;
  145. }
  146. #ifdef CONFIG_IEEE80211N
  147. static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
  148. {
  149. int sec_chan, ok, j, first;
  150. int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
  151. 184, 192 };
  152. size_t k;
  153. if (!iface->conf->secondary_channel)
  154. return 1; /* HT40 not used */
  155. sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
  156. wpa_printf(MSG_DEBUG, "HT40: control channel: %d "
  157. "secondary channel: %d",
  158. iface->conf->channel, sec_chan);
  159. /* Verify that HT40 secondary channel is an allowed 20 MHz
  160. * channel */
  161. ok = 0;
  162. for (j = 0; j < iface->current_mode->num_channels; j++) {
  163. struct hostapd_channel_data *chan =
  164. &iface->current_mode->channels[j];
  165. if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
  166. chan->chan == sec_chan) {
  167. ok = 1;
  168. break;
  169. }
  170. }
  171. if (!ok) {
  172. wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
  173. sec_chan);
  174. return 0;
  175. }
  176. /*
  177. * Verify that HT40 primary,secondary channel pair is allowed per
  178. * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
  179. * 2.4 GHz rules allow all cases where the secondary channel fits into
  180. * the list of allowed channels (already checked above).
  181. */
  182. if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
  183. return 1;
  184. if (iface->conf->secondary_channel > 0)
  185. first = iface->conf->channel;
  186. else
  187. first = sec_chan;
  188. ok = 0;
  189. for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
  190. if (first == allowed[k]) {
  191. ok = 1;
  192. break;
  193. }
  194. }
  195. if (!ok) {
  196. wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
  197. iface->conf->channel,
  198. iface->conf->secondary_channel);
  199. return 0;
  200. }
  201. return 1;
  202. }
  203. static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
  204. {
  205. if (iface->conf->secondary_channel > 0) {
  206. iface->conf->channel += 4;
  207. iface->conf->secondary_channel = -1;
  208. } else {
  209. iface->conf->channel -= 4;
  210. iface->conf->secondary_channel = 1;
  211. }
  212. }
  213. static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface)
  214. {
  215. int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
  216. const struct hostapd_neighbor_bss *n;
  217. size_t i, num;
  218. int match;
  219. pri_chan = iface->conf->channel;
  220. sec_chan = iface->conf->secondary_channel * 4;
  221. pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
  222. if (iface->conf->secondary_channel > 0)
  223. sec_freq = pri_freq + 20;
  224. else
  225. sec_freq = pri_freq - 20;
  226. n = hostapd_driver_get_neighbor_bss(iface->bss[0], &num);
  227. /*
  228. * Switch PRI/SEC channels if Beacons were detected on selected SEC
  229. * channel, but not on selected PRI channel.
  230. */
  231. pri_bss = sec_bss = 0;
  232. for (i = 0; n && i < num; i++) {
  233. if (n[i].freq == pri_freq)
  234. pri_bss++;
  235. else if (n[i].freq == sec_freq)
  236. sec_bss++;
  237. }
  238. if (sec_bss && !pri_bss) {
  239. wpa_printf(MSG_INFO, "Switch own primary and secondary "
  240. "channel to get secondary channel with no Beacons "
  241. "from other BSSes");
  242. ieee80211n_switch_pri_sec(iface);
  243. }
  244. /*
  245. * Match PRI/SEC channel with any existing HT40 BSS on the same
  246. * channels that we are about to use (if already mixed order in
  247. * existing BSSes, use own preference).
  248. */
  249. match = 0;
  250. for (i = 0; n && i < num; i++) {
  251. if (pri_chan == n[i].pri_chan &&
  252. sec_chan == n[i].sec_chan) {
  253. match = 1;
  254. break;
  255. }
  256. }
  257. if (!match) {
  258. for (i = 0; n && i < num; i++) {
  259. if (pri_chan == n[i].sec_chan &&
  260. sec_chan == n[i].pri_chan) {
  261. wpa_printf(MSG_INFO, "Switch own primary and "
  262. "secondary channel due to BSS "
  263. "overlap with " MACSTR,
  264. MAC2STR(n[i].bssid));
  265. ieee80211n_switch_pri_sec(iface);
  266. break;
  267. }
  268. }
  269. }
  270. return 1;
  271. }
  272. static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface)
  273. {
  274. int pri_freq, sec_freq;
  275. int affected_start, affected_end;
  276. const struct hostapd_neighbor_bss *n;
  277. size_t i, num;
  278. pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
  279. if (iface->conf->secondary_channel > 0)
  280. sec_freq = pri_freq + 20;
  281. else
  282. sec_freq = pri_freq - 20;
  283. affected_start = (pri_freq + sec_freq) / 2 - 25;
  284. affected_end = (pri_freq + sec_freq) / 2 + 25;
  285. wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
  286. affected_start, affected_end);
  287. n = hostapd_driver_get_neighbor_bss(iface->bss[0], &num);
  288. for (i = 0; n && i < num; i++) {
  289. int pri = n[i].freq;
  290. int sec = pri;
  291. if (n[i].sec_chan) {
  292. if (n[i].sec_chan < n[i].pri_chan)
  293. sec = pri - 20;
  294. else
  295. sec = pri + 20;
  296. }
  297. if ((pri < affected_start || pri > affected_end) &&
  298. (sec < affected_start || sec > affected_end))
  299. continue; /* not within affected channel range */
  300. if (n[i].sec_chan) {
  301. if (pri_freq != pri || sec_freq != sec) {
  302. wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
  303. "mismatch with BSS " MACSTR
  304. " <%d,%d> (chan=%d%c) vs. <%d,%d>",
  305. MAC2STR(n[i].bssid),
  306. pri, sec, n[i].pri_chan,
  307. sec > pri ? '+' : '-',
  308. pri_freq, sec_freq);
  309. return 0;
  310. }
  311. }
  312. /* TODO: 40 MHz intolerant */
  313. }
  314. return 1;
  315. }
  316. static void ieee80211n_check_40mhz(struct hostapd_iface *iface)
  317. {
  318. int oper40;
  319. if (!iface->conf->secondary_channel)
  320. return; /* HT40 not used */
  321. /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
  322. * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
  323. if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
  324. oper40 = ieee80211n_check_40mhz_5g(iface);
  325. else
  326. oper40 = ieee80211n_check_40mhz_2g4(iface);
  327. if (!oper40) {
  328. wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
  329. "channel pri=%d sec=%d based on overlapping BSSes",
  330. iface->conf->channel,
  331. iface->conf->channel +
  332. iface->conf->secondary_channel * 4);
  333. iface->conf->secondary_channel = 0;
  334. iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
  335. }
  336. }
  337. static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
  338. {
  339. u16 hw = iface->current_mode->ht_capab;
  340. u16 conf = iface->conf->ht_capab;
  341. if (!iface->conf->ieee80211n)
  342. return 1;
  343. if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
  344. !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
  345. wpa_printf(MSG_ERROR, "Driver does not support configured "
  346. "HT capability [LDPC]");
  347. return 0;
  348. }
  349. if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
  350. !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
  351. wpa_printf(MSG_ERROR, "Driver does not support configured "
  352. "HT capability [HT40*]");
  353. return 0;
  354. }
  355. if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
  356. (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
  357. wpa_printf(MSG_ERROR, "Driver does not support configured "
  358. "HT capability [SMPS-*]");
  359. return 0;
  360. }
  361. if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
  362. !(hw & HT_CAP_INFO_GREEN_FIELD)) {
  363. wpa_printf(MSG_ERROR, "Driver does not support configured "
  364. "HT capability [GF]");
  365. return 0;
  366. }
  367. if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
  368. !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
  369. wpa_printf(MSG_ERROR, "Driver does not support configured "
  370. "HT capability [SHORT-GI-20]");
  371. return 0;
  372. }
  373. if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
  374. !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
  375. wpa_printf(MSG_ERROR, "Driver does not support configured "
  376. "HT capability [SHORT-GI-40]");
  377. return 0;
  378. }
  379. if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
  380. wpa_printf(MSG_ERROR, "Driver does not support configured "
  381. "HT capability [TX-STBC]");
  382. return 0;
  383. }
  384. if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
  385. (hw & HT_CAP_INFO_RX_STBC_MASK)) {
  386. wpa_printf(MSG_ERROR, "Driver does not support configured "
  387. "HT capability [RX-STBC*]");
  388. return 0;
  389. }
  390. if ((conf & HT_CAP_INFO_DELAYED_BA) &&
  391. !(hw & HT_CAP_INFO_DELAYED_BA)) {
  392. wpa_printf(MSG_ERROR, "Driver does not support configured "
  393. "HT capability [DELAYED-BA]");
  394. return 0;
  395. }
  396. if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
  397. !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
  398. wpa_printf(MSG_ERROR, "Driver does not support configured "
  399. "HT capability [MAX-AMSDU-7935]");
  400. return 0;
  401. }
  402. if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
  403. !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
  404. wpa_printf(MSG_ERROR, "Driver does not support configured "
  405. "HT capability [DSSS_CCK-40]");
  406. return 0;
  407. }
  408. if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
  409. wpa_printf(MSG_ERROR, "Driver does not support configured "
  410. "HT capability [PSMP]");
  411. return 0;
  412. }
  413. if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
  414. !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
  415. wpa_printf(MSG_ERROR, "Driver does not support configured "
  416. "HT capability [LSIG-TXOP-PROT]");
  417. return 0;
  418. }
  419. return 1;
  420. }
  421. #endif /* CONFIG_IEEE80211N */
  422. /**
  423. * hostapd_select_hw_mode - Select the hardware mode
  424. * @iface: Pointer to interface data.
  425. * Returns: 0 on success, -1 on failure
  426. *
  427. * Sets up the hardware mode, channel, rates, and passive scanning
  428. * based on the configuration.
  429. */
  430. int hostapd_select_hw_mode(struct hostapd_iface *iface)
  431. {
  432. int i, j, ok, ret;
  433. if (iface->num_hw_features < 1)
  434. return -1;
  435. iface->current_mode = NULL;
  436. for (i = 0; i < iface->num_hw_features; i++) {
  437. struct hostapd_hw_modes *mode = &iface->hw_features[i];
  438. if (mode->mode == (int) iface->conf->hw_mode) {
  439. iface->current_mode = mode;
  440. break;
  441. }
  442. }
  443. if (iface->current_mode == NULL) {
  444. wpa_printf(MSG_ERROR, "Hardware does not support configured "
  445. "mode");
  446. hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
  447. HOSTAPD_LEVEL_WARNING,
  448. "Hardware does not support configured mode "
  449. "(%d)", (int) iface->conf->hw_mode);
  450. return -1;
  451. }
  452. ok = 0;
  453. for (j = 0; j < iface->current_mode->num_channels; j++) {
  454. struct hostapd_channel_data *chan =
  455. &iface->current_mode->channels[j];
  456. if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
  457. (chan->chan == iface->conf->channel)) {
  458. ok = 1;
  459. break;
  460. }
  461. }
  462. if (ok == 0 && iface->conf->channel != 0) {
  463. hostapd_logger(iface->bss[0], NULL,
  464. HOSTAPD_MODULE_IEEE80211,
  465. HOSTAPD_LEVEL_WARNING,
  466. "Configured channel (%d) not found from the "
  467. "channel list of current mode (%d) %s",
  468. iface->conf->channel,
  469. iface->current_mode->mode,
  470. hostapd_hw_mode_txt(iface->current_mode->mode));
  471. iface->current_mode = NULL;
  472. }
  473. if (iface->current_mode == NULL) {
  474. hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
  475. HOSTAPD_LEVEL_WARNING,
  476. "Hardware does not support configured channel");
  477. return -1;
  478. }
  479. #ifdef CONFIG_IEEE80211N
  480. ieee80211n_check_40mhz(iface);
  481. if (!ieee80211n_allowed_ht40_channel_pair(iface))
  482. return -1;
  483. if (!ieee80211n_supported_ht_capab(iface))
  484. return -1;
  485. #endif /* CONFIG_IEEE80211N */
  486. if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) {
  487. wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
  488. hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
  489. HOSTAPD_LEVEL_WARNING,
  490. "Failed to prepare rates table.");
  491. return -1;
  492. }
  493. ret = hostapd_passive_scan(iface->bss[0], 0,
  494. iface->conf->passive_scan_mode,
  495. iface->conf->passive_scan_interval,
  496. iface->conf->passive_scan_listen,
  497. NULL, NULL);
  498. if (ret) {
  499. if (ret == -1) {
  500. wpa_printf(MSG_DEBUG, "Passive scanning not "
  501. "supported");
  502. } else {
  503. wpa_printf(MSG_ERROR, "Could not set passive "
  504. "scanning: %s", strerror(ret));
  505. }
  506. ret = 0;
  507. }
  508. return ret;
  509. }
  510. const char * hostapd_hw_mode_txt(int mode)
  511. {
  512. switch (mode) {
  513. case HOSTAPD_MODE_IEEE80211A:
  514. return "IEEE 802.11a";
  515. case HOSTAPD_MODE_IEEE80211B:
  516. return "IEEE 802.11b";
  517. case HOSTAPD_MODE_IEEE80211G:
  518. return "IEEE 802.11g";
  519. default:
  520. return "UNKNOWN";
  521. }
  522. }
  523. int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
  524. {
  525. int i;
  526. if (!hapd->iface->current_mode)
  527. return 0;
  528. for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
  529. struct hostapd_channel_data *ch =
  530. &hapd->iface->current_mode->channels[i];
  531. if (ch->chan == chan)
  532. return ch->freq;
  533. }
  534. return 0;
  535. }
  536. int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
  537. {
  538. int i;
  539. if (!hapd->iface->current_mode)
  540. return 0;
  541. for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
  542. struct hostapd_channel_data *ch =
  543. &hapd->iface->current_mode->channels[i];
  544. if (ch->freq == freq)
  545. return ch->chan;
  546. }
  547. return 0;
  548. }