From e434207af70b91ae4feee864446eeebd1ede14bd Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 14 Dec 2012 15:33:57 +0100 Subject: [PATCH] neard: Add nfctool native application nfctool is a native application used as a tool box for various nfc related operations. First goal is to be able to do such operations on embedded platforms that don't have a python interpretor (since all neard test tools are pyhton scripts) This initial release can list nfc adapters, targets, etc. --- .gitignore | 1 + Makefile.am | 5 +- tools/nfctool/main.c | 225 +++++++++++++++++++++++++ tools/nfctool/netlink.c | 428 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/nfctool/netlink.h | 34 ++++ tools/nfctool/nfctool.h | 61 +++++++ 6 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 tools/nfctool/main.c create mode 100644 tools/nfctool/netlink.c create mode 100644 tools/nfctool/netlink.h create mode 100644 tools/nfctool/nfctool.h diff --git a/.gitignore b/.gitignore index e678568..af74ca3 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ include/version.h src/builtin.h src/neard tools/snep-send +tools/nfctool/nfctool diff --git a/Makefile.am b/Makefile.am index b563dfc..79110a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,12 +80,15 @@ test_SCRIPTS = $(test_scripts) endif if TOOLS -noinst_PROGRAMS = tools/snep-send +noinst_PROGRAMS = tools/snep-send tools/nfctool/nfctool tools_snep_send_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \ src/bluetooth.c src/ndef.c tools/snep-send.c src/agent.c tools_snep_send_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ +tools_nfctool_nfctool_SOURCES = tools/nfctool/main.c tools/nfctool/netlink.c +tools_nfctool_nfctool_LDADD = @GLIB_LIBS@ @NETLINK_LIBS@ + endif include Makefile.plugins diff --git a/tools/nfctool/main.c b/tools/nfctool/main.c new file mode 100644 index 0000000..fc8e0a3 --- /dev/null +++ b/tools/nfctool/main.c @@ -0,0 +1,225 @@ +/* + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "nfctool.h" +#include "netlink.h" + +GSList *adapters = NULL; + +static void nfctool_print_target(guint32 idx, gchar *type) +{ + printf("%s%d ", type, idx); +} + +static void nfctool_print_targets(struct nfc_adapter *adapter, gchar *prefix) +{ + printf("%sTags: [ ", prefix); + + g_slist_foreach(adapter->tags, (GFunc)nfctool_print_target, "tag"); + + printf("]\n"); + + printf("%sDevices: [ ", prefix); + + g_slist_foreach(adapter->devices, + (GFunc)nfctool_print_target, "device"); + + printf("]\n"); +} + +static void nfctool_print_adapter_info(struct nfc_adapter *adapter) +{ + printf("nfc%d:\n", adapter->idx); + + nfctool_print_targets(adapter, " "); + + printf(" Protocols: [ "); + + if (adapter->protocols & NFC_PROTO_FELICA_MASK) + printf("Felica "); + + if (adapter->protocols & NFC_PROTO_MIFARE_MASK) + printf("MIFARE "); + + if (adapter->protocols & NFC_PROTO_JEWEL_MASK) + printf("Jewel "); + + if (adapter->protocols & NFC_PROTO_ISO14443_MASK) + printf("ISO-DEP "); + + if (adapter->protocols & NFC_PROTO_NFC_DEP_MASK) + printf("NFC-DEP "); + + printf("]\n"); + + printf(" Powered: %s\n", + adapter->powered ? "Yes" : "No"); + + printf("\n"); +} + +static void nfctool_list_adapter(struct nfc_adapter *adapter, guint32 idx) +{ + if (idx == INVALID_ADAPTER_IDX || idx == adapter->idx) + nfctool_print_adapter_info(adapter); +} + +static void nfctool_list_adapters(void) +{ + g_slist_foreach(adapters, (GFunc)nfctool_list_adapter, + GINT_TO_POINTER(opts.adapter_idx)); +} + +static void nfctool_adapter_free(struct nfc_adapter *adapter) +{ + g_slist_free(adapter->tags); + g_slist_free(adapter->devices); + + g_free(adapter); +} + +static void nfctool_get_device(struct nfc_adapter *adapter) +{ + nl_get_targets(adapter); +} + +static int nfctool_get_devices(void) +{ + int err; + + err = nl_get_devices(); + if (err) + return err; + + g_slist_foreach(adapters, (GFunc)nfctool_get_device, NULL); + + return 0; +} + +struct nfctool_options opts = { + .list = FALSE, + .device_name = NULL, + .adapter_idx = INVALID_ADAPTER_IDX, +}; + +static GOptionEntry option_entries[] = { + { "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" }, + { 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.device_name != NULL) { + 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.list) { + printf("%s", g_option_context_get_help(context, TRUE, NULL)); + + goto exit; + } + + err = 0; + +exit: + g_option_context_free(context); + + return err; +} + +static void nfctool_options_cleanup(void) +{ + if (opts.device_name != NULL) + g_free(opts.device_name); +} + +int main(int argc, char **argv) +{ + int err; + + err = nfctool_options_parse(argc, argv); + if (err) + goto exit_err; + + err = nl_init(); + if (err) + goto exit_err; + + err = nfctool_get_devices(); + if (err) + goto exit_err; + + if (opts.list) + nfctool_list_adapters(); + + err = 0; + +exit_err: + g_slist_free_full(adapters, (GDestroyNotify)nfctool_adapter_free); + + nl_cleanup(); + + nfctool_options_cleanup(); + + return err; +} diff --git a/tools/nfctool/netlink.c b/tools/nfctool/netlink.c new file mode 100644 index 0000000..c7bf07a --- /dev/null +++ b/tools/nfctool/netlink.c @@ -0,0 +1,428 @@ +/* + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include + +#include "nfctool.h" +#include "netlink.h" + +struct handler_args { + const char *group; + int id; +}; + +struct nlnfc_state { + struct nl_sock *cmd_sock; + struct nl_sock *event_sock; + int nfc_id; + int mcid; +}; + +static struct nlnfc_state *nfc_state = NULL; + +static void adapter_add_target(struct nfc_adapter *adapter, + guint8 type, guint32 idx) +{ + DBG("adapter_idx: %d, target_type: %d, target_idx: %d", adapter->idx, + type, idx); + + if (type == TARGET_TYPE_TAG) + adapter->tags = g_slist_append(adapter->tags, + GINT_TO_POINTER(idx)); + else + adapter->devices = g_slist_append(adapter->devices, + GINT_TO_POINTER(idx)); +} + +static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + DBG(""); + + *ret = err->error; + + return NL_STOP; +} + +static int nl_finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + DBG(""); + + *ret = 1; + + return NL_SKIP; +} + +static int nl_ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + DBG(""); + + *ret = 1; + + return NL_STOP; +} + +static int nl_send_msg(struct nl_sock *sock, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), + void *data) +{ + struct nl_cb *cb; + int err, done; + + DBG(""); + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (cb == NULL) + return -ENOMEM; + + err = nl_send_auto_complete(sock, msg); + if (err < 0) { + nl_cb_put(cb); + print_error("%s", strerror(err)); + + return err; + } + + err = done = 0; + + nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_handler, &done); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, &done); + + if (rx_handler != NULL) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + while (err == 0 && done == 0) + nl_recvmsgs(sock, cb); + + nl_cb_put(cb); + + return err; +} + +static int nl_family_handler(struct nl_msg *msg, void *arg) +{ + struct handler_args *grp = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int rem_mcgrp; + + DBG(""); + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { + struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; + + nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, + nla_data(mcgrp), nla_len(mcgrp), NULL); + + if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || + !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) + continue; + if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), + grp->group, + nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) + continue; + grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); + break; + } + + return NL_SKIP; +} + +static int nl_get_multicast_id(struct nl_sock *sock, const char *family, + const char *group) +{ + struct nl_msg *msg; + int err = 0, ctrlid; + struct handler_args grp = { + .group = group, + .id = -ENOENT, + }; + + DBG(""); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + ctrlid = genl_ctrl_resolve(sock, "nlctrl"); + + genlmsg_put(msg, 0, 0, ctrlid, 0, + 0, CTRL_CMD_GETFAMILY, 0); + + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + err = nl_send_msg(sock, msg, nl_family_handler, &grp); + if (err) + goto nla_put_failure; + + DBG("multicast id %d", grp.id); + + err = grp.id; + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + +static int nl_get_targets_handler(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + guint32 target_idx, target_type, protocols; + struct nfc_adapter *adapter; + + DBG(""); + + adapter = (struct nfc_adapter *)arg; + + genlmsg_parse(nlh, 0, attrs, NFC_ATTR_MAX, NULL); + + target_idx = nla_get_u32(attrs[NFC_ATTR_TARGET_INDEX]); + protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]); + + if (protocols & NFC_PROTO_NFC_DEP_MASK) + target_type = TARGET_TYPE_DEVICE; + else + target_type = TARGET_TYPE_TAG; + + adapter_add_target(adapter, target_type, target_idx); + + return 0; +} + +int nl_get_targets(struct nfc_adapter *adapter) +{ + struct nl_msg *msg; + void *hdr; + int err; + + DBG(""); + + if (nfc_state == NULL || nfc_state->nfc_id < 0) + return -ENODEV; + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_DUMP, NFC_CMD_GET_TARGET, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto nla_put_failure; + } + + err = -EMSGSIZE; + + g_slist_free(adapter->tags); + adapter->tags = NULL; + + g_slist_free(adapter->devices); + adapter->devices = NULL; + + NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, adapter->idx); + + err = nl_send_msg(nfc_state->cmd_sock, msg, + nl_get_targets_handler, adapter); + + DBG("nl_send_msg returns %d", err); + +nla_put_failure: + nlmsg_free(msg); + + return err; +} + +static int nl_get_devices_handler(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct nlattr *attrs[NFC_ATTR_MAX + 1]; + guint32 idx, protocols = 0; + guint8 powered = 0; + guint8 rf_mode = NFC_RF_NONE; + struct nfc_adapter *adapter; + + DBG(""); + + genlmsg_parse(nlh, 0, attrs, NFC_ATTR_MAX, NULL); + + if (attrs[NFC_ATTR_DEVICE_INDEX] == NULL) { + nl_perror(NLE_MISSING_ATTR, "NFC_CMD_GET_DEVICE"); + return NL_STOP; + } + + idx = nla_get_u32(attrs[NFC_ATTR_DEVICE_INDEX]); + + if (attrs[NFC_ATTR_PROTOCOLS] != NULL) + protocols = nla_get_u32(attrs[NFC_ATTR_PROTOCOLS]); + + if (attrs[NFC_ATTR_RF_MODE] != NULL) + rf_mode = nla_get_u8(attrs[NFC_ATTR_RF_MODE]); + + if (attrs[NFC_ATTR_DEVICE_POWERED] != NULL) + powered = nla_get_u8(attrs[NFC_ATTR_DEVICE_POWERED]); + + adapter = g_malloc0(sizeof(struct nfc_adapter)); + + adapter->idx = idx; + adapter->protocols = protocols; + adapter->powered = powered; + + if (rf_mode == NFC_RF_TARGET) + adapter_add_target(adapter, TARGET_TYPE_DEVICE, 0); + + adapters = g_slist_append(adapters, adapter); + + return NL_SKIP; +} + +int nl_get_devices(void) +{ + struct nl_msg *msg; + void *hdr; + int err; + + DBG(""); + + if (nfc_state == NULL || nfc_state->nfc_id < 0) + return -ENODEV; + + msg = nlmsg_alloc(); + if (msg == NULL) + return -ENOMEM; + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nfc_state->nfc_id, 0, + NLM_F_DUMP, NFC_CMD_GET_DEVICE, NFC_GENL_VERSION); + if (hdr == NULL) { + err = -EINVAL; + goto out; + } + + err = nl_send_msg(nfc_state->cmd_sock, msg, nl_get_devices_handler, + NULL); + + DBG("nl_send_msg returns %d", err); + +out: + nlmsg_free(msg); + + return err; +} + +void nl_cleanup(void) +{ + if (nfc_state) { + if (nfc_state->cmd_sock) + nl_socket_free(nfc_state->cmd_sock); + + if (nfc_state->event_sock) + nl_socket_free(nfc_state->event_sock); + + g_free(nfc_state); + nfc_state = NULL; + } +} + +int nl_init(void) +{ + int err; + + DBG(""); + + nfc_state = g_malloc0(sizeof(struct nlnfc_state)); + + nfc_state->cmd_sock = nl_socket_alloc(); + if (nfc_state->cmd_sock == NULL) { + print_error("Failed to allocate NFC netlink socket"); + err = -ENOMEM; + goto exit_err; + } + + nfc_state->event_sock = nl_socket_alloc(); + if (nfc_state->event_sock == NULL) { + print_error("Failed to allocate NFC netlink socket"); + err = -ENOMEM; + goto exit_err; + } + + if (genl_connect(nfc_state->cmd_sock)) { + print_error("Failed to connect to generic netlink"); + err = -ENOLINK; + goto exit_err; + } + + if (genl_connect(nfc_state->event_sock)) { + print_error("Failed to connect to generic netlink"); + err = -ENOLINK; + goto exit_err; + } + + nfc_state->nfc_id = genl_ctrl_resolve(nfc_state->cmd_sock, "nfc"); + if (nfc_state->nfc_id < 0) { + print_error("Unable to find NFC netlink family"); + err = -ENOENT; + goto exit_err; + } + + nfc_state->mcid = nl_get_multicast_id(nfc_state->cmd_sock, + NFC_GENL_NAME, + NFC_GENL_MCAST_EVENT_NAME); + if (nfc_state->mcid <= 0) { + print_error("Wrong mcast id %d", nfc_state->mcid); + err = nfc_state->mcid; + goto exit_err; + } + + err = nl_socket_add_membership(nfc_state->event_sock, nfc_state->mcid); + if (err) { + print_error("Error adding nl socket to membership"); + goto exit_err; + } + + return 0; + +exit_err: + nl_cleanup(); + + print_error("netlink init failed"); + + return err; +} diff --git a/tools/nfctool/netlink.h b/tools/nfctool/netlink.h new file mode 100644 index 0000000..99e4e91 --- /dev/null +++ b/tools/nfctool/netlink.h @@ -0,0 +1,34 @@ +/* + * + * 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 + * + */ +#ifndef __NETLINK_H +#define __NETLINK_H + +struct nfc_adapter; + +int nl_init(void); + +void nl_cleanup(void); + +int nl_get_devices(void); + +int nl_get_targets(struct nfc_adapter *adapter); + +#endif /* __NETLINK_H */ diff --git a/tools/nfctool/nfctool.h b/tools/nfctool/nfctool.h new file mode 100644 index 0000000..35d985b --- /dev/null +++ b/tools/nfctool/nfctool.h @@ -0,0 +1,61 @@ +/* + * + * 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 + * + */ + +#ifndef __NFCTOOL_H +#define __NFCTOOL_H + +#ifdef DEBUG +#define DBG(fmt, ...) fprintf(stdout, "%s: " fmt "\n", __func__, ## __VA_ARGS__) +#else +#define DBG(fmt, ...) +#endif + +#define print_error(fmt, ...) fprintf(stderr, fmt"\n", ## __VA_ARGS__) + +#define INVALID_ADAPTER_IDX 0xFFFFFFFF + +#define TARGET_TYPE_TAG 0 +#define TARGET_TYPE_DEVICE 1 + +struct nfc_target { + guint32 idx; + guint8 type; +}; + +struct nfc_adapter { + guint32 idx; + guint32 protocols; + guint8 powered; + GSList *tags; + GSList *devices; +}; + +struct nfctool_options { + gboolean list; + gchar *device_name; + guint32 adapter_idx; +}; + +extern struct nfctool_options opts; + +extern GSList *adapters; + +#endif /* __NFCTOOL_H */ -- 2.7.4