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