scan.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /* Helpers for managing scan queues
  2. *
  3. * See copyright notice in main.c
  4. */
  5. #include <linux/kernel.h>
  6. #include <linux/string.h>
  7. #include <linux/etherdevice.h>
  8. #include "hermes.h"
  9. #include "orinoco.h"
  10. #include "scan.h"
  11. #define ORINOCO_MAX_BSS_COUNT 64
  12. #define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data)
  13. #define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data)
  14. int orinoco_bss_data_allocate(struct orinoco_private *priv)
  15. {
  16. if (priv->bss_xbss_data)
  17. return 0;
  18. if (priv->has_ext_scan)
  19. priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
  20. sizeof(struct xbss_element),
  21. GFP_KERNEL);
  22. else
  23. priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
  24. sizeof(struct bss_element),
  25. GFP_KERNEL);
  26. if (!priv->bss_xbss_data) {
  27. printk(KERN_WARNING "Out of memory allocating beacons");
  28. return -ENOMEM;
  29. }
  30. return 0;
  31. }
  32. void orinoco_bss_data_free(struct orinoco_private *priv)
  33. {
  34. kfree(priv->bss_xbss_data);
  35. priv->bss_xbss_data = NULL;
  36. }
  37. void orinoco_bss_data_init(struct orinoco_private *priv)
  38. {
  39. int i;
  40. INIT_LIST_HEAD(&priv->bss_free_list);
  41. INIT_LIST_HEAD(&priv->bss_list);
  42. if (priv->has_ext_scan)
  43. for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
  44. list_add_tail(&(PRIV_XBSS[i].list),
  45. &priv->bss_free_list);
  46. else
  47. for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
  48. list_add_tail(&(PRIV_BSS[i].list),
  49. &priv->bss_free_list);
  50. }
  51. void orinoco_clear_scan_results(struct orinoco_private *priv,
  52. unsigned long scan_age)
  53. {
  54. if (priv->has_ext_scan) {
  55. struct xbss_element *bss;
  56. struct xbss_element *tmp_bss;
  57. /* Blow away current list of scan results */
  58. list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
  59. if (!scan_age ||
  60. time_after(jiffies, bss->last_scanned + scan_age)) {
  61. list_move_tail(&bss->list,
  62. &priv->bss_free_list);
  63. /* Don't blow away ->list, just BSS data */
  64. memset(&bss->bss, 0, sizeof(bss->bss));
  65. bss->last_scanned = 0;
  66. }
  67. }
  68. } else {
  69. struct bss_element *bss;
  70. struct bss_element *tmp_bss;
  71. /* Blow away current list of scan results */
  72. list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
  73. if (!scan_age ||
  74. time_after(jiffies, bss->last_scanned + scan_age)) {
  75. list_move_tail(&bss->list,
  76. &priv->bss_free_list);
  77. /* Don't blow away ->list, just BSS data */
  78. memset(&bss->bss, 0, sizeof(bss->bss));
  79. bss->last_scanned = 0;
  80. }
  81. }
  82. }
  83. }
  84. void orinoco_add_ext_scan_result(struct orinoco_private *priv,
  85. struct agere_ext_scan_info *atom)
  86. {
  87. struct xbss_element *bss = NULL;
  88. int found = 0;
  89. /* Try to update an existing bss first */
  90. list_for_each_entry(bss, &priv->bss_list, list) {
  91. if (compare_ether_addr(bss->bss.bssid, atom->bssid))
  92. continue;
  93. /* ESSID lengths */
  94. if (bss->bss.data[1] != atom->data[1])
  95. continue;
  96. if (memcmp(&bss->bss.data[2], &atom->data[2],
  97. atom->data[1]))
  98. continue;
  99. found = 1;
  100. break;
  101. }
  102. /* Grab a bss off the free list */
  103. if (!found && !list_empty(&priv->bss_free_list)) {
  104. bss = list_entry(priv->bss_free_list.next,
  105. struct xbss_element, list);
  106. list_del(priv->bss_free_list.next);
  107. list_add_tail(&bss->list, &priv->bss_list);
  108. }
  109. if (bss) {
  110. /* Always update the BSS to get latest beacon info */
  111. memcpy(&bss->bss, atom, sizeof(bss->bss));
  112. bss->last_scanned = jiffies;
  113. }
  114. }
  115. int orinoco_process_scan_results(struct orinoco_private *priv,
  116. unsigned char *buf,
  117. int len)
  118. {
  119. int offset; /* In the scan data */
  120. union hermes_scan_info *atom;
  121. int atom_len;
  122. switch (priv->firmware_type) {
  123. case FIRMWARE_TYPE_AGERE:
  124. atom_len = sizeof(struct agere_scan_apinfo);
  125. offset = 0;
  126. break;
  127. case FIRMWARE_TYPE_SYMBOL:
  128. /* Lack of documentation necessitates this hack.
  129. * Different firmwares have 68 or 76 byte long atoms.
  130. * We try modulo first. If the length divides by both,
  131. * we check what would be the channel in the second
  132. * frame for a 68-byte atom. 76-byte atoms have 0 there.
  133. * Valid channel cannot be 0. */
  134. if (len % 76)
  135. atom_len = 68;
  136. else if (len % 68)
  137. atom_len = 76;
  138. else if (len >= 1292 && buf[68] == 0)
  139. atom_len = 76;
  140. else
  141. atom_len = 68;
  142. offset = 0;
  143. break;
  144. case FIRMWARE_TYPE_INTERSIL:
  145. offset = 4;
  146. if (priv->has_hostscan) {
  147. atom_len = le16_to_cpup((__le16 *)buf);
  148. /* Sanity check for atom_len */
  149. if (atom_len < sizeof(struct prism2_scan_apinfo)) {
  150. printk(KERN_ERR "%s: Invalid atom_len in scan "
  151. "data: %d\n", priv->ndev->name,
  152. atom_len);
  153. return -EIO;
  154. }
  155. } else
  156. atom_len = offsetof(struct prism2_scan_apinfo, atim);
  157. break;
  158. default:
  159. return -EOPNOTSUPP;
  160. }
  161. /* Check that we got an whole number of atoms */
  162. if ((len - offset) % atom_len) {
  163. printk(KERN_ERR "%s: Unexpected scan data length %d, "
  164. "atom_len %d, offset %d\n", priv->ndev->name, len,
  165. atom_len, offset);
  166. return -EIO;
  167. }
  168. orinoco_clear_scan_results(priv, msecs_to_jiffies(15000));
  169. /* Read the entries one by one */
  170. for (; offset + atom_len <= len; offset += atom_len) {
  171. int found = 0;
  172. struct bss_element *bss = NULL;
  173. /* Get next atom */
  174. atom = (union hermes_scan_info *) (buf + offset);
  175. /* Try to update an existing bss first */
  176. list_for_each_entry(bss, &priv->bss_list, list) {
  177. if (compare_ether_addr(bss->bss.a.bssid, atom->a.bssid))
  178. continue;
  179. if (le16_to_cpu(bss->bss.a.essid_len) !=
  180. le16_to_cpu(atom->a.essid_len))
  181. continue;
  182. if (memcmp(bss->bss.a.essid, atom->a.essid,
  183. le16_to_cpu(atom->a.essid_len)))
  184. continue;
  185. found = 1;
  186. break;
  187. }
  188. /* Grab a bss off the free list */
  189. if (!found && !list_empty(&priv->bss_free_list)) {
  190. bss = list_entry(priv->bss_free_list.next,
  191. struct bss_element, list);
  192. list_del(priv->bss_free_list.next);
  193. list_add_tail(&bss->list, &priv->bss_list);
  194. }
  195. if (bss) {
  196. /* Always update the BSS to get latest beacon info */
  197. memcpy(&bss->bss, atom, sizeof(bss->bss));
  198. bss->last_scanned = jiffies;
  199. }
  200. }
  201. return 0;
  202. }