nfctool: Add a handler mechanism for netlink events
[platform/upstream/neard.git] / tools / nfctool / main.c
1 /*
2  *
3  *  Near Field Communication nfctool
4  *
5  *  Copyright (C) 2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <glib.h>
28
29 #include <near/nfc_copy.h>
30
31 #include "nfctool.h"
32 #include "adapter.h"
33 #include "netlink.h"
34 #include "sniffer.h"
35
36 #define LLCP_MAX_LTO  0xff
37 #define LLCP_MAX_RW   0x0f
38 #define LLCP_MAX_MIUX 0x7ff
39
40 static GMainLoop *main_loop = NULL;
41
42 static int nfctool_poll_cb(guint8 cmd, guint32 idx, gpointer data);
43
44 static void nfctool_quit(gboolean force);
45
46 static gchar *nfctool_poll_mode_str(int mode)
47 {
48         if (mode == POLLING_MODE_TARGET)
49                 return "target";
50
51         if (mode == POLLING_MODE_BOTH)
52                 return "both initiator and target";
53
54         return "initiator";
55 }
56
57 static int nfctool_start_poll(void)
58 {
59         int err;
60
61         struct nfc_adapter *adapter;
62
63         adapter = adapter_get(opts.adapter_idx);
64
65         if (adapter == NULL) {
66                 print_error("Invalid adapter index: %d", opts.adapter_idx);
67
68                 return -ENODEV;
69         }
70
71         nl_add_event_handler(NFC_EVENT_TARGETS_FOUND, nfctool_poll_cb);
72         nl_add_event_handler(NFC_EVENT_TM_ACTIVATED, nfctool_poll_cb);
73
74         err = nl_start_poll(adapter, opts.poll_mode);
75
76         if (err == 0) {
77                 printf("Start polling on nfc%d as %s\n\n",
78                         adapter->idx, nfctool_poll_mode_str(opts.poll_mode));
79                 return 0;
80         }
81
82         if (err != -EBUSY)
83                 return err;
84
85         if (adapter->rf_mode == NFC_RF_NONE)
86                 printf("nfc%d already in polling mode\n\n", adapter->idx);
87         else
88                 printf("nfc%d already activated\n\n", adapter->idx);
89
90         return err;
91 }
92
93 static int nfctool_set_params(void)
94 {
95         struct nfc_adapter *adapter;
96         int err;
97
98         adapter = adapter_get(opts.adapter_idx);
99         if (!adapter)
100                 return -ENODEV;
101
102         err = nl_set_params(adapter, opts.lto, opts.rw, opts.miux);
103         if (err) {
104                 print_error("Error setting one of the parameters.");
105                 goto exit;
106         }
107
108         nl_get_params(adapter);
109
110         adapter_print_info(adapter);
111
112 exit:
113         return err;
114 }
115
116 static void nfctool_send_dep_link_up(guint32 target_idx, guint32 adapter_idx)
117 {
118         nl_send_dep_link_up(adapter_idx, target_idx);
119 }
120
121 static int nfctool_targets_found(guint32 adapter_idx)
122 {
123         int err;
124         struct nfc_adapter *adapter;
125
126         DBG("adapter_idx: %d", adapter_idx);
127
128         if (adapter_idx == INVALID_ADAPTER_IDX)
129                 return -ENODEV;
130
131         adapter = adapter_get(adapter_idx);
132
133         if (adapter == NULL)
134                 return -ENODEV;
135
136         err = nl_get_targets(adapter);
137         if (err) {
138                 print_error("Error getting targets\n");
139                 goto exit;
140         }
141
142         printf("Targets found for nfc%d\n", adapter_idx);
143         adpater_print_targets(adapter, "  ");
144
145         if (adapter->polling) {
146                 g_slist_foreach(adapter->devices,
147                                 (GFunc)nfctool_send_dep_link_up,
148                                 GINT_TO_POINTER(adapter_idx));
149
150                 adapter->polling = FALSE;
151         }
152
153 exit:
154         return err;
155 }
156
157 static int nfctool_poll_cb(guint8 cmd, guint32 idx, gpointer data)
158 {
159         int err = 0;
160
161         DBG("cmd: %d, idx: %d", cmd, idx);
162
163         switch (cmd) {
164         case NFC_EVENT_TARGETS_FOUND:
165                 err = nfctool_targets_found(idx);
166                 break;
167         case NFC_EVENT_TM_ACTIVATED:
168                 printf("Target mode activated\n");
169                 break;
170         }
171
172         nfctool_quit(FALSE);
173
174         return err;
175 }
176
177 static volatile sig_atomic_t __terminated = 0;
178
179 static void sig_term(int sig)
180 {
181         if (__terminated > 0)
182                 return;
183
184         __terminated = 1;
185
186         DBG("Terminating");
187
188         nfctool_quit(TRUE);
189 }
190
191 struct nfctool_options opts = {
192         .list = FALSE,
193         .poll = FALSE,
194         .poll_mode = POLLING_MODE_INITIATOR,
195         .device_name = NULL,
196         .adapter_idx = INVALID_ADAPTER_IDX,
197         .set_param = FALSE,
198         .lto = -1,
199         .rw = -1,
200         .miux = -1,
201         .need_netlink = FALSE,
202         .sniff = FALSE,
203         .snap_len = 0,
204         .dump_symm = FALSE,
205         .show_timestamp = SNIFFER_SHOW_TIMESTAMP_NONE,
206         .pcap_filename = NULL,
207 };
208
209 static gboolean opt_parse_poll_arg(const gchar *option_name, const gchar *value,
210                                    gpointer data, GError **error)
211 {
212         opts.poll = TRUE;
213
214         opts.poll_mode = POLLING_MODE_INITIATOR;
215
216         if (value != NULL) {
217                 if (*value == 't' || *value == 'T')
218                         opts.poll_mode = POLLING_MODE_TARGET;
219                 else if (*value == 'b' || *value == 'B')
220                         opts.poll_mode = POLLING_MODE_BOTH;
221         }
222
223         return TRUE;
224 }
225
226 static gboolean opt_parse_set_param_arg(const gchar *option_name,
227                                         const gchar *value,
228                                         gpointer data, GError **error)
229 {
230         gchar **params = NULL, **keyval = NULL;
231         gchar *end;
232         gint i, intval;
233         gboolean result;
234
235         params = g_strsplit(value, ",", -1);
236
237         i = 0;
238         while (params[i] != NULL) {
239                 keyval = g_strsplit(params[i], "=", 2);
240
241                 if (keyval[0] == NULL || keyval[1] == NULL) {
242                         result = FALSE;
243                         goto exit;
244                 }
245
246                 intval = strtol(keyval[1], &end, 10);
247                 if (keyval[1] == end) {
248                         result = FALSE;
249                         goto exit;
250                 }
251
252                 if (g_ascii_strcasecmp(keyval[0], "lto") == 0) {
253                         if (intval < 0 || intval > LLCP_MAX_LTO) {
254                                 print_error("Bad value: max lto value is %d",
255                                                                 LLCP_MAX_LTO);
256                                 result = FALSE;
257                                 goto exit;
258                         }
259
260                         opts.lto = intval;
261                 } else if (g_ascii_strcasecmp(keyval[0], "rw") == 0) {
262                         if (intval < 0 || intval > LLCP_MAX_RW) {
263                                 print_error("Bad value: max rw value is %d",
264                                                                 LLCP_MAX_RW);
265                                 result = FALSE;
266                                 goto exit;
267                         }
268
269                         opts.rw = intval;
270                 } else if (g_ascii_strcasecmp(keyval[0], "miux") == 0) {
271                         if (intval < 0 || intval > LLCP_MAX_MIUX) {
272                                 print_error("Bad value: max miux value is %d",
273                                                                 LLCP_MAX_MIUX);
274                                 result = FALSE;
275                                 goto exit;
276                         }
277
278                         opts.miux = intval;
279                 } else {
280                         result = FALSE;
281                         goto exit;
282                 }
283
284                 opts.set_param = TRUE;
285
286                 g_strfreev(keyval);
287                 keyval = NULL;
288
289                 i++;
290         }
291
292         result = TRUE;
293
294 exit:
295         if (params)
296                 g_strfreev(params);
297
298         if (keyval)
299                 g_strfreev(keyval);
300
301         return result;
302 }
303
304 static gboolean opt_parse_show_timestamp_arg(const gchar *option_name,
305                                              const gchar *value,
306                                              gpointer data, GError **error)
307 {
308         if (value != NULL && (*value == 'a' || *value == 'A'))
309                 opts.show_timestamp = SNIFFER_SHOW_TIMESTAMP_ABS;
310         else
311                 opts.show_timestamp = SNIFFER_SHOW_TIMESTAMP_DELTA;
312
313         return TRUE;
314 }
315
316 static GOptionEntry option_entries[] = {
317         { "list", 'l', 0, G_OPTION_ARG_NONE, &opts.list,
318           "list attached NFC devices", NULL },
319         { "device", 'd', 0, G_OPTION_ARG_STRING, &opts.device_name,
320           "specify a nfc device", "nfcX" },
321         { "poll", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
322           opt_parse_poll_arg, "start polling as initiator, target, or both; "
323           "default mode is initiator", "[Initiator|Target|Both]" },
324         { "set-param", 's', 0, G_OPTION_ARG_CALLBACK, opt_parse_set_param_arg,
325           "set lto, rw, and/or miux parameters", "lto=150,rw=1,miux=100" },
326         { "sniff", 'n', 0, G_OPTION_ARG_NONE, &opts.sniff,
327           "start LLCP sniffer on the specified device", NULL },
328         { "snapshot-len", 'a', 0, G_OPTION_ARG_INT, &opts.snap_len,
329           "packet snapshot length (in bytes); only relevant with -n", "1024" },
330         { "dump-symm", 'y', 0, G_OPTION_ARG_NONE, &opts.dump_symm,
331           "dump SYMM packets to stdout (flooding); only relevant with -n",
332           NULL },
333         { "show-timestamp", 't', G_OPTION_FLAG_OPTIONAL_ARG,
334           G_OPTION_ARG_CALLBACK, opt_parse_show_timestamp_arg,
335           "show packet timestamp as the delta from first frame (default) "
336           "or absolute value; only relevant with -n", "[delta|abs]" },
337         { "pcap-file", 'f', 0, G_OPTION_ARG_STRING, &opts.pcap_filename,
338           "specify a filename to save traffic in pcap format; "
339           "only relevant with -n", "filename" },
340         { NULL }
341 };
342
343 static int nfctool_options_parse(int argc, char **argv)
344 {
345         GOptionContext *context;
346         GError *error = NULL;
347         gchar *start, *end;
348         int err = -EINVAL;
349
350         context = g_option_context_new("- A small NFC tool box");
351
352         g_option_context_add_main_entries(context, option_entries, NULL);
353
354         if (!g_option_context_parse(context, &argc, &argv, &error)) {
355                 print_error("%s: %s", argv[0], error->message);
356
357                 g_error_free(error);
358
359                 goto exit;
360         }
361
362         if (opts.device_name != NULL) {
363                 if (strncmp("nfc", opts.device_name, 3) != 0) {
364                         print_error("Invalid device name: %s",
365                                                         opts.device_name);
366
367                         goto exit;
368                 }
369
370                 start = opts.device_name + 3;
371
372                 opts.adapter_idx = strtol(start, &end, 10);
373                 if (start == end) {
374                         print_error("Invalid NFC adapter %s", opts.device_name);
375
376                         goto exit;
377                 }
378         }
379
380         opts.need_netlink = opts.list || opts.poll || opts.set_param;
381
382         if (!opts.need_netlink && !opts.sniff) {
383                 printf("%s", g_option_context_get_help(context, TRUE, NULL));
384
385                 goto exit;
386         }
387
388         if ((opts.poll || opts.set_param || opts.sniff) &&
389             opts.adapter_idx == INVALID_ADAPTER_IDX) {
390                 print_error("Please specify a device with -d nfcX option");
391
392                 goto exit;
393         }
394
395         err = 0;
396
397 exit:
398         g_option_context_free(context);
399
400         return err;
401 }
402
403 static void nfctool_main_loop_start(void)
404 {
405         struct sigaction sa;
406
407         memset(&sa, 0, sizeof(sa));
408         sa.sa_handler = sig_term;
409         sigaction(SIGINT, &sa, NULL);
410         sigaction(SIGTERM, &sa, NULL);
411
412         main_loop = g_main_loop_new(NULL, FALSE);
413
414         g_main_loop_run(main_loop);
415 }
416
417 static void nfctool_options_cleanup(void)
418 {
419         if (opts.device_name != NULL)
420                 g_free(opts.device_name);
421
422         if (opts.pcap_filename != NULL)
423                 g_free(opts.pcap_filename);
424 }
425
426 static void nfctool_main_loop_clean(void)
427 {
428         if (main_loop != NULL)
429                 g_main_loop_unref(main_loop);
430 }
431
432 static void nfctool_quit(gboolean force)
433 {
434         if (force || !opts.sniff)
435                 g_main_loop_quit(main_loop);
436 }
437
438 int main(int argc, char **argv)
439 {
440         int err;
441
442         err = nfctool_options_parse(argc, argv);
443         if (err)
444                 goto exit_err;
445
446         adapter_init();
447
448         if (opts.need_netlink) {
449                 err = nl_init();
450                 if (err)
451                         goto exit_err;
452
453                 err = adapter_all_get_devices();
454                 if (err)
455                         goto exit_err;
456
457                 if (opts.list && !opts.set_param)
458                         adapter_idx_print_info(opts.adapter_idx);
459
460                 if (opts.set_param) {
461                         err = nfctool_set_params();
462                         if (err)
463                                 goto exit_err;
464                 }
465
466                 if (opts.poll) {
467                         err = nfctool_start_poll();
468
469                         if (err == -EBUSY && opts.sniff)
470                                 err = 0;
471
472                         if (err)
473                                 goto exit_err;
474                 }
475         }
476
477         if (opts.sniff) {
478                 err = sniffer_init();
479                 if (err)
480                         goto exit_err;
481         }
482
483         if (opts.poll || opts.sniff)
484                 nfctool_main_loop_start();
485
486         err = 0;
487
488 exit_err:
489         nfctool_main_loop_clean();
490
491         adapter_cleanup();
492
493         nl_cleanup();
494
495         sniffer_cleanup();
496
497         nfctool_options_cleanup();
498
499         return err;
500 }