scan.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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/ieee80211.h>
  8. #include <net/cfg80211.h>
  9. #include "hermes.h"
  10. #include "orinoco.h"
  11. #include "main.h"
  12. #include "scan.h"
  13. #define ZERO_DBM_OFFSET 0x95
  14. #define MAX_SIGNAL_LEVEL 0x8A
  15. #define MIN_SIGNAL_LEVEL 0x2F
  16. #define SIGNAL_TO_DBM(x) \
  17. (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \
  18. - ZERO_DBM_OFFSET)
  19. #define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100)
  20. static int symbol_build_supp_rates(u8 *buf, const __le16 *rates)
  21. {
  22. int i;
  23. u8 rate;
  24. buf[0] = WLAN_EID_SUPP_RATES;
  25. for (i = 0; i < 5; i++) {
  26. rate = le16_to_cpu(rates[i]);
  27. /* NULL terminated */
  28. if (rate == 0x0)
  29. break;
  30. buf[i + 2] = rate;
  31. }
  32. buf[1] = i;
  33. return i + 2;
  34. }
  35. static int prism_build_supp_rates(u8 *buf, const u8 *rates)
  36. {
  37. int i;
  38. buf[0] = WLAN_EID_SUPP_RATES;
  39. for (i = 0; i < 8; i++) {
  40. /* NULL terminated */
  41. if (rates[i] == 0x0)
  42. break;
  43. buf[i + 2] = rates[i];
  44. }
  45. buf[1] = i;
  46. /* We might still have another 2 rates, which need to go in
  47. * extended supported rates */
  48. if (i == 8 && rates[i] > 0) {
  49. buf[10] = WLAN_EID_EXT_SUPP_RATES;
  50. for (; i < 10; i++) {
  51. /* NULL terminated */
  52. if (rates[i] == 0x0)
  53. break;
  54. buf[i + 2] = rates[i];
  55. }
  56. buf[11] = i - 8;
  57. }
  58. return (i < 8) ? i + 2 : i + 4;
  59. }
  60. static void orinoco_add_hostscan_result(struct orinoco_private *priv,
  61. const union hermes_scan_info *bss)
  62. {
  63. struct wiphy *wiphy = priv_to_wiphy(priv);
  64. struct ieee80211_channel *channel;
  65. u8 *ie;
  66. u8 ie_buf[46];
  67. u64 timestamp;
  68. s32 signal;
  69. u16 capability;
  70. u16 beacon_interval;
  71. int ie_len;
  72. int freq;
  73. int len;
  74. len = le16_to_cpu(bss->a.essid_len);
  75. /* Reconstruct SSID and bitrate IEs to pass up */
  76. ie_buf[0] = WLAN_EID_SSID;
  77. ie_buf[1] = len;
  78. memcpy(&ie_buf[2], bss->a.essid, len);
  79. ie = ie_buf + len + 2;
  80. ie_len = ie_buf[1] + 2;
  81. switch (priv->firmware_type) {
  82. case FIRMWARE_TYPE_SYMBOL:
  83. ie_len += symbol_build_supp_rates(ie, bss->s.rates);
  84. break;
  85. case FIRMWARE_TYPE_INTERSIL:
  86. ie_len += prism_build_supp_rates(ie, bss->p.rates);
  87. break;
  88. case FIRMWARE_TYPE_AGERE:
  89. default:
  90. break;
  91. }
  92. freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel));
  93. channel = ieee80211_get_channel(wiphy, freq);
  94. timestamp = 0;
  95. capability = le16_to_cpu(bss->a.capabilities);
  96. beacon_interval = le16_to_cpu(bss->a.beacon_interv);
  97. signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level));
  98. cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp,
  99. capability, beacon_interval, ie_buf, ie_len,
  100. signal, GFP_KERNEL);
  101. }
  102. void orinoco_add_extscan_result(struct orinoco_private *priv,
  103. struct agere_ext_scan_info *bss,
  104. size_t len)
  105. {
  106. struct wiphy *wiphy = priv_to_wiphy(priv);
  107. struct ieee80211_channel *channel;
  108. u8 *ie;
  109. u64 timestamp;
  110. s32 signal;
  111. u16 capability;
  112. u16 beacon_interval;
  113. size_t ie_len;
  114. int chan, freq;
  115. ie_len = len - sizeof(*bss);
  116. ie = orinoco_get_ie(bss->data, ie_len, WLAN_EID_DS_PARAMS);
  117. chan = ie ? ie[2] : 0;
  118. freq = ieee80211_dsss_chan_to_freq(chan);
  119. channel = ieee80211_get_channel(wiphy, freq);
  120. timestamp = le64_to_cpu(bss->timestamp);
  121. capability = le16_to_cpu(bss->capabilities);
  122. beacon_interval = le16_to_cpu(bss->beacon_interval);
  123. ie = bss->data;
  124. signal = SIGNAL_TO_MBM(bss->level);
  125. cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp,
  126. capability, beacon_interval, ie, ie_len,
  127. signal, GFP_KERNEL);
  128. }
  129. void orinoco_add_hostscan_results(struct orinoco_private *priv,
  130. unsigned char *buf,
  131. size_t len)
  132. {
  133. int offset; /* In the scan data */
  134. size_t atom_len;
  135. bool abort = false;
  136. switch (priv->firmware_type) {
  137. case FIRMWARE_TYPE_AGERE:
  138. atom_len = sizeof(struct agere_scan_apinfo);
  139. offset = 0;
  140. break;
  141. case FIRMWARE_TYPE_SYMBOL:
  142. /* Lack of documentation necessitates this hack.
  143. * Different firmwares have 68 or 76 byte long atoms.
  144. * We try modulo first. If the length divides by both,
  145. * we check what would be the channel in the second
  146. * frame for a 68-byte atom. 76-byte atoms have 0 there.
  147. * Valid channel cannot be 0. */
  148. if (len % 76)
  149. atom_len = 68;
  150. else if (len % 68)
  151. atom_len = 76;
  152. else if (len >= 1292 && buf[68] == 0)
  153. atom_len = 76;
  154. else
  155. atom_len = 68;
  156. offset = 0;
  157. break;
  158. case FIRMWARE_TYPE_INTERSIL:
  159. offset = 4;
  160. if (priv->has_hostscan) {
  161. atom_len = le16_to_cpup((__le16 *)buf);
  162. /* Sanity check for atom_len */
  163. if (atom_len < sizeof(struct prism2_scan_apinfo)) {
  164. printk(KERN_ERR "%s: Invalid atom_len in scan "
  165. "data: %zu\n", priv->ndev->name,
  166. atom_len);
  167. abort = true;
  168. goto scan_abort;
  169. }
  170. } else
  171. atom_len = offsetof(struct prism2_scan_apinfo, atim);
  172. break;
  173. default:
  174. abort = true;
  175. goto scan_abort;
  176. }
  177. /* Check that we got an whole number of atoms */
  178. if ((len - offset) % atom_len) {
  179. printk(KERN_ERR "%s: Unexpected scan data length %zu, "
  180. "atom_len %zu, offset %d\n", priv->ndev->name, len,
  181. atom_len, offset);
  182. abort = true;
  183. goto scan_abort;
  184. }
  185. /* Process the entries one by one */
  186. for (; offset + atom_len <= len; offset += atom_len) {
  187. union hermes_scan_info *atom;
  188. atom = (union hermes_scan_info *) (buf + offset);
  189. orinoco_add_hostscan_result(priv, atom);
  190. }
  191. scan_abort:
  192. if (priv->scan_request) {
  193. cfg80211_scan_done(priv->scan_request, abort);
  194. priv->scan_request = NULL;
  195. }
  196. }