bgscan_learn.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * WPA Supplicant - background scan and roaming module: learn
  3. * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * Alternatively, this software may be distributed under the terms of BSD
  10. * license.
  11. *
  12. * See README and COPYING for more details.
  13. */
  14. #include "includes.h"
  15. #include "common.h"
  16. #include "eloop.h"
  17. #include "drivers/driver.h"
  18. #include "config_ssid.h"
  19. #include "wpa_supplicant_i.h"
  20. #include "driver_i.h"
  21. #include "scan.h"
  22. #include "bgscan.h"
  23. struct bgscan_learn_data {
  24. struct wpa_supplicant *wpa_s;
  25. const struct wpa_ssid *ssid;
  26. int scan_interval;
  27. int signal_threshold;
  28. int short_interval; /* use if signal < threshold */
  29. int long_interval; /* use if signal > threshold */
  30. struct os_time last_bgscan;
  31. char *fname;
  32. };
  33. static int bgscan_learn_load(struct bgscan_learn_data *data)
  34. {
  35. FILE *f;
  36. char buf[128];
  37. if (data->fname == NULL)
  38. return 0;
  39. f = fopen(data->fname, "r");
  40. if (f == NULL)
  41. return 0;
  42. wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s",
  43. data->fname);
  44. if (fgets(buf, sizeof(buf), f) == NULL ||
  45. os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) {
  46. wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s",
  47. data->fname);
  48. fclose(f);
  49. return -1;
  50. }
  51. fclose(f);
  52. return 0;
  53. }
  54. static void bgscan_learn_save(struct bgscan_learn_data *data)
  55. {
  56. FILE *f;
  57. if (data->fname == NULL)
  58. return;
  59. wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s",
  60. data->fname);
  61. f = fopen(data->fname, "w");
  62. if (f == NULL)
  63. return;
  64. fprintf(f, "wpa_supplicant-bgscan-learn\n");
  65. fclose(f);
  66. }
  67. static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
  68. {
  69. struct bgscan_learn_data *data = eloop_ctx;
  70. struct wpa_supplicant *wpa_s = data->wpa_s;
  71. struct wpa_driver_scan_params params;
  72. os_memset(&params, 0, sizeof(params));
  73. params.num_ssids = 1;
  74. params.ssids[0].ssid = data->ssid->ssid;
  75. params.ssids[0].ssid_len = data->ssid->ssid_len;
  76. params.freqs = data->ssid->scan_freq;
  77. /*
  78. * A more advanced bgscan module would learn about most like channels
  79. * over time and request scans only for some channels (probing others
  80. * every now and then) to reduce effect on the data connection.
  81. */
  82. wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan");
  83. if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
  84. wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan");
  85. eloop_register_timeout(data->scan_interval, 0,
  86. bgscan_learn_timeout, data, NULL);
  87. } else
  88. os_get_time(&data->last_bgscan);
  89. }
  90. static int bgscan_learn_get_params(struct bgscan_learn_data *data,
  91. const char *params)
  92. {
  93. const char *pos;
  94. if (params == NULL)
  95. return 0;
  96. data->short_interval = atoi(params);
  97. pos = os_strchr(params, ':');
  98. if (pos == NULL)
  99. return 0;
  100. pos++;
  101. data->signal_threshold = atoi(pos);
  102. pos = os_strchr(pos, ':');
  103. if (pos == NULL) {
  104. wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval "
  105. "for high signal");
  106. return -1;
  107. }
  108. pos++;
  109. data->long_interval = atoi(pos);
  110. pos = os_strchr(pos, ':');
  111. if (pos) {
  112. pos++;
  113. data->fname = os_strdup(pos);
  114. }
  115. return 0;
  116. }
  117. static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
  118. const char *params,
  119. const struct wpa_ssid *ssid)
  120. {
  121. struct bgscan_learn_data *data;
  122. data = os_zalloc(sizeof(*data));
  123. if (data == NULL)
  124. return NULL;
  125. data->wpa_s = wpa_s;
  126. data->ssid = ssid;
  127. if (bgscan_learn_get_params(data, params) < 0) {
  128. os_free(data->fname);
  129. os_free(data);
  130. return NULL;
  131. }
  132. if (data->short_interval <= 0)
  133. data->short_interval = 30;
  134. if (data->long_interval <= 0)
  135. data->long_interval = 30;
  136. if (bgscan_learn_load(data) < 0) {
  137. os_free(data->fname);
  138. os_free(data);
  139. return NULL;
  140. }
  141. wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d "
  142. "Short bgscan interval %d Long bgscan interval %d",
  143. data->signal_threshold, data->short_interval,
  144. data->long_interval);
  145. if (data->signal_threshold &&
  146. wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
  147. wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable "
  148. "signal strength monitoring");
  149. }
  150. data->scan_interval = data->short_interval;
  151. eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
  152. data, NULL);
  153. return data;
  154. }
  155. static void bgscan_learn_deinit(void *priv)
  156. {
  157. struct bgscan_learn_data *data = priv;
  158. bgscan_learn_save(data);
  159. eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
  160. if (data->signal_threshold)
  161. wpa_drv_signal_monitor(data->wpa_s, 0, 0);
  162. os_free(data->fname);
  163. os_free(data);
  164. }
  165. static int bgscan_learn_notify_scan(void *priv,
  166. struct wpa_scan_results *scan_res)
  167. {
  168. struct bgscan_learn_data *data = priv;
  169. wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification");
  170. eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
  171. eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
  172. data, NULL);
  173. /*
  174. * A more advanced bgscan could process scan results internally, select
  175. * the BSS and request roam if needed. This sample uses the existing
  176. * BSS/ESS selection routine. Change this to return 1 if selection is
  177. * done inside the bgscan module.
  178. */
  179. return 0;
  180. }
  181. static void bgscan_learn_notify_beacon_loss(void *priv)
  182. {
  183. wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss");
  184. /* TODO: speed up background scanning */
  185. }
  186. static void bgscan_learn_notify_signal_change(void *priv, int above)
  187. {
  188. struct bgscan_learn_data *data = priv;
  189. if (data->short_interval == data->long_interval ||
  190. data->signal_threshold == 0)
  191. return;
  192. wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed "
  193. "(above=%d)", above);
  194. if (data->scan_interval == data->long_interval && !above) {
  195. wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan "
  196. "and start using short bgscan interval");
  197. data->scan_interval = data->short_interval;
  198. eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
  199. eloop_register_timeout(0, 0, bgscan_learn_timeout, data,
  200. NULL);
  201. } else if (data->scan_interval == data->short_interval && above) {
  202. wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan "
  203. "interval");
  204. data->scan_interval = data->long_interval;
  205. eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
  206. eloop_register_timeout(data->scan_interval, 0,
  207. bgscan_learn_timeout, data, NULL);
  208. } else if (!above) {
  209. struct os_time now;
  210. /*
  211. * Signal dropped further 4 dB. Request a new scan if we have
  212. * not yet scanned in a while.
  213. */
  214. os_get_time(&now);
  215. if (now.sec > data->last_bgscan.sec + 10) {
  216. wpa_printf(MSG_DEBUG, "bgscan learn: Trigger "
  217. "immediate scan");
  218. eloop_cancel_timeout(bgscan_learn_timeout, data,
  219. NULL);
  220. eloop_register_timeout(0, 0, bgscan_learn_timeout,
  221. data, NULL);
  222. }
  223. }
  224. }
  225. const struct bgscan_ops bgscan_learn_ops = {
  226. .name = "learn",
  227. .init = bgscan_learn_init,
  228. .deinit = bgscan_learn_deinit,
  229. .notify_scan = bgscan_learn_notify_scan,
  230. .notify_beacon_loss = bgscan_learn_notify_beacon_loss,
  231. .notify_signal_change = bgscan_learn_notify_signal_change,
  232. };