| /* |
| * |
| * 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; |
| } |