Updates spec file version
[platform/upstream/crda.git] / intersect.c
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <sys/mman.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <arpa/inet.h> /* ntohl */
8 #include <string.h>
9
10 #include "reglib.h"
11
12 /* Intersects regulatory domains, this will skip any regulatory marked with
13  * an alpha2 of '00', which is used to indicate a regulatory domain */
14
15 #define BUG_ON(foo) do { \
16         if (foo) { \
17                 printf("BUG\n"); \
18                 exit(-1); \
19         } \
20         } while (0)
21
22 /* Sanity check on a regulatory rule */
23 static int is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
24 {
25         const struct ieee80211_freq_range *freq_range = &rule->freq_range;
26         __u32 freq_diff;
27
28         if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0)
29                 return 0;
30
31         if (freq_range->start_freq_khz > freq_range->end_freq_khz)
32                 return 0;
33
34         freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
35
36         if (freq_diff == 0 || freq_range->max_bandwidth_khz > freq_diff)
37                 return 0;
38
39         return 1;
40 }
41
42 /* Helper for regdom_intersect(), this does the real
43  * mathematical intersection fun */
44 static int reg_rules_intersect(
45         struct ieee80211_reg_rule *rule1,
46         struct ieee80211_reg_rule *rule2,
47         struct ieee80211_reg_rule *intersected_rule)
48 {
49         struct ieee80211_freq_range *freq_range1, *freq_range2, *freq_range;
50         struct ieee80211_power_rule *power_rule1, *power_rule2, *power_rule;
51         __u32 freq_diff;
52
53         freq_range1 = &rule1->freq_range;
54         freq_range2 = &rule2->freq_range;
55         freq_range = &intersected_rule->freq_range;
56
57         power_rule1 = &rule1->power_rule;
58         power_rule2 = &rule2->power_rule;
59         power_rule = &intersected_rule->power_rule;
60
61         freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
62                 freq_range2->start_freq_khz);
63         freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
64                 freq_range2->end_freq_khz);
65         freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
66                 freq_range2->max_bandwidth_khz);
67
68         freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
69         if (freq_range->max_bandwidth_khz > freq_diff)
70                 freq_range->max_bandwidth_khz = freq_diff;
71
72         power_rule->max_eirp = min(power_rule1->max_eirp,
73                 power_rule2->max_eirp);
74         power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
75                 power_rule2->max_antenna_gain);
76
77         intersected_rule->flags = (rule1->flags | rule2->flags);
78
79         if (!is_valid_reg_rule(intersected_rule))
80                 return -EINVAL;
81
82         return 0;
83 }
84
85 /**
86  * regdom_intersect - do the intersection between two regulatory domains
87  * @rd1: first regulatory domain
88  * @rd2: second regulatory domain
89  *
90  * Use this function to get the intersection between two regulatory domains.
91  * Once completed we will mark the alpha2 for the rd as intersected, "98",
92  * as no one single alpha2 can represent this regulatory domain.
93  *
94  * Returns a pointer to the regulatory domain structure which will hold the
95  * resulting intersection of rules between rd1 and rd2. We will
96  * malloc() this structure for you.
97  */
98 static struct ieee80211_regdomain *regdom_intersect(
99         struct ieee80211_regdomain *rd1,
100         struct ieee80211_regdomain *rd2)
101 {
102         int r, size_of_regd;
103         unsigned int x, y;
104         unsigned int num_rules = 0, rule_idx = 0;
105         struct ieee80211_reg_rule *rule1, *rule2, *intersected_rule;
106         struct ieee80211_regdomain *rd;
107         /* This is just a dummy holder to help us count */
108         struct ieee80211_reg_rule irule;
109
110         /* Uses the stack temporarily for counter arithmetic */
111         intersected_rule = &irule;
112
113         memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
114
115         if (!rd1 || !rd2) {
116                 fprintf(stderr, "rd1 or or rd2 is null\n");
117                 return NULL;
118         }
119
120         /* First we get a count of the rules we'll need, then we actually
121          * build them. This is to so we can malloc() and free() a
122          * regdomain once. The reason we use reg_rules_intersect() here
123          * is it will return -EINVAL if the rule computed makes no sense.
124          * All rules that do check out OK are valid. */
125
126         for (x = 0; x < rd1->n_reg_rules; x++) {
127                 rule1 = &rd1->reg_rules[x];
128                 for (y = 0; y < rd2->n_reg_rules; y++) {
129                         rule2 = &rd2->reg_rules[y];
130                         if (!reg_rules_intersect(rule1, rule2,
131                                         intersected_rule))
132                                 num_rules++;
133                         memset(intersected_rule, 0,
134                                         sizeof(struct ieee80211_reg_rule));
135                 }
136         }
137
138         if (!num_rules) {
139                 fprintf(stderr, "error: num_rules == 0\n");
140                 return NULL;
141         }
142
143         size_of_regd = sizeof(struct ieee80211_regdomain) +
144                 ((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
145
146         rd = malloc(size_of_regd);
147         if (!rd) {
148                 fprintf(stderr, "no memory left\n");
149                 return NULL;
150         }
151
152         memset(rd, 0, size_of_regd);
153
154         for (x = 0; x < rd1->n_reg_rules; x++) {
155                 rule1 = &rd1->reg_rules[x];
156                 for (y = 0; y < rd2->n_reg_rules; y++) {
157                         rule2 = &rd2->reg_rules[y];
158                         /* This time around instead of using the stack lets
159                          * write to the target rule directly saving ourselves
160                          * a memcpy() */
161                         intersected_rule = &rd->reg_rules[rule_idx];
162                         r = reg_rules_intersect(rule1, rule2,
163                                 intersected_rule);
164                         if (r)
165                                 continue;
166                         rule_idx++;
167                 }
168         }
169
170         if (rule_idx != num_rules) {
171                 fprintf(stderr, "Error while doing regdom interesection :(\n");
172                 free(rd);
173                 return NULL;
174         }
175
176         rd->n_reg_rules = num_rules;
177         rd->alpha2[0] = '9';
178         rd->alpha2[1] = '9';
179
180         return rd;
181 }
182
183 int main(int argc, char **argv)
184 {
185         int fd;
186         struct stat stat;
187         __u8 *db;
188         struct regdb_file_header *header;
189         struct regdb_file_reg_country *countries;
190         int dblen, siglen, num_countries, i, r = 0;
191         struct ieee80211_regdomain *prev_world = NULL, *rd = NULL, *world = NULL;
192         int intersected = 0;
193
194         if (argc != 2) {
195                 fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
196                 return 2;
197         }
198
199         fd = open(argv[1], O_RDONLY);
200         if (fd < 0) {
201                 perror("failed to open db file");
202                 return 2;
203         }
204
205         if (fstat(fd, &stat)) {
206                 perror("failed to fstat db file");
207                 return 2;
208         }
209
210         dblen = stat.st_size;
211
212         db = mmap(NULL, dblen, PROT_READ, MAP_PRIVATE, fd, 0);
213         if (db == MAP_FAILED) {
214                 perror("failed to mmap db file");
215                 return 2;
216         }
217
218         header = crda_get_file_ptr(db, dblen, sizeof(*header), 0);
219
220         if (ntohl(header->magic) != REGDB_MAGIC) {
221                 fprintf(stderr, "Invalid database magic\n");
222                 return 2;
223         }
224
225         if (ntohl(header->version) != REGDB_VERSION) {
226                 fprintf(stderr, "Invalid database version\n");
227                 return 2;
228         }
229
230         siglen = ntohl(header->signature_length);
231         /* adjust dblen so later sanity checks don't run into the signature */
232         dblen -= siglen;
233
234         if (dblen <= (int)sizeof(*header)) {
235                 fprintf(stderr, "Invalid signature length %d\n", siglen);
236                 return 2;
237         }
238
239         /* verify signature */
240         if (!crda_verify_db_signature(db, dblen, siglen))
241                 return -EINVAL;
242
243         num_countries = ntohl(header->reg_country_num);
244
245         if (num_countries <= 0)
246                 return 0;
247
248         countries = crda_get_file_ptr(db, dblen,
249                         sizeof(struct regdb_file_reg_country) * num_countries,
250                         header->reg_country_ptr);
251
252         /* We intersect only when we have to rd structures ready */
253         for (i = 0; i < num_countries; i++) {
254                 struct regdb_file_reg_country *country = countries + i;
255
256                 if (is_world_regdom((const char *) country->alpha2))
257                         continue;
258
259                 /* Gets the rd for the current country */
260                 rd = country2rd(db, dblen, country);
261                 if (!rd) {
262                         r = -ENOMEM;
263                         fprintf(stderr, "Could not covert country "
264                                 "(%.2s) to rd\n", country->alpha2);
265                         goto out;
266                 }
267
268                 if (num_countries == 1) {
269                         world = rd;
270                         rd = NULL;
271                         break;
272                 }
273
274                 if (!prev_world) {
275                         prev_world = rd;
276                         continue;
277                 }
278
279
280                 if (world) {
281                         free(prev_world);
282                         prev_world = world;
283                 }
284
285                 world = regdom_intersect(prev_world, rd);
286                 if (!world) {
287                         /* Could be something else but we'll live with this */
288                         r = -ENOMEM;
289                         if (intersected)
290                                 fprintf(stderr, "Could not intersect world "
291                                         "with country (%.2s)\n",
292                                         rd->alpha2);
293                         else
294                                 fprintf(stderr, "Could not intersect country (%.2s) "
295                                         "with country (%.2s)\n",
296                                         prev_world->alpha2,
297                                         rd->alpha2);
298                         goto out;
299                 }
300
301                 if (intersected)
302                         /* Use UTF-8 Intersection symbol ? (0xE2,0x88,0xA9) :) */
303                         printf("WW (%d) intersect %c%c (%d) ==> %d rules\n",
304                                 prev_world->n_reg_rules,
305                                 rd->alpha2[0],
306                                 rd->alpha2[1],
307                                 rd->n_reg_rules,
308                                 world->n_reg_rules);
309                 else
310                         printf("%c%c (%d) intersect %c%c (%d) ==> %d rules\n",
311                                 prev_world->alpha2[0],
312                                 prev_world->alpha2[1],
313                                 prev_world->n_reg_rules,
314                                 rd->alpha2[0],
315                                 rd->alpha2[1],
316                                 rd->n_reg_rules,
317                                 world->n_reg_rules);
318                 intersected++;
319         }
320
321         if (intersected > 1)
322                 printf("%d regulatory domains intersected\n", intersected);
323         else
324                 printf("Only one intersection completed\n");
325
326         /* Tada! */
327         printf("== World regulatory domain: ==\n");
328         print_regdom(world);
329
330 out:
331         if (!intersected) {
332                 free(world);
333                 return r;
334         }
335         if (intersected > 1) {
336                 free(rd);
337                 free(prev_world);
338         }
339         free(world);
340         return r;
341 }