blob: 2bdf3f57dffb0cf3af87f1abce221b8b9498b4e0 [file] [log] [blame]
/*
*
* Near Field Communication nfctool
*
* Copyright (C) 2012 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <glib.h>
#include <netlink/genl/genl.h>
#include <near/nfc_copy.h>
#include <near/types.h>
#include <near/ndef.h>
#include "../../src/near.h"
#include "nfctool.h"
#include "adapter.h"
#include "netlink.h"
#include "sniffer.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/socket.h>
/* HACK HACK */
#ifndef AF_NFC
#define AF_NFC 39
#endif
#define LLCP_MAX_LTO 0xff
#define LLCP_MAX_RW 0x0f
#define LLCP_MAX_MIUX 0x7ff
#define OUI_LEN 4
#define SUBCMD_LEN 4
#define SNEP_VERSION 0x10
/* Request codes */
#define SNEP_REQ_CONTINUE 0x00
#define SNEP_REQ_GET 0x01
#define SNEP_REQ_PUT 0x02
#define SNEP_REQ_REJECT 0x7f
/* Response codes */
#define SNEP_RESP_CONTINUE 0x80
#define SNEP_RESP_SUCCESS 0x81
#define SNEP_RESP_NOT_FOUND 0xc0
#define SNEP_RESP_EXCESS 0xc1
#define SNEP_RESP_BAD_REQ 0xc2
#define SNEP_RESP_NOT_IMPL 0xe0
#define SNEP_RESP_VERSION 0xe1
#define SNEP_RESP_REJECT 0xff
struct p2p_snep_req_frame {
uint8_t version;
uint8_t request;
uint32_t length;
uint8_t ndef[];
} __attribute__((packed));
static GMainLoop *main_loop = NULL;
static int nfctool_poll_cb(guint8 cmd, guint32 idx, gpointer data);
static int nfctool_snl_cb(guint8 cmd, guint32 idx, gpointer data);
static void nfctool_quit(bool force);
static gchar *nfctool_poll_mode_str(int mode)
{
if (mode == POLLING_MODE_TARGET)
return "target";
if (mode == POLLING_MODE_BOTH)
return "both initiator and target";
return "initiator";
}
static int nfctool_start_poll(void)
{
int err;
struct nfc_adapter *adapter;
adapter = adapter_get(opts.adapter_idx);
if (!adapter) {
print_error("Invalid adapter index: %d", opts.adapter_idx);
return -ENODEV;
}
nl_add_event_handler(NFC_EVENT_TARGETS_FOUND, nfctool_poll_cb);
nl_add_event_handler(NFC_EVENT_TM_ACTIVATED, nfctool_poll_cb);
err = nl_start_poll(adapter, opts.poll_mode);
if (err == 0) {
printf("Start polling on nfc%d as %s\n\n",
adapter->idx, nfctool_poll_mode_str(opts.poll_mode));
return 0;
}
if (err != -EBUSY)
return err;
if (adapter->rf_mode == NFC_RF_NONE)
printf("nfc%d already in polling mode\n\n", adapter->idx);
else
printf("nfc%d already activated\n\n", adapter->idx);
/* Don't fail if there is a pending SNL request */
if (opts.snl)
return 0;
return err;
}
static int nfctool_set_params(void)
{
struct nfc_adapter *adapter;
int err;
adapter = adapter_get(opts.adapter_idx);
if (!adapter)
return -ENODEV;
err = nl_set_params(adapter, opts.lto, opts.rw, opts.miux);
if (err) {
print_error("Error setting one of the parameters.");
goto exit;
}
nl_get_params(adapter);
adapter_print_info(adapter);
exit:
return err;
}
static int nfctool_snl_send_request(struct nfc_adapter *adapter)
{
int err;
nl_add_event_handler(NFC_EVENT_LLC_SDRES, nfctool_snl_cb);
err = nl_send_sdreq(adapter, opts.snl_list);
if (err)
print_error("Can't send SNL request: %s", strerror(-err));
return err;
}
static int nfctool_set_powered(bool powered)
{
struct nfc_adapter *adapter;
int err;
adapter = adapter_get(opts.adapter_idx);
if (!adapter)
return -ENODEV;
err = nl_set_powered(adapter, powered);
if (err == 0)
adapter->powered = powered;
return err;
}
static int nfctool_fw_download_cb(guint8 cmd, guint32 adapter_idx,
gpointer data)
{
int err;
gchar *fw_filename;
struct nlattr **nl_attr = data;
if (nl_attr[NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS] != NULL)
err = nla_get_u32(nl_attr[NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS]);
else
err = -ENOTSUP;
if (nl_attr[NFC_ATTR_FIRMWARE_NAME] != NULL)
fw_filename = nla_get_string(nl_attr[NFC_ATTR_FIRMWARE_NAME]);
else
fw_filename = "UNKNOWN";
printf("Firmware download operation for %s terminated with status %s\n",
fw_filename, err ? strerror(-err) : "OK");
nfctool_quit(false);
return 0;
}
static int nfctool_fw_download(gchar *fw_filename)
{
struct nfc_adapter *adapter;
int err;
adapter = adapter_get(opts.adapter_idx);
if (!adapter)
return -ENODEV;
nl_add_event_handler(NFC_CMD_FW_DOWNLOAD, nfctool_fw_download_cb);
err = nl_fw_download(adapter, fw_filename);
if (err)
print_error("Firmware download failed: %s", strerror(-err));
return err;
}
static int nfctool_dep_link_up_cb(guint8 cmd, guint32 idx, gpointer data)
{
struct nfc_adapter *adapter;
printf("Link is UP for adapter nfc%d\n\n", idx);
if (idx != opts.adapter_idx)
return -ENODEV;
adapter = adapter_get(idx);
if (!adapter)
return -ENODEV;
nfctool_snl_send_request(adapter);
return 0;
}
static int nfctool_dep_link_down_cb(guint8 cmd, guint32 idx, gpointer data)
{
if (idx != opts.adapter_idx)
return -ENODEV;
printf("Link is DOWN for adapter nfc%d\n\n", idx);
opts.snl = false;
nfctool_quit(false);
return 0;
}
static int nfctool_snl(void)
{
struct nfc_adapter *adapter;
adapter = adapter_get(opts.adapter_idx);
if (!adapter)
return -ENODEV;
if (adapter->polling) {
/* Delay netlink message until the link is established */
nl_add_event_handler(NFC_CMD_DEP_LINK_UP,
nfctool_dep_link_up_cb);
nl_add_event_handler(NFC_CMD_DEP_LINK_DOWN,
nfctool_dep_link_down_cb);
return 0;
}
if (adapter->rf_mode == NFC_RF_NONE) {
print_error("Can't send SNL request: No active link");
opts.snl = false;
return -ENOLINK;
}
return nfctool_snl_send_request(adapter);
}
static void nfctool_send_dep_link_up(guint32 target_idx, guint32 adapter_idx)
{
nl_send_dep_link_up(adapter_idx, target_idx);
}
static int nfctool_snep_send(guint32 target_idx, guint32 adapter_idx) {
int fd, len;
struct near_ndef_message *ndef;
struct sockaddr_nfc_llcp addr;
struct p2p_snep_req_frame *frame;
size_t frame_length;
const char *uri_msg = "http://nfc-forum.org/";
const char *txt_msg = "Hello world!";
DBG("target_idx=%d, adapter_idx=%d\n", target_idx, adapter_idx);
fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP);
if (fd < 0)
return -1;
memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp));
addr.sa_family = AF_NFC;
addr.dev_idx = adapter_idx;
addr.target_idx = target_idx;
addr.nfc_protocol = NFC_PROTO_NFC_DEP;
addr.service_name_len = strlen("urn:nfc:sn:snep");
strcpy(addr.service_name, "urn:nfc:sn:snep");
if (connect(fd, (struct sockaddr *)&addr,
sizeof(struct sockaddr_nfc_llcp)) < 0) {
printf("Connect error %s\n", strerror(errno));
return -1;
}
if (opts.push_snep_uri)
ndef = near_ndef_prepare_uri_record(0, strlen(uri_msg), uri_msg);
else
ndef = near_ndef_prepare_text_record("UTF-8", "en", txt_msg);
if (!ndef) {
close(fd);
printf("Could not build NDEF");
return -1;
}
frame_length = sizeof(struct p2p_snep_req_frame) + ndef->length;
frame = g_try_malloc0(frame_length);
if (!frame) {
close(fd);
printf("Could not allocate SNEP frame");
return -1;
}
frame->version = SNEP_VERSION;
frame->request = SNEP_REQ_PUT;
frame->length = GUINT_TO_BE(ndef->length);
memcpy(frame->ndef, ndef->data, ndef->length);
len = send(fd, (uint8_t *)frame, frame_length, 0);
if (len < 0) {
printf("Could not send NDEF %s\n", strerror(errno));
g_free(frame);
close(fd);
return -1;
}
printf("Sent %d bytes\n", len);
g_free(frame);
close(fd);
return 0;
}
static int nfctool_targets_found(guint32 adapter_idx)
{
int err;
struct nfc_adapter *adapter;
DBG("adapter_idx: %d", adapter_idx);
if (adapter_idx == INVALID_ADAPTER_IDX)
return -ENODEV;
adapter = adapter_get(adapter_idx);
if (!adapter)
return -ENODEV;
err = nl_get_targets(adapter);
if (err) {
print_error("Error getting targets\n");
goto exit;
}
printf("Targets found for nfc%d\n", adapter_idx);
adpater_print_targets(adapter, " ");
printf("\n");
if (adapter->polling) {
if (opts.push_snep_msg)
g_slist_foreach(adapter->devices,
(GFunc)nfctool_snep_send,
GINT_TO_POINTER(adapter_idx));
else
g_slist_foreach(adapter->devices,
(GFunc)nfctool_send_dep_link_up,
GINT_TO_POINTER(adapter_idx));
adapter->polling = FALSE;
}
exit:
return err;
}
static int nfctool_initiator_found(guint32 adapter_idx)
{
struct nfc_adapter *adapter;
DBG("adapter_idx: %d", adapter_idx);
if (adapter_idx == INVALID_ADAPTER_IDX)
return -ENODEV;
adapter = adapter_get(adapter_idx);
if (!adapter)
return -ENODEV;
if (adapter->polling && opts.push_snep_msg)
nfctool_snep_send(0, adapter_idx);
adapter->polling = FALSE;
return 0;
}
static int nfctool_poll_cb(guint8 cmd, guint32 idx, gpointer data)
{
int err = 0;
DBG("cmd: %d, idx: %d", cmd, idx);
switch (cmd) {
case NFC_EVENT_TARGETS_FOUND:
err = nfctool_targets_found(idx);
break;
case NFC_EVENT_TM_ACTIVATED:
printf("Target mode activated\n");
err = nfctool_initiator_found(idx);
break;
}
nfctool_quit(false);
return err;
}
static void nfctool_print_and_remove_snl(struct nfc_snl *sdres,
guint32 adapter_idx)
{
GSList *elem;
printf(" uri: %s - sap: %d\n", sdres->uri, sdres->sap);
if (adapter_idx == opts.adapter_idx) {
elem = g_slist_find_custom(opts.snl_list, sdres->uri,
(GCompareFunc)g_strcmp0);
if (elem) {
g_free(elem->data);
opts.snl_list = g_slist_delete_link(opts.snl_list,
elem);
}
}
}
static int nfctool_snl_cb(guint8 cmd, guint32 idx, gpointer data)
{
GSList *sdres_list = (GSList *)data;
printf("nfc%d: Service Name lookup:\n", idx);
g_slist_foreach(sdres_list, (GFunc)nfctool_print_and_remove_snl,
GINT_TO_POINTER(idx));
printf("\n");
if (!opts.snl_list) {
opts.snl = false;
nfctool_quit(false);
}
return 0;
}
static int nfctool_send_vendor_cmd_cb(guint8 cmd, guint32 adapter_idx,
gpointer data)
{
guint32 vendor;
guint32 sub_cmd;
gchar *rsp;
int rsp_len, i;
struct nlattr **nl_attr = data;
if (nl_attr[NFC_ATTR_VENDOR_ID] != NULL)
vendor = nla_get_u32(nl_attr[NFC_ATTR_VENDOR_ID]);
else
vendor = 0;
if (nl_attr[NFC_ATTR_VENDOR_SUBCMD] != NULL)
sub_cmd = nla_get_u32(nl_attr[NFC_ATTR_VENDOR_SUBCMD]);
else
sub_cmd = 0;
printf("Vendor 0x%.8X response 0x%.8X with", vendor, sub_cmd);
if (nl_attr[NFC_ATTR_VENDOR_DATA] != NULL) {
printf(" data:");
rsp = nla_data(nl_attr[NFC_ATTR_VENDOR_DATA]);
rsp_len = nla_len(nl_attr[NFC_ATTR_VENDOR_DATA]);
for (i = 0; i < rsp_len; i++)
printf(" 0x%02X", rsp[i]);
puts("");
} else {
puts("out data");
rsp = NULL;
}
nfctool_quit(false);
return 0;
}
static int nfctool_send_vendor_cmd(gchar *data)
{
int err;
unsigned int index = 0;
struct nfc_adapter *adapter;
guint32 oui = 0;
guint32 sub_cmd = 0;
gchar buffer[9];
gchar *ptr = data;
guint32 data_buffer_size = 0;
guint8 *data_buffer;
adapter = adapter_get(opts.adapter_idx);
if (!adapter) {
print_error("Invalid adapter index: %d", opts.adapter_idx);
return -ENODEV;
}
/* Each byte use 2 characters */
data_buffer_size = strlen(ptr) / 2;
if (data_buffer_size < (OUI_LEN + SUBCMD_LEN))
return -ENODATA;
if (data_buffer_size > (OUI_LEN + SUBCMD_LEN)) {
data_buffer = g_malloc(data_buffer_size - 8);
if (!data_buffer)
return -ENOMEM;
} else {
data_buffer = NULL;
}
/* Parse OUI */
memcpy(buffer, ptr + index * 2, OUI_LEN * 2);
buffer[OUI_LEN * 2] = '\0';
oui = strtol(buffer, NULL, 16);
index += OUI_LEN;
/* Parse sub cmd code */
memcpy(buffer, ptr + index * 2, SUBCMD_LEN * 2);
buffer[SUBCMD_LEN * 2] = '\0';
sub_cmd = strtol(buffer, NULL, 16);
index += SUBCMD_LEN;
/* Parse data */
buffer[2] = '\0';
for (; index < data_buffer_size; index++) {
memcpy(buffer, ptr + index * 2, 2);
data_buffer[index - OUI_LEN - SUBCMD_LEN] = (gchar) strtol(buffer, NULL, 16);
}
nl_add_event_handler(NFC_EVENT_VENDOR_RSP, nfctool_send_vendor_cmd_cb);
err = nl_send_vendor_cmd(adapter, oui, sub_cmd,
data_buffer_size - OUI_LEN - SUBCMD_LEN,
data_buffer);
g_free(data_buffer);
return err;
}
struct nfc_snl *nfctool_snl_alloc(gsize uri_size)
{
struct nfc_snl *snl;
snl = g_malloc(sizeof(struct nfc_snl));
snl->uri = g_malloc0(uri_size);
snl->uri_size = uri_size;
snl->sap = 0;
return snl;
}
void nfctool_sdres_free(struct nfc_snl *snl)
{
if (snl->uri_size)
g_free(snl->uri);
g_free(snl);
}
static volatile sig_atomic_t __terminated = 0;
static void sig_term(int sig)
{
if (__terminated > 0)
return;
__terminated = 1;
DBG("Terminating");
nfctool_quit(true);
}
struct nfctool_options opts = {
.show_version = FALSE,
.list = FALSE,
.poll = FALSE,
.poll_mode = POLLING_MODE_INITIATOR,
.device_name = NULL,
.adapter_idx = INVALID_ADAPTER_IDX,
.enable_dev = FALSE,
.disable_dev = FALSE,
.set_param = FALSE,
.lto = -1,
.rw = -1,
.miux = -1,
.need_netlink = FALSE,
.snl = FALSE,
.snl_list = NULL,
.sniff = FALSE,
.snap_len = 0,
.dump_symm = FALSE,
.show_timestamp = SNIFFER_SHOW_TIMESTAMP_NONE,
.snep_sap = 0x04,
.pcap_filename = NULL,
.vendor_cmd = NULL,
.push_snep_msg = FALSE,
.push_snep_uri = TRUE,
};
static bool opt_parse_poll_arg(const gchar *option_name, const gchar *value,
gpointer data, GError **error)
{
opts.poll = true;
opts.poll_mode = POLLING_MODE_INITIATOR;
if (value) {
if (*value == 't' || *value == 'T')
opts.poll_mode = POLLING_MODE_TARGET;
else if (*value == 'b' || *value == 'B')
opts.poll_mode = POLLING_MODE_BOTH;
}
return true;
}
static bool opt_parse_pushsnepmsg_arg(const gchar *option_name, const gchar *value,
gpointer data, GError **error)
{
opts.push_snep_msg = true;
opts.poll_mode = POLLING_MODE_BOTH;
if (value) {
if (*value == 't' || *value == 'T')
opts.push_snep_uri = FALSE;
}
return true;
}
static bool opt_parse_set_param_arg(const gchar *option_name,
const gchar *value,
gpointer data, GError **error)
{
gchar **params = NULL, **keyval = NULL;
gchar *end;
gint i, intval;
bool result;
params = g_strsplit(value, ",", -1);
i = 0;
while (params[i]) {
keyval = g_strsplit(params[i], "=", 2);
if (!keyval[0] || !keyval[1]) {
result = false;
goto exit;
}
intval = strtol(keyval[1], &end, 10);
if (keyval[1] == end) {
result = false;
goto exit;
}
if (g_ascii_strcasecmp(keyval[0], "lto") == 0) {
if (intval < 0 || intval > LLCP_MAX_LTO) {
print_error("Bad value: max lto value is %d",
LLCP_MAX_LTO);
result = false;
goto exit;
}
opts.lto = intval;
} else if (g_ascii_strcasecmp(keyval[0], "rw") == 0) {
if (intval < 0 || intval > LLCP_MAX_RW) {
print_error("Bad value: max rw value is %d",
LLCP_MAX_RW);
result = false;
goto exit;
}
opts.rw = intval;
} else if (g_ascii_strcasecmp(keyval[0], "miux") == 0) {
if (intval < 0 || intval > LLCP_MAX_MIUX) {
print_error("Bad value: max miux value is %d",
LLCP_MAX_MIUX);
result = false;
goto exit;
}
opts.miux = intval;
} else {
result = false;
goto exit;
}
opts.set_param = true;
g_strfreev(keyval);
keyval = NULL;
i++;
}
result = true;
exit:
if (params)
g_strfreev(params);
if (keyval)
g_strfreev(keyval);
return result;
}
static bool opt_parse_show_timestamp_arg(const gchar *option_name,
const gchar *value,
gpointer data, GError **error)
{
if (value && (*value == 'a' || *value == 'A'))
opts.show_timestamp = SNIFFER_SHOW_TIMESTAMP_ABS;
else
opts.show_timestamp = SNIFFER_SHOW_TIMESTAMP_DELTA;
return true;
}
static bool opt_parse_snl_arg(const gchar *option_name, const gchar *value,
gpointer data, GError **error)
{
gchar *uri;
opts.snl = true;
uri = g_strdup(value);
opts.snl_list = g_slist_prepend(opts.snl_list, uri);
return true;
}
static GOptionEntry option_entries[] = {
{ "version", 'v', 0, G_OPTION_ARG_NONE, &opts.show_version,
"show version information and exit" },
{ "list", 'l', 0, G_OPTION_ARG_NONE, &opts.list,
"list attached NFC devices", NULL },
{ "device", 'd', 0, G_OPTION_ARG_STRING, &opts.device_name,
"specify a nfc device", "nfcX" },
{ "poll", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
opt_parse_poll_arg, "start polling as initiator, target, or both; "
"default mode is initiator", "[Initiator|Target|Both]" },
{ "enable", '1', 0, G_OPTION_ARG_NONE, &opts.enable_dev,
"enable device", NULL },
{ "disable", '0', 0, G_OPTION_ARG_NONE, &opts.disable_dev,
"disable device", NULL },
{ "fw-download", 'w', 0, G_OPTION_ARG_STRING, &opts.fw_filename,
"Put the device in firmware download mode", "fw_filename" },
{ "set-param", 's', 0, G_OPTION_ARG_CALLBACK, opt_parse_set_param_arg,
"set lto, rw, and/or miux parameters", "lto=150,rw=1,miux=100" },
{ "snl", 'k', 0, G_OPTION_ARG_CALLBACK, &opt_parse_snl_arg,
"Send a Service Name Lookup request", "urn:nfc:sn:snep"},
{ "sniff", 'n', 0, G_OPTION_ARG_NONE, &opts.sniff,
"start LLCP sniffer on the specified device", NULL },
{ "snapshot-len", 'a', 0, G_OPTION_ARG_INT, &opts.snap_len,
"packet snapshot length (in bytes); only relevant with -n", "1024" },
{ "dump-symm", 'y', 0, G_OPTION_ARG_NONE, &opts.dump_symm,
"dump SYMM packets to stdout (flooding); only relevant with -n",
NULL },
{ "snep-sap", 'e', 0, G_OPTION_ARG_INT, &opts.snep_sap,
"Specify the sap number to be used for snep decoding; "
"only relevant with -n", "0x04" },
{ "show-timestamp", 't', G_OPTION_FLAG_OPTIONAL_ARG,
G_OPTION_ARG_CALLBACK, opt_parse_show_timestamp_arg,
"show packet timestamp as the delta from first frame (default) "
"or absolute value; only relevant with -n", "[delta|abs]" },
{ "pcap-file", 'f', 0, G_OPTION_ARG_STRING, &opts.pcap_filename,
"specify a filename to save traffic in pcap format; "
"only relevant with -n", "filename" },
{ "push-snep-message", 'm', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
opt_parse_pushsnepmsg_arg, "push a URI or Text message to a p2p device, "
"default is a URI message", "[URI|Text]" },
{ "vendor_cmd", 'V', 0, G_OPTION_ARG_STRING, &opts.vendor_cmd,
"Send vendor command to driver through NCI or HCI layer",
"\"xx ... xx\"=[OUI][SUBCMD][DATA]"},
{ NULL }
};
static int nfctool_options_parse(int argc, char **argv)
{
GOptionContext *context;
GError *error = NULL;
gchar *start, *end;
int err = -EINVAL;
context = g_option_context_new("- A small NFC tool box");
g_option_context_add_main_entries(context, option_entries, NULL);
if (!g_option_context_parse(context, &argc, &argv, &error)) {
print_error("%s: %s", argv[0], error->message);
g_error_free(error);
goto exit;
}
if (opts.show_version) {
printf("%s\n", VERSION);
goto done;
}
if (opts.device_name) {
if (strncmp("nfc", opts.device_name, 3) != 0) {
print_error("Invalid device name: %s",
opts.device_name);
goto exit;
}
start = opts.device_name + 3;
opts.adapter_idx = strtol(start, &end, 10);
if (start == end) {
print_error("Invalid NFC adapter %s", opts.device_name);
goto exit;
}
}
if (opts.enable_dev || opts.disable_dev)
opts.list = true;
if (opts.poll || opts.push_snep_msg)
opts.enable_dev = true;
opts.need_netlink = opts.list || opts.poll || opts.set_param ||
opts.snl || opts.fw_filename || opts.vendor_cmd ||
opts.push_snep_msg;
if (!opts.need_netlink && !opts.sniff) {
printf("%s", g_option_context_get_help(context, TRUE, NULL));
goto exit;
}
if ((opts.poll || opts.set_param || opts.sniff || opts.snl ||
opts.enable_dev || opts.disable_dev || opts.fw_filename ||
opts.vendor_cmd || opts.push_snep_msg) &&
opts.adapter_idx == INVALID_ADAPTER_IDX) {
print_error("Please specify a device with -d nfcX option");
goto exit;
}
done:
err = 0;
exit:
g_option_context_free(context);
return err;
}
static void nfctool_main_loop_start(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_term;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
main_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(main_loop);
}
static void nfctool_options_cleanup(void)
{
if (opts.device_name)
g_free(opts.device_name);
if (opts.pcap_filename)
g_free(opts.pcap_filename);
if (opts.fw_filename != NULL)
g_free(opts.fw_filename);
g_slist_free_full(opts.snl_list, g_free);
}
static void nfctool_main_loop_clean(void)
{
if (main_loop)
g_main_loop_unref(main_loop);
}
static void nfctool_quit(bool force)
{
if (force || (!opts.sniff && !opts.snl))
g_main_loop_quit(main_loop);
if (force)
nfctool_set_powered(FALSE);
}
int main(int argc, char **argv)
{
int err;
err = nfctool_options_parse(argc, argv);
if (err)
goto exit_err;
if (opts.show_version)
goto done;
adapter_init();
if (opts.need_netlink) {
err = nl_init();
if (err)
goto exit_err;
err = adapter_all_get_devices();
if (err)
goto exit_err;
}
if (opts.fw_filename) {
err = nfctool_fw_download(opts.fw_filename);
if (err)
goto exit_err;
goto start_loop;
}
if (opts.sniff) {
err = sniffer_init();
if (err)
goto exit_err;
}
if (opts.enable_dev || opts.disable_dev) {
err = nfctool_set_powered(opts.enable_dev);
if (err && err != -EALREADY)
goto exit_err;
err = 0;
}
if (opts.list && !opts.set_param)
adapter_idx_print_info(opts.adapter_idx);
if (opts.set_param) {
err = nfctool_set_params();
if (err)
goto exit_err;
}
if (opts.poll || opts.push_snep_msg) {
err = nfctool_start_poll();
if (err == -EBUSY && opts.sniff)
err = 0;
if (err)
goto exit_err;
}
if (opts.snl)
nfctool_snl();
if (opts.vendor_cmd) {
err = nfctool_send_vendor_cmd(opts.vendor_cmd);
if (err)
goto exit_err;
goto start_loop;
}
start_loop:
if (opts.poll || opts.sniff || opts.snl || opts.fw_filename || opts.vendor_cmd || opts.push_snep_msg)
nfctool_main_loop_start();
done:
err = 0;
exit_err:
nfctool_main_loop_clean();
adapter_cleanup();
nl_cleanup();
sniffer_cleanup();
nfctool_options_cleanup();
if (err)
print_error("%s", strerror(-err));
return err;
}