• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * PLT utility for wireless chip supported by TI's driver wl12xx
3  *
4  * See README and COPYING for more details.
5  */
6 
7 #include <errno.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <net/if.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <stdbool.h>
16 
17 #include <netlink/genl/genl.h>
18 #include <netlink/genl/family.h>
19 #include <netlink/genl/ctrl.h>
20 #include <netlink/msg.h>
21 #include <netlink/attr.h>
22 
23 #include "nl80211.h"
24 #include "calibrator.h"
25 #include "plt.h"
26 #include "ini.h"
27 
28 char calibrator_version[] = "0.71";
29 #ifndef CONFIG_LIBNL20
30 /* libnl 2.0 compatibility code */
31 
nl_socket_alloc(void)32 static inline struct nl_handle *nl_socket_alloc(void)
33 {
34     return nl_handle_alloc();
35 }
36 
nl_socket_free(struct nl_sock * h)37 static inline void nl_socket_free(struct nl_sock *h)
38 {
39     nl_handle_destroy(h);
40 }
41 
__genl_ctrl_alloc_cache(struct nl_sock * h,struct nl_cache ** cache)42 static inline int __genl_ctrl_alloc_cache(struct nl_sock *h,
43     struct nl_cache **cache)
44 {
45     struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
46     if (!tmp) {
47         return -ENOMEM;
48     }
49     *cache = tmp;
50     return 0;
51 }
52 #define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
53 #endif /* CONFIG_LIBNL20 */
54 
55 int calibrator_debug;
56 
nl80211_init(struct nl80211_state * state)57 static int nl80211_init(struct nl80211_state *state)
58 {
59     int err;
60 
61     state->nl_sock = nl_socket_alloc();
62     if (!state->nl_sock) {
63         fprintf(stderr, "Failed to allocate netlink socket.\n");
64         return -ENOMEM;
65     }
66 
67     if (genl_connect(state->nl_sock)) {
68         fprintf(stderr, "Failed to connect to generic netlink.\n");
69         err = -ENOLINK;
70         goto out_handle_destroy;
71     }
72 
73     if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) {
74         fprintf(stderr, "Failed to allocate generic netlink cache.\n");
75         err = -ENOMEM;
76         goto out_handle_destroy;
77     }
78 
79     state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211");
80     if (!state->nl80211) {
81         fprintf(stderr, "nl80211 not found.\n");
82         err = -ENOENT;
83         goto out_cache_free;
84     }
85 
86     return 0;
87 
88  out_cache_free:
89     nl_cache_free(state->nl_cache);
90  out_handle_destroy:
91     nl_socket_free(state->nl_sock);
92     return err;
93 }
94 
nl80211_cleanup(struct nl80211_state * state)95 static void nl80211_cleanup(struct nl80211_state *state)
96 {
97     genl_family_put(state->nl80211);
98     nl_cache_free(state->nl_cache);
99     nl_socket_free(state->nl_sock);
100 }
101 
102 static int cmd_size;
103 
104 extern struct cmd __start___cmd;
105 extern struct cmd __stop___cmd;
106 
107 #define for_each_cmd(_cmd)                    \
108     for (_cmd = &__start___cmd; _cmd < &__stop___cmd;        \
109          _cmd = (const struct cmd *)((char *)_cmd + cmd_size))
110 
111 
__usage_cmd(const struct cmd * cmd,char * indent,bool full)112 static void __usage_cmd(const struct cmd *cmd, char *indent, bool full)
113 {
114     const char *start, *lend, *end;
115 
116     printf("%s", indent);
117 
118     switch (cmd->idby) {
119     case CIB_NONE:
120         break;
121     case CIB_PHY:
122         printf("phy <phyname> ");
123         break;
124     case CIB_NETDEV:
125         printf("dev <devname> ");
126         break;
127     }
128     if (cmd->parent && cmd->parent->name) {
129         printf("%s ", cmd->parent->name);
130     }
131     printf("%s", cmd->name);
132     if (cmd->args) {
133         printf(" %s", cmd->args);
134     }
135     printf("\n");
136 
137     if (!full || !cmd->help) {
138         return;
139     }
140 
141     /* hack */
142     if (strlen(indent)) {
143         indent = "\t\t";
144     }
145     else {
146         printf("\n");
147     }
148 
149     /* print line by line */
150     start = cmd->help;
151     end = strchr(start, '\0');
152     do {
153         lend = strchr(start, '\n');
154         if (!lend) {
155             lend = end;
156         }
157         printf("%s", indent);
158         printf("%.*s\n", (int)(lend - start), start);
159         start = lend + 1;
160     } while (end != lend);
161 
162     printf("\n");
163 }
164 
usage_options(void)165 static void usage_options(void)
166 {
167     printf("Options:\n");
168     printf("\t--debug\t\tenable netlink debugging\n");
169 }
170 
171 static const char *argv0;
172 
usage(bool full)173 static void usage(bool full)
174 {
175     const struct cmd *section, *cmd;
176 
177     printf("Usage:\t%s [options] command\n", argv0);
178     usage_options();
179     printf("\t--version\tshow version (%s)\n", calibrator_version);
180     printf("Commands:\n");
181     for_each_cmd(section) {
182         if (section->parent) {
183             continue;
184         }
185 
186         if (section->handler && !section->hidden) {
187             __usage_cmd(section, "\t", full);
188         }
189 
190         for_each_cmd(cmd) {
191             if (section != cmd->parent) {
192                 continue;
193             }
194             if (!cmd->handler || cmd->hidden) {
195                 continue;
196             }
197             __usage_cmd(cmd, "\t", full);
198         }
199     }
200 #if 0
201     printf("\nYou can omit the 'phy' or 'dev' if "
202             "the identification is unique,\n"
203             "e.g. \"iw wlan0 info\" or \"iw phy0 info\". "
204             "(Don't when scripting.)\n\n"
205             "Do NOT screenscrape this tool, we don't "
206             "consider its output stable.\n\n");
207 #endif
208 }
209 
print_help(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv)210 static int print_help(struct nl80211_state *state,
211               struct nl_cb *cb,
212               struct nl_msg *msg,
213               int argc, char **argv)
214 {
215     exit(3);
216 }
217 TOPLEVEL(help, NULL, 0, 0, CIB_NONE, print_help,
218      "Print usage for each command.");
219 
usage_cmd(const struct cmd * cmd)220 static void usage_cmd(const struct cmd *cmd)
221 {
222     printf("\nUsage:\t%s [options] ", argv0);
223     __usage_cmd(cmd, "", true);
224     usage_options();
225 }
226 
version(void)227 static void version(void)
228 {
229     printf("calibrator version %s\n", calibrator_version);
230 }
231 
phy_lookup(char * name)232 static int phy_lookup(char *name)
233 {
234     char buf[200];
235     int fd, pos;
236 
237     snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
238 
239     fd = open(buf, O_RDONLY);
240     if (fd < 0) {
241         return -1;
242     }
243     pos = read(fd, buf, sizeof(buf) - 1);
244     if (pos < 0) {
245         close(fd);
246         return -1;
247     }
248     buf[pos] = '\0';
249     close(fd);
250     return atoi(buf);
251 }
252 
error_handler(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)253 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
254              void *arg)
255 {
256     int *ret = arg;
257     *ret = err->error;
258 
259     return NL_STOP;
260 }
261 
finish_handler(struct nl_msg * msg,void * arg)262 static int finish_handler(struct nl_msg *msg, void *arg)
263 {
264     int *ret = arg;
265     *ret = 0;
266 
267     return NL_SKIP;
268 }
269 
ack_handler(struct nl_msg * msg,void * arg)270 static int ack_handler(struct nl_msg *msg, void *arg)
271 {
272     int *ret = arg;
273     *ret = 0;
274 
275     return NL_STOP;
276 }
277 
__handle_cmd(struct nl80211_state * state,enum id_input idby,int argc,char ** argv,const struct cmd ** cmdout)278 static int __handle_cmd(struct nl80211_state *state, enum id_input idby,
279             int argc, char **argv, const struct cmd **cmdout)
280 {
281     const struct cmd *cmd, *match = NULL, *sectcmd;
282     struct nl_cb *cb;
283     struct nl_msg *msg;
284     int devidx = 0;
285     int err, o_argc;
286     const char *command, *section;
287     char *tmp, **o_argv;
288     enum command_identify_by command_idby = CIB_NONE;
289 #if 0
290     if (file_exist(CURRENT_NVS_NAME) < 0) {
291         fprintf(stderr, "\n\tUnable to find NVS file (%s).\n\t"
292             "Make sure to use reference-nvs.bin instead.\n\n",
293             CURRENT_NVS_NAME);
294         return 2;
295     }
296 #endif
297     if (argc <= 1) {
298         return 1;
299     }
300 
301     o_argc = argc;
302     o_argv = argv;
303 
304     switch (idby) {
305     case II_PHY_IDX:
306         command_idby = CIB_PHY;
307         devidx = strtoul(*argv + 4, &tmp, 0);
308         if (*tmp != '\0') {
309             return 1;
310         }
311         argc--;
312         argv++;
313         break;
314     case II_PHY_NAME:
315         command_idby = CIB_PHY;
316         devidx = phy_lookup(*argv);
317         argc--;
318         argv++;
319         break;
320     case II_NETDEV:
321         command_idby = CIB_NETDEV;
322         devidx = if_nametoindex(*argv);
323         if (devidx == 0) {
324             devidx = -1;
325         }
326         argc--;
327         argv++;
328         break;
329     default:
330         break;
331     }
332 
333     if (devidx < 0) {
334         return -errno;
335     }
336 
337     section = *argv;
338     argc--;
339     argv++;
340 
341     for_each_cmd(sectcmd) {
342         if (sectcmd->parent) {
343             continue;
344         }
345         /* ok ... bit of a hack for the dupe 'info' section */
346         if (match && sectcmd->idby != command_idby) {
347             continue;
348         }
349 
350         if (strcmp(sectcmd->name, section) == 0) {
351             match = sectcmd;
352         }
353     }
354 
355     sectcmd = match;
356     match = NULL;
357     if (!sectcmd) {
358         return 1;
359     }
360 
361     if (argc > 0) {
362         command = *argv;
363 
364         for_each_cmd(cmd) {
365             if (!cmd->handler) {
366                 continue;
367             }
368             if (cmd->parent != sectcmd) {
369                 continue;
370             }
371             if (cmd->idby != command_idby) {
372                 continue;
373             }
374             if (strcmp(cmd->name, command)) {
375                 continue;
376             }
377             if (argc > 1 && !cmd->args) {
378                 continue;
379             }
380             match = cmd;
381             break;
382         }
383 
384         if (match) {
385             argc--;
386             argv++;
387         }
388     }
389 
390 
391     if (match) {
392         cmd = match;
393     } else {
394         /* Use the section itself, if possible. */
395         cmd = sectcmd;
396         if (argc && !cmd->args) {
397             return 1;
398         }
399         if (cmd->idby != command_idby) {
400             return 1;
401         }
402         if (!cmd->handler) {
403             return 1;
404         }
405     }
406 
407     if (cmdout) {
408         *cmdout = cmd;
409     }
410 
411     if (!cmd->cmd) {
412         argc = o_argc;
413         argv = o_argv;
414         return cmd->handler(state, NULL, NULL, argc, argv);
415     }
416 
417     msg = nlmsg_alloc();
418     if (!msg) {
419         fprintf(stderr, "failed to allocate netlink message\n");
420         return 2;
421     }
422 
423     cb = nl_cb_alloc(calibrator_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
424     if (!cb) {
425         fprintf(stderr, "failed to allocate netlink callbacks\n");
426         err = 2;
427         goto out_free_msg;
428     }
429 
430     genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
431             cmd->nl_msg_flags, cmd->cmd, 0);
432 
433     switch (command_idby) {
434     case CIB_PHY:
435         NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
436         break;
437     case CIB_NETDEV:
438         NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
439         break;
440     default:
441         break;
442     }
443 
444     err = cmd->handler(state, cb, msg, argc, argv);
445     if (err) {
446         fprintf(stderr, "failed to handle\n");
447         goto out;
448     }
449 
450     err = nl_send_auto_complete(state->nl_sock, msg);
451     if (err < 0) {
452         fprintf(stderr, "failed to autocomplete\n");
453         goto out;
454     }
455 
456     err = 1;
457 
458     nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
459     nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
460     nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
461 
462     while (err > 0)
463         nl_recvmsgs(state->nl_sock, cb);
464 
465  out:
466     nl_cb_put(cb);
467  out_free_msg:
468     nlmsg_free(msg);
469     return err;
470 
471  nla_put_failure:
472     fprintf(stderr, "building message failed\n");
473     return 2;
474 }
475 
handle_cmd(struct nl80211_state * state,enum id_input idby,int argc,char ** argv)476 int handle_cmd(struct nl80211_state *state, enum id_input idby,
477            int argc, char **argv)
478 {
479     return __handle_cmd(state, idby, argc, argv, NULL);
480 }
481 
main(int argc,char ** argv)482 int main(int argc, char **argv)
483 {
484     struct nl80211_state nlstate;
485     int err;
486     const struct cmd *cmd = NULL;
487 
488     /* calculate command size including padding */
489     cmd_size = abs((long)&__section_set - (long)&__section_get);
490     /* strip off self */
491     argc--;
492     argv0 = *argv++;
493 
494     if (argc > 0 && strcmp(*argv, "--debug") == 0) {
495         calibrator_debug = 1;
496         argc--;
497         argv++;
498     }
499 
500     if (argc > 0 && strcmp(*argv, "--version") == 0) {
501         version();
502         return 0;
503     }
504 
505     /* need to treat "help" command specially so it works w/o nl80211 */
506     if (argc == 0 || strcmp(*argv, "help") == 0) {
507         usage(argc != 0);
508         return 0;
509     }
510 
511     err = nl80211_init(&nlstate);
512     if (err) {
513         return 1;
514     }
515 
516     if (strcmp(*argv, "dev") == 0 && argc > 1) {
517         argc--;
518         argv++;
519         err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd);
520     } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) {
521         if (strlen(*argv) == 3) {
522             argc--;
523             argv++;
524             err = __handle_cmd(&nlstate, II_PHY_NAME,
525                 argc, argv, &cmd);
526         } else if (*(*argv + 3) == '#')
527             err = __handle_cmd(&nlstate, II_PHY_IDX,
528                 argc, argv, &cmd);
529         else
530             goto detect;
531     } else {
532         int idx;
533         enum id_input idby = II_NONE;
534  detect:
535         idx = if_nametoindex(argv[0]);
536         if (idx != 0) {
537             idby = II_NETDEV;
538         } else {
539             idx = phy_lookup(argv[0]);
540             if (idx >= 0) {
541                 idby = II_PHY_NAME;
542             }
543         }
544 
545         err = __handle_cmd(&nlstate, idby, argc, argv, &cmd);
546     }
547 
548     if (err == 1) {
549         if (cmd) {
550             usage_cmd(cmd);
551         }
552         else
553             usage(false);
554     } else if (err < 0)
555         fprintf(stderr, "command failed: %s (%d)\n",
556             strerror(-err), err);
557 
558     nl80211_cleanup(&nlstate);
559 
560     return err;
561 }
562