]>
Commit | Line | Data |
---|---|---|
b22128ef JM |
1 | /* |
2 | * P2P - generic helper functions | |
3 | * Copyright (c) 2009, Atheros Communications | |
4 | * | |
e22d4d95 JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
b22128ef JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
12 | #include "p2p_i.h" | |
13 | ||
14 | ||
15 | /** | |
16 | * p2p_random - Generate random string for SSID and passphrase | |
17 | * @buf: Buffer for returning the result | |
18 | * @len: Number of octets to write to the buffer | |
19 | * Returns: 0 on success, -1 on failure | |
20 | * | |
21 | * This function generates a random string using the following character set: | |
22 | * 'A'-'Z', 'a'-'z', '0'-'9'. | |
23 | */ | |
24 | int p2p_random(char *buf, size_t len) | |
25 | { | |
26 | u8 val; | |
27 | size_t i; | |
28 | u8 letters = 'Z' - 'A' + 1; | |
29 | u8 numbers = 10; | |
30 | ||
31 | if (os_get_random((unsigned char *) buf, len)) | |
32 | return -1; | |
33 | /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */ | |
34 | for (i = 0; i < len; i++) { | |
35 | val = buf[i]; | |
36 | val %= 2 * letters + numbers; | |
37 | if (val < letters) | |
38 | buf[i] = 'A' + val; | |
39 | else if (val < 2 * letters) | |
40 | buf[i] = 'a' + (val - letters); | |
41 | else | |
42 | buf[i] = '0' + (val - 2 * letters); | |
43 | } | |
44 | ||
45 | return 0; | |
46 | } | |
47 | ||
48 | ||
49 | static int p2p_channel_to_freq_j4(int reg_class, int channel) | |
50 | { | |
51 | /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */ | |
52 | /* TODO: more regulatory classes */ | |
53 | switch (reg_class) { | |
54 | case 81: | |
55 | /* channels 1..13 */ | |
56 | if (channel < 1 || channel > 13) | |
57 | return -1; | |
58 | return 2407 + 5 * channel; | |
59 | case 82: | |
60 | /* channel 14 */ | |
61 | if (channel != 14) | |
62 | return -1; | |
63 | return 2414 + 5 * channel; | |
64 | case 83: /* channels 1..9; 40 MHz */ | |
65 | case 84: /* channels 5..13; 40 MHz */ | |
66 | if (channel < 1 || channel > 13) | |
67 | return -1; | |
68 | return 2407 + 5 * channel; | |
69 | case 115: /* channels 36,40,44,48; indoor only */ | |
70 | case 118: /* channels 52,56,60,64; dfs */ | |
71 | if (channel < 36 || channel > 64) | |
72 | return -1; | |
73 | return 5000 + 5 * channel; | |
74 | case 124: /* channels 149,153,157,161 */ | |
75 | case 125: /* channels 149,153,157,161,165,169 */ | |
76 | if (channel < 149 || channel > 161) | |
77 | return -1; | |
78 | return 5000 + 5 * channel; | |
79 | case 116: /* channels 36,44; 40 MHz; indoor only */ | |
80 | case 117: /* channels 40,48; 40 MHz; indoor only */ | |
81 | case 119: /* channels 52,60; 40 MHz; dfs */ | |
82 | case 120: /* channels 56,64; 40 MHz; dfs */ | |
83 | if (channel < 36 || channel > 64) | |
84 | return -1; | |
85 | return 5000 + 5 * channel; | |
86 | case 126: /* channels 149,157; 40 MHz */ | |
87 | case 127: /* channels 153,161; 40 MHz */ | |
88 | if (channel < 149 || channel > 161) | |
89 | return -1; | |
90 | return 5000 + 5 * channel; | |
91 | } | |
92 | return -1; | |
93 | } | |
94 | ||
95 | ||
96 | /** | |
97 | * p2p_channel_to_freq - Convert channel info to frequency | |
98 | * @country: Country code | |
99 | * @reg_class: Regulatory class | |
100 | * @channel: Channel number | |
101 | * Returns: Frequency in MHz or -1 if the specified channel is unknown | |
102 | */ | |
103 | int p2p_channel_to_freq(const char *country, int reg_class, int channel) | |
104 | { | |
105 | if (country[2] == 0x04) | |
106 | return p2p_channel_to_freq_j4(reg_class, channel); | |
107 | ||
108 | /* These are mainly for backwards compatibility; to be removed */ | |
109 | switch (reg_class) { | |
110 | case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */ | |
111 | if (channel < 36 || channel > 48) | |
112 | return -1; | |
113 | return 5000 + 5 * channel; | |
114 | case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */ | |
115 | case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */ | |
116 | if (channel < 149 || channel > 161) | |
117 | return -1; | |
118 | return 5000 + 5 * channel; | |
119 | case 4: /* EU/4 = 2.407 GHz, channels 1..13 */ | |
120 | case 12: /* US/12 = 2.407 GHz, channels 1..11 */ | |
121 | case 30: /* JP/30 = 2.407 GHz, channels 1..13 */ | |
122 | if (channel < 1 || channel > 13) | |
123 | return -1; | |
124 | return 2407 + 5 * channel; | |
125 | case 31: /* JP/31 = 2.414 GHz, channel 14 */ | |
126 | if (channel != 14) | |
127 | return -1; | |
128 | return 2414 + 5 * channel; | |
129 | } | |
130 | ||
131 | return -1; | |
132 | } | |
133 | ||
134 | ||
135 | /** | |
136 | * p2p_freq_to_channel - Convert frequency into channel info | |
137 | * @country: Country code | |
138 | * @reg_class: Buffer for returning regulatory class | |
139 | * @channel: Buffer for returning channel number | |
140 | * Returns: 0 on success, -1 if the specified frequency is unknown | |
141 | */ | |
142 | int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, | |
143 | u8 *channel) | |
144 | { | |
145 | /* TODO: more operating classes */ | |
146 | if (freq >= 2412 && freq <= 2472) { | |
147 | *reg_class = 81; /* 2.407 GHz, channels 1..13 */ | |
148 | *channel = (freq - 2407) / 5; | |
149 | return 0; | |
150 | } | |
151 | ||
152 | if (freq == 2484) { | |
153 | *reg_class = 82; /* channel 14 */ | |
154 | *channel = 14; | |
155 | return 0; | |
156 | } | |
157 | ||
158 | if (freq >= 5180 && freq <= 5240) { | |
159 | *reg_class = 115; /* 5 GHz, channels 36..48 */ | |
160 | *channel = (freq - 5000) / 5; | |
161 | return 0; | |
162 | } | |
163 | ||
164 | if (freq >= 5745 && freq <= 5805) { | |
165 | *reg_class = 124; /* 5 GHz, channels 149..161 */ | |
166 | *channel = (freq - 5000) / 5; | |
167 | return 0; | |
168 | } | |
169 | ||
170 | return -1; | |
171 | } | |
172 | ||
173 | ||
174 | static void p2p_reg_class_intersect(const struct p2p_reg_class *a, | |
175 | const struct p2p_reg_class *b, | |
176 | struct p2p_reg_class *res) | |
177 | { | |
178 | size_t i, j; | |
179 | ||
180 | res->reg_class = a->reg_class; | |
181 | ||
182 | for (i = 0; i < a->channels; i++) { | |
183 | for (j = 0; j < b->channels; j++) { | |
184 | if (a->channel[i] != b->channel[j]) | |
185 | continue; | |
186 | res->channel[res->channels] = a->channel[i]; | |
187 | res->channels++; | |
188 | if (res->channels == P2P_MAX_REG_CLASS_CHANNELS) | |
189 | return; | |
190 | } | |
191 | } | |
192 | } | |
193 | ||
194 | ||
195 | /** | |
196 | * p2p_channels_intersect - Intersection of supported channel lists | |
197 | * @a: First set of supported channels | |
198 | * @b: Second set of supported channels | |
199 | * @res: Data structure for returning the intersection of support channels | |
200 | * | |
201 | * This function can be used to find a common set of supported channels. Both | |
202 | * input channels sets are assumed to use the same country code. If different | |
203 | * country codes are used, the regulatory class numbers may not be matched | |
204 | * correctly and results are undefined. | |
205 | */ | |
206 | void p2p_channels_intersect(const struct p2p_channels *a, | |
207 | const struct p2p_channels *b, | |
208 | struct p2p_channels *res) | |
209 | { | |
210 | size_t i, j; | |
211 | ||
212 | os_memset(res, 0, sizeof(*res)); | |
213 | ||
214 | for (i = 0; i < a->reg_classes; i++) { | |
215 | const struct p2p_reg_class *a_reg = &a->reg_class[i]; | |
216 | for (j = 0; j < b->reg_classes; j++) { | |
217 | const struct p2p_reg_class *b_reg = &b->reg_class[j]; | |
218 | if (a_reg->reg_class != b_reg->reg_class) | |
219 | continue; | |
220 | p2p_reg_class_intersect( | |
221 | a_reg, b_reg, | |
222 | &res->reg_class[res->reg_classes]); | |
223 | if (res->reg_class[res->reg_classes].channels) { | |
224 | res->reg_classes++; | |
225 | if (res->reg_classes == P2P_MAX_REG_CLASSES) | |
226 | return; | |
227 | } | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | ||
233 | /** | |
234 | * p2p_channels_includes - Check whether a channel is included in the list | |
235 | * @channels: List of supported channels | |
236 | * @reg_class: Regulatory class of the channel to search | |
237 | * @channel: Channel number of the channel to search | |
238 | * Returns: 1 if channel was found or 0 if not | |
239 | */ | |
240 | int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, | |
241 | u8 channel) | |
242 | { | |
243 | size_t i, j; | |
244 | for (i = 0; i < channels->reg_classes; i++) { | |
245 | const struct p2p_reg_class *reg = &channels->reg_class[i]; | |
246 | if (reg->reg_class != reg_class) | |
247 | continue; | |
248 | for (j = 0; j < reg->channels; j++) { | |
249 | if (reg->channel[j] == channel) | |
250 | return 1; | |
251 | } | |
252 | } | |
253 | return 0; | |
254 | } | |
d054a462 JM |
255 | |
256 | ||
257 | int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) | |
258 | { | |
259 | u8 op_reg_class, op_channel; | |
260 | if (p2p_freq_to_channel(p2p->cfg->country, freq, | |
261 | &op_reg_class, &op_channel) < 0) | |
262 | return 0; | |
263 | return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, | |
264 | op_channel); | |
265 | } |