From 4aeee6871e8c3b043ef02996db8ac70a1af8be92 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:08 +0100 Subject: NFC: nci: Add dynamic logical connections support The current NCI core only support the RF static connection. For other NFC features such as Secure Element communication, we may need to create logical connections to the NFCEE (Execution Environment. In order to track each logical connection ID dynamically, we add a linked list of connection info pointers to the nci_dev structure. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 47 ++++++++++++++++++++++++++++++++++++++------- net/nfc/nci/data.c | 56 +++++++++++++++++++++++++++++++++++++++--------------- net/nfc/nci/ntf.c | 37 ++++++++++++++++++++++++++---------- net/nfc/nci/rsp.c | 20 ++++++++++++++++++- 4 files changed, 127 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 51feb5e63008..eb607970bd56 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -45,6 +45,19 @@ static void nci_cmd_work(struct work_struct *work); static void nci_rx_work(struct work_struct *work); static void nci_tx_work(struct work_struct *work); +struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev, + int conn_id) +{ + struct nci_conn_info *conn_info; + + list_for_each_entry(conn_info, &ndev->conn_info_list, list) { + if (conn_info->conn_id == conn_id) + return conn_info; + } + + return NULL; +} + /* ---- NCI requests ---- */ void nci_req_complete(struct nci_dev *ndev, int result) @@ -712,6 +725,11 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; + struct nci_conn_info *conn_info; + + conn_info = nci_get_conn_info_by_conn_id(ndev, NCI_STATIC_RF_CONN_ID); + if (!conn_info) + return -EPROTO; pr_debug("target_idx %d, len %d\n", target->idx, skb->len); @@ -724,8 +742,8 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, return -EBUSY; /* store cb and context to be used on receiving data */ - ndev->data_exchange_cb = cb; - ndev->data_exchange_cb_context = cb_context; + conn_info->data_exchange_cb = cb; + conn_info->data_exchange_cb_context = cb_context; rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); if (rc) @@ -913,6 +931,7 @@ int nci_register_device(struct nci_dev *ndev) (unsigned long) ndev); mutex_init(&ndev->req_lock); + INIT_LIST_HEAD(&ndev->conn_info_list); rc = nfc_register_device(ndev->nfc_dev); if (rc) @@ -938,12 +957,19 @@ EXPORT_SYMBOL(nci_register_device); */ void nci_unregister_device(struct nci_dev *ndev) { + struct nci_conn_info *conn_info, *n; + nci_close_device(ndev); destroy_workqueue(ndev->cmd_wq); destroy_workqueue(ndev->rx_wq); destroy_workqueue(ndev->tx_wq); + list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) { + list_del(&conn_info->list); + /* conn_info is allocated with devm_kzalloc */ + } + nfc_unregister_device(ndev->nfc_dev); } EXPORT_SYMBOL(nci_unregister_device); @@ -1027,20 +1053,25 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) static void nci_tx_work(struct work_struct *work) { struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); + struct nci_conn_info *conn_info; struct sk_buff *skb; - pr_debug("credits_cnt %d\n", atomic_read(&ndev->credits_cnt)); + conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); + if (!conn_info) + return; + + pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt)); /* Send queued tx data */ - while (atomic_read(&ndev->credits_cnt)) { + while (atomic_read(&conn_info->credits_cnt)) { skb = skb_dequeue(&ndev->tx_q); if (!skb) return; /* Check if data flow control is used */ - if (atomic_read(&ndev->credits_cnt) != + if (atomic_read(&conn_info->credits_cnt) != NCI_DATA_FLOW_CONTROL_NOT_USED) - atomic_dec(&ndev->credits_cnt); + atomic_dec(&conn_info->credits_cnt); pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", nci_pbf(skb->data), @@ -1092,7 +1123,9 @@ static void nci_rx_work(struct work_struct *work) if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { /* complete the data exchange transaction, if exists */ if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) - nci_data_exchange_complete(ndev, NULL, -ETIMEDOUT); + nci_data_exchange_complete(ndev, NULL, + ndev->cur_conn_id, + -ETIMEDOUT); clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); } diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index a2de2a8cb00e..566466d90048 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -36,10 +36,20 @@ /* Complete data exchange transaction and forward skb to nfc core */ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, - int err) + __u8 conn_id, int err) { - data_exchange_cb_t cb = ndev->data_exchange_cb; - void *cb_context = ndev->data_exchange_cb_context; + struct nci_conn_info *conn_info; + data_exchange_cb_t cb; + void *cb_context; + + conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); + if (!conn_info) { + kfree_skb(skb); + goto exit; + } + + cb = conn_info->data_exchange_cb; + cb_context = conn_info->data_exchange_cb_context; pr_debug("len %d, err %d\n", skb ? skb->len : 0, err); @@ -48,9 +58,6 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); if (cb) { - ndev->data_exchange_cb = NULL; - ndev->data_exchange_cb_context = NULL; - /* forward skb to nfc core */ cb(cb_context, skb, err); } else if (skb) { @@ -60,6 +67,7 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, kfree_skb(skb); } +exit: clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); } @@ -85,6 +93,7 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev, static int nci_queue_tx_data_frags(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) { + struct nci_conn_info *conn_info; int total_len = skb->len; unsigned char *data = skb->data; unsigned long flags; @@ -95,11 +104,17 @@ static int nci_queue_tx_data_frags(struct nci_dev *ndev, pr_debug("conn_id 0x%x, total_len %d\n", conn_id, total_len); + conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); + if (!conn_info) { + rc = -EPROTO; + goto free_exit; + } + __skb_queue_head_init(&frags_q); while (total_len) { frag_len = - min_t(int, total_len, ndev->max_data_pkt_payload_size); + min_t(int, total_len, conn_info->max_pkt_payload_len); skb_frag = nci_skb_alloc(ndev, (NCI_DATA_HDR_SIZE + frag_len), @@ -151,12 +166,19 @@ exit: /* Send NCI data */ int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) { + struct nci_conn_info *conn_info; int rc = 0; pr_debug("conn_id 0x%x, plen %d\n", conn_id, skb->len); + conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); + if (!conn_info) { + rc = -EPROTO; + goto free_exit; + } + /* check if the packet need to be fragmented */ - if (skb->len <= ndev->max_data_pkt_payload_size) { + if (skb->len <= conn_info->max_pkt_payload_len) { /* no need to fragment packet */ nci_push_data_hdr(ndev, conn_id, skb, NCI_PBF_LAST); @@ -170,6 +192,7 @@ int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) } } + ndev->cur_conn_id = conn_id; queue_work(ndev->tx_wq, &ndev->tx_work); goto exit; @@ -185,7 +208,7 @@ exit: static void nci_add_rx_data_frag(struct nci_dev *ndev, struct sk_buff *skb, - __u8 pbf, __u8 status) + __u8 pbf, __u8 conn_id, __u8 status) { int reassembly_len; int err = 0; @@ -229,16 +252,13 @@ static void nci_add_rx_data_frag(struct nci_dev *ndev, } exit: - if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) { - nci_data_exchange_complete(ndev, skb, err); - } else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) { + if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) { /* Data received in Target mode, forward to nfc core */ err = nfc_tm_data_received(ndev->nfc_dev, skb); if (err) pr_err("unable to handle received data\n"); } else { - pr_err("rf mode unknown\n"); - kfree_skb(skb); + nci_data_exchange_complete(ndev, skb, conn_id, err); } } @@ -247,6 +267,8 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u8 pbf = nci_pbf(skb->data); __u8 status = 0; + __u8 conn_id = nci_conn_id(skb->data); + struct nci_conn_info *conn_info; pr_debug("len %d\n", skb->len); @@ -255,6 +277,10 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_conn_id(skb->data), nci_plen(skb->data)); + conn_info = nci_get_conn_info_by_conn_id(ndev, nci_conn_id(skb->data)); + if (!conn_info) + return; + /* strip the nci data header */ skb_pull(skb, NCI_DATA_HDR_SIZE); @@ -268,5 +294,5 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) skb_trim(skb, (skb->len - 1)); } - nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status)); + nci_add_rx_data_frag(ndev, skb, pbf, conn_id, nci_to_errno(status)); } diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 22e453cb787d..28fdbe234bd4 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -43,6 +43,7 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; + struct nci_conn_info *conn_info; int i; pr_debug("num_entries %d\n", ntf->num_entries); @@ -59,11 +60,13 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, i, ntf->conn_entries[i].conn_id, ntf->conn_entries[i].credits); - if (ntf->conn_entries[i].conn_id == NCI_STATIC_RF_CONN_ID) { - /* found static rf connection */ - atomic_add(ntf->conn_entries[i].credits, - &ndev->credits_cnt); - } + conn_info = nci_get_conn_info_by_conn_id(ndev, + ntf->conn_entries[i].conn_id); + if (!conn_info) + return; + + atomic_add(ntf->conn_entries[i].credits, + &conn_info->credits_cnt); } /* trigger the next tx */ @@ -96,7 +99,7 @@ static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, /* complete the data exchange transaction, if exists */ if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) - nci_data_exchange_complete(ndev, NULL, -EIO); + nci_data_exchange_complete(ndev, NULL, ntf->conn_id, -EIO); } static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, @@ -513,6 +516,7 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev, static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { + struct nci_conn_info *conn_info; struct nci_rf_intf_activated_ntf ntf; __u8 *data = skb->data; int err = NCI_STATUS_OK; @@ -614,11 +618,17 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, exit: if (err == NCI_STATUS_OK) { - ndev->max_data_pkt_payload_size = ntf.max_data_pkt_payload_size; - ndev->initial_num_credits = ntf.initial_num_credits; + conn_info = nci_get_conn_info_by_conn_id(ndev, + NCI_STATIC_RF_CONN_ID); + if (!conn_info) + return; + + conn_info->max_pkt_payload_len = ntf.max_data_pkt_payload_size; + conn_info->initial_num_credits = ntf.initial_num_credits; /* set the available credits to initial value */ - atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); + atomic_set(&conn_info->credits_cnt, + conn_info->initial_num_credits); /* store general bytes to be reported later in dep_link_up */ if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) { @@ -661,10 +671,16 @@ exit: static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { + struct nci_conn_info *conn_info; struct nci_rf_deactivate_ntf *ntf = (void *) skb->data; pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); + conn_info = + nci_get_conn_info_by_conn_id(ndev, NCI_STATIC_RF_CONN_ID); + if (!conn_info) + return; + /* drop tx data queue */ skb_queue_purge(&ndev->tx_q); @@ -676,7 +692,8 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, /* complete the data exchange transaction, if exists */ if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) - nci_data_exchange_complete(ndev, NULL, -EIO); + nci_data_exchange_complete(ndev, NULL, NCI_STATIC_RF_CONN_ID, + -EIO); switch (ntf->type) { case NCI_DEACTIVATE_TYPE_IDLE_MODE: diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 041de51ccdbe..93b914937263 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -140,13 +140,31 @@ static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { + struct nci_conn_info *conn_info; __u8 status = skb->data[0]; pr_debug("status 0x%x\n", status); - if (status == NCI_STATUS_OK) + if (status == NCI_STATUS_OK) { atomic_set(&ndev->state, NCI_DISCOVERY); + conn_info = nci_get_conn_info_by_conn_id(ndev, + NCI_STATIC_RF_CONN_ID); + if (!conn_info) { + conn_info = devm_kzalloc(&ndev->nfc_dev->dev, + sizeof(struct nci_conn_info), + GFP_KERNEL); + if (!conn_info) { + status = NCI_STATUS_REJECTED; + goto exit; + } + conn_info->conn_id = NCI_STATIC_RF_CONN_ID; + INIT_LIST_HEAD(&conn_info->list); + list_add(&conn_info->list, &ndev->conn_info_list); + } + } + +exit: nci_req_complete(ndev, status); } -- cgit v1.2.3 From af9c8aa67d07adcd3b41fb2934af7af056eabecf Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:10 +0100 Subject: NFC: nci: Add NFCEE discover support NFCEEs (NFC Execution Environment) have to be explicitly discovered by sending the NCI_OP_NFCEE_DISCOVER_CMD command. The NFCC will respond to this command by telling us how many NFCEEs are connected to it. Then the NFCC sends a notification command for each and every NFCEE connected. Here we implement support for sending NCI_OP_NFCEE_DISCOVER_CMD command, receiving the response and the potential notifications. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 17 +++++++++++++++++ net/nfc/nci/ntf.c | 30 ++++++++++++++++++++++++++++++ net/nfc/nci/rsp.c | 21 +++++++++++++++++++++ 3 files changed, 68 insertions(+) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index eb607970bd56..a25857548524 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -469,6 +469,23 @@ int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) } EXPORT_SYMBOL(nci_set_config); +static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_nfcee_discover_cmd cmd; + __u8 action = opt; + + cmd.discovery_action = action; + + nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd); +} + +int nci_nfcee_discover(struct nci_dev *ndev, u8 action) +{ + return nci_request(ndev, nci_nfcee_discover_req, action, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); +} +EXPORT_SYMBOL(nci_nfcee_discover); + static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 28fdbe234bd4..4c0be7e82d29 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -713,6 +713,33 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, nci_req_complete(ndev, NCI_STATUS_OK); } +static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + u8 status = NCI_STATUS_OK; + struct nci_conn_info *conn_info; + struct nci_nfcee_discover_ntf *nfcee_ntf = + (struct nci_nfcee_discover_ntf *)skb->data; + + pr_debug("\n"); + + conn_info = devm_kzalloc(&ndev->nfc_dev->dev, + sizeof(struct nci_conn_info), GFP_KERNEL); + if (!conn_info) { + status = NCI_STATUS_REJECTED; + goto exit; + } + + conn_info->id = nfcee_ntf->nfcee_id; + conn_info->conn_id = NCI_INVALID_CONN_ID; + + INIT_LIST_HEAD(&conn_info->list); + list_add(&conn_info->list, &ndev->conn_info_list); + +exit: + nci_req_complete(ndev, status); +} + void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u16 ntf_opcode = nci_opcode(skb->data); @@ -751,6 +778,9 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_rf_deactivate_ntf_packet(ndev, skb); break; + case NCI_OP_NFCEE_DISCOVER_NTF: + nci_nfcee_discover_ntf_packet(ndev, skb); + break; default: pr_err("unknown ntf opcode 0x%x\n", ntf_opcode); break; diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 93b914937263..ee094dfab2ed 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -196,6 +196,23 @@ static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, } } +static void nci_nfcee_discover_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_nfcee_discover_rsp *discover_rsp; + + if (skb->len != 2) { + nci_req_complete(ndev, NCI_STATUS_NFCEE_PROTOCOL_ERROR); + return; + } + + discover_rsp = (struct nci_nfcee_discover_rsp *)skb->data; + + if (discover_rsp->status != NCI_STATUS_OK || + discover_rsp->num_nfcee == 0) + nci_req_complete(ndev, discover_rsp->status); +} + void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u16 rsp_opcode = nci_opcode(skb->data); @@ -241,6 +258,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_rf_deactivate_rsp_packet(ndev, skb); break; + case NCI_OP_NFCEE_DISCOVER_RSP: + nci_nfcee_discover_rsp_packet(ndev, skb); + break; + default: pr_err("unknown rsp opcode 0x%x\n", rsp_opcode); break; -- cgit v1.2.3 From f7f793f31378d5e83276871339c2a8374b0e8657 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:11 +0100 Subject: NFC: nci: Add NFCEE enabling and disabling support NFCEEs can be enabled or disabled by sending the NCI_OP_NFCEE_MODE_SET_CMD command to the NFCC. This patch provides an API for drivers to enable and disable e.g. their NCI discoveredd secure elements. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 21 +++++++++++++++++++++ net/nfc/nci/rsp.c | 13 +++++++++++++ 2 files changed, 34 insertions(+) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index a25857548524..e5fb8c8eed94 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -486,6 +486,27 @@ int nci_nfcee_discover(struct nci_dev *ndev, u8 action) } EXPORT_SYMBOL(nci_nfcee_discover); +static void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_nfcee_mode_set_cmd *cmd = + (struct nci_nfcee_mode_set_cmd *)opt; + + nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD, + sizeof(struct nci_nfcee_mode_set_cmd), cmd); +} + +int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) +{ + struct nci_nfcee_mode_set_cmd cmd; + + cmd.nfcee_id = nfcee_id; + cmd.nfcee_mode = nfcee_mode; + + return nci_request(ndev, nci_nfcee_mode_set_req, (unsigned long)&cmd, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); +} +EXPORT_SYMBOL(nci_nfcee_mode_set); + static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index ee094dfab2ed..0a3e98240dd6 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -213,6 +213,15 @@ static void nci_nfcee_discover_rsp_packet(struct nci_dev *ndev, nci_req_complete(ndev, discover_rsp->status); } +static void nci_nfcee_mode_set_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + pr_debug("status 0x%x\n", status); + nci_req_complete(ndev, status); +} + void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u16 rsp_opcode = nci_opcode(skb->data); @@ -262,6 +271,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_nfcee_discover_rsp_packet(ndev, skb); break; + case NCI_OP_NFCEE_MODE_SET_RSP: + nci_nfcee_mode_set_rsp_packet(ndev, skb); + break; + default: pr_err("unknown rsp opcode 0x%x\n", rsp_opcode); break; -- cgit v1.2.3 From 736bb9577407d3556d81c3c3cd57581cd3ae10ea Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:12 +0100 Subject: NFC: nci: Support logical connections management In order to communicate with an NFCEE, we need to open a logical connection to it, by sending the NCI_OP_CORE_CONN_CREATE_CMD command to the NFCC. It's left up to the drivers to decide when to close an already opened logical connection. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 38 ++++++++++++++++++++++++++++++++++++++ net/nfc/nci/rsp.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index e5fb8c8eed94..2a96ed68c7bb 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -507,6 +507,44 @@ int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) } EXPORT_SYMBOL(nci_nfcee_mode_set); +static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_core_conn_create_cmd cmd; + struct core_conn_create_dest_spec_params *params = + (struct core_conn_create_dest_spec_params *)opt; + + cmd.destination_type = NCI_DESTINATION_NFCEE; + cmd.number_destination_params = 1; + memcpy(&cmd.params.type, params, + sizeof(struct core_conn_create_dest_spec_params)); + nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, + sizeof(struct nci_core_conn_create_cmd), &cmd); +} + +int nci_core_conn_create(struct nci_dev *ndev, + struct core_conn_create_dest_spec_params *params) +{ + ndev->cur_id = params->value.id; + return nci_request(ndev, nci_core_conn_create_req, + (unsigned long)params, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); +} +EXPORT_SYMBOL(nci_core_conn_create); + +static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt) +{ + __u8 conn_id = opt; + + nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id); +} + +int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id) +{ + return nci_request(ndev, nci_core_conn_close_req, conn_id, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); +} +EXPORT_SYMBOL(nci_core_conn_close); + static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 0a3e98240dd6..31ccf7d05e82 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -222,6 +222,45 @@ static void nci_nfcee_mode_set_rsp_packet(struct nci_dev *ndev, nci_req_complete(ndev, status); } +static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + struct nci_conn_info *conn_info; + struct nci_core_conn_create_rsp *rsp; + + pr_debug("status 0x%x\n", status); + + if (status == NCI_STATUS_OK) { + rsp = (struct nci_core_conn_create_rsp *)skb->data; + list_for_each_entry(conn_info, &ndev->conn_info_list, list) { + if (conn_info->id == ndev->cur_id) + break; + } + + if (!conn_info || conn_info->id != ndev->cur_id) { + status = NCI_STATUS_REJECTED; + goto exit; + } + + conn_info->conn_id = rsp->conn_id; + conn_info->max_pkt_payload_len = rsp->max_ctrl_pkt_payload_len; + atomic_set(&conn_info->credits_cnt, rsp->credits); + } + +exit: + nci_req_complete(ndev, status); +} + +static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + pr_debug("status 0x%x\n", status); + nci_req_complete(ndev, status); +} + void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u16 rsp_opcode = nci_opcode(skb->data); @@ -251,6 +290,14 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_core_set_config_rsp_packet(ndev, skb); break; + case NCI_OP_CORE_CONN_CREATE_RSP: + nci_core_conn_create_rsp_packet(ndev, skb); + break; + + case NCI_OP_CORE_CONN_CLOSE_RSP: + nci_core_conn_close_rsp_packet(ndev, skb); + break; + case NCI_OP_RF_DISCOVER_MAP_RSP: nci_rf_disc_map_rsp_packet(ndev, skb); break; -- cgit v1.2.3 From 11f54f228643d0248ec00ce8c9fb8d872f87e7b8 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:14 +0100 Subject: NFC: nci: Add HCI over NCI protocol support According to the NCI specification, one can use HCI over NCI to talk with specific NFCEE. The HCI network is viewed as one logical NFCEE. This is needed to support secure element running HCI only firmwares embedded on an NCI capable chipset, like e.g. the st21nfcb. There is some duplication between this piece of code and the HCI core code, but the latter would need to be abstracted even more to be able to use NCI as a logical transport for HCP packets. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/Makefile | 2 +- net/nfc/nci/core.c | 20 +- net/nfc/nci/hci.c | 686 +++++++++++++++++++++++++++++++++++++++++++++++++++ net/nfc/nci/ntf.c | 32 ++- 4 files changed, 722 insertions(+), 18 deletions(-) create mode 100644 net/nfc/nci/hci.c (limited to 'net') diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile index 7aeedc43187d..7ed8949266cc 100644 --- a/net/nfc/nci/Makefile +++ b/net/nfc/nci/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_NFC_NCI) += nci.o -nci-objs := core.o data.o lib.o ntf.o rsp.o +nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o nci-$(CONFIG_NFC_NCI_SPI) += spi.o diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 2a96ed68c7bb..f74d420e2ead 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -122,10 +122,10 @@ static int __nci_request(struct nci_dev *ndev, return rc; } -static inline int nci_request(struct nci_dev *ndev, - void (*req)(struct nci_dev *ndev, - unsigned long opt), - unsigned long opt, __u32 timeout) +inline int nci_request(struct nci_dev *ndev, + void (*req)(struct nci_dev *ndev, + unsigned long opt), + unsigned long opt, __u32 timeout) { int rc; @@ -901,7 +901,6 @@ static struct nfc_ops nci_nfc_ops = { }; /* ---- Interface to NCI drivers ---- */ - /** * nci_allocate_device - allocate a new nci device * @@ -936,13 +935,20 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops, tx_headroom + NCI_DATA_HDR_SIZE, tx_tailroom); if (!ndev->nfc_dev) - goto free_exit; + goto free_nci; + + ndev->hci_dev = nci_hci_allocate(ndev); + if (!ndev->hci_dev) + goto free_nfc; nfc_set_drvdata(ndev->nfc_dev, ndev); return ndev; -free_exit: +free_nfc: + kfree(ndev->nfc_dev); + +free_nci: kfree(ndev); return NULL; } diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c new file mode 100644 index 000000000000..ecf253942606 --- /dev/null +++ b/net/nfc/nci/hci.c @@ -0,0 +1,686 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * This is the HCI over NCI implementation, as specified in the 10.2 + * section of the NCI 1.1 specification. + * + * Copyright (C) 2014 STMicroelectronics SAS. 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, see . + * + */ + +#include + +#include "../nfc.h" +#include +#include +#include + +struct nci_data { + u8 conn_id; + u8 pipe; + u8 cmd; + const u8 *data; + u32 data_len; +} __packed; + +struct nci_hci_create_pipe_params { + u8 src_gate; + u8 dest_host; + u8 dest_gate; +} __packed; + +struct nci_hci_create_pipe_resp { + u8 src_host; + u8 src_gate; + u8 dest_host; + u8 dest_gate; + u8 pipe; +} __packed; + +struct nci_hci_delete_pipe_noti { + u8 pipe; +} __packed; + +struct nci_hci_all_pipe_cleared_noti { + u8 host; +} __packed; + +struct nci_hcp_message { + u8 header; /* type -cmd,evt,rsp- + instruction */ + u8 data[]; +} __packed; + +struct nci_hcp_packet { + u8 header; /* cbit+pipe */ + struct nci_hcp_message message; +} __packed; + +#define NCI_HCI_ANY_SET_PARAMETER 0x01 +#define NCI_HCI_ANY_GET_PARAMETER 0x02 +#define NCI_HCI_ANY_CLOSE_PIPE 0x04 + +#define NCI_HFP_NO_CHAINING 0x80 + +#define NCI_NFCEE_ID_HCI 0x80 + +#define NCI_EVT_HOT_PLUG 0x03 + +#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01 + +/* HCP headers */ +#define NCI_HCI_HCP_PACKET_HEADER_LEN 1 +#define NCI_HCI_HCP_MESSAGE_HEADER_LEN 1 +#define NCI_HCI_HCP_HEADER_LEN 2 + +/* HCP types */ +#define NCI_HCI_HCP_COMMAND 0x00 +#define NCI_HCI_HCP_EVENT 0x01 +#define NCI_HCI_HCP_RESPONSE 0x02 + +#define NCI_HCI_ADM_NOTIFY_PIPE_CREATED 0x12 +#define NCI_HCI_ADM_NOTIFY_PIPE_DELETED 0x13 +#define NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED 0x15 + +#define NCI_HCI_FRAGMENT 0x7f +#define NCI_HCP_HEADER(type, instr) ((((type) & 0x03) << 6) |\ + ((instr) & 0x3f)) + +#define NCI_HCP_MSG_GET_TYPE(header) ((header & 0xc0) >> 6) +#define NCI_HCP_MSG_GET_CMD(header) (header & 0x3f) +#define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f) + +/* HCI core */ +static void nci_hci_reset_pipes(struct nci_hci_dev *hdev) +{ + int i; + + for (i = 0; i < NCI_HCI_MAX_PIPES; i++) { + hdev->pipes[i].gate = NCI_HCI_INVALID_GATE; + hdev->pipes[i].host = NCI_HCI_INVALID_HOST; + } + memset(hdev->gate2pipe, NCI_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); +} + +static void nci_hci_reset_pipes_per_host(struct nci_dev *ndev, u8 host) +{ + int i; + + for (i = 0; i < NCI_HCI_MAX_PIPES; i++) { + if (ndev->hci_dev->pipes[i].host == host) { + ndev->hci_dev->pipes[i].gate = NCI_HCI_INVALID_GATE; + ndev->hci_dev->pipes[i].host = NCI_HCI_INVALID_HOST; + } + } +} + +/* Fragment HCI data over NCI packet. + * NFC Forum NCI 10.2.2 Data Exchange: + * The payload of the Data Packets sent on the Logical Connection SHALL be + * valid HCP packets, as defined within [ETSI_102622]. Each Data Packet SHALL + * contain a single HCP packet. NCI Segmentation and Reassembly SHALL NOT be + * applied to Data Messages in either direction. The HCI fragmentation mechanism + * is used if required. + */ +static int nci_hci_send_data(struct nci_dev *ndev, u8 pipe, + const u8 data_type, const u8 *data, + size_t data_len) +{ + struct nci_conn_info *conn_info; + struct sk_buff *skb; + int len, i, r; + u8 cb = pipe; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + return -EPROTO; + + skb = nci_skb_alloc(ndev, 2 + conn_info->max_pkt_payload_len + + NCI_DATA_HDR_SIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, 2 + NCI_DATA_HDR_SIZE); + *skb_push(skb, 1) = data_type; + + i = 0; + len = conn_info->max_pkt_payload_len; + + do { + /* If last packet add NCI_HFP_NO_CHAINING */ + if (i + conn_info->max_pkt_payload_len - + (skb->len + 1) >= data_len) { + cb |= NCI_HFP_NO_CHAINING; + len = data_len - i; + } else { + len = conn_info->max_pkt_payload_len - skb->len - 1; + } + + *skb_push(skb, 1) = cb; + + if (len > 0) + memcpy(skb_put(skb, len), data + i, len); + + r = nci_send_data(ndev, conn_info->conn_id, skb); + if (r < 0) + return r; + + i += len; + if (i < data_len) { + skb_trim(skb, 0); + skb_pull(skb, len); + } + } while (i < data_len); + + return i; +} + +static void nci_hci_send_data_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_data *data = (struct nci_data *)opt; + + nci_hci_send_data(ndev, data->pipe, data->cmd, + data->data, data->data_len); +} + +int nci_hci_send_event(struct nci_dev *ndev, u8 gate, u8 event, + const u8 *param, size_t param_len) +{ + u8 pipe = ndev->hci_dev->gate2pipe[gate]; + + if (pipe == NCI_HCI_INVALID_PIPE) + return -EADDRNOTAVAIL; + + return nci_hci_send_data(ndev, pipe, + NCI_HCP_HEADER(NCI_HCI_HCP_EVENT, event), + param, param_len); +} +EXPORT_SYMBOL(nci_hci_send_event); + +int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, + const u8 *param, size_t param_len, + struct sk_buff **skb) +{ + struct nci_conn_info *conn_info; + struct nci_data data; + int r; + u8 pipe = ndev->hci_dev->gate2pipe[gate]; + + if (pipe == NCI_HCI_INVALID_PIPE) + return -EADDRNOTAVAIL; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + return -EPROTO; + + data.conn_id = conn_info->conn_id; + data.pipe = pipe; + data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, cmd); + data.data = param; + data.data_len = param_len; + + r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, + msecs_to_jiffies(NCI_DATA_TIMEOUT)); + + if (r == NCI_STATUS_OK) + *skb = conn_info->rx_skb; + + return r; +} +EXPORT_SYMBOL(nci_hci_send_cmd); + +static void nci_hci_event_received(struct nci_dev *ndev, u8 pipe, + u8 event, struct sk_buff *skb) +{ + if (ndev->ops->hci_event_received) + ndev->ops->hci_event_received(ndev, pipe, event, skb); +} + +static void nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, + u8 cmd, struct sk_buff *skb) +{ + u8 gate = ndev->hci_dev->pipes[pipe].gate; + u8 status = NCI_HCI_ANY_OK | ~NCI_HCI_FRAGMENT; + u8 dest_gate, new_pipe; + struct nci_hci_create_pipe_resp *create_info; + struct nci_hci_delete_pipe_noti *delete_info; + struct nci_hci_all_pipe_cleared_noti *cleared_info; + + pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd); + + switch (cmd) { + case NCI_HCI_ADM_NOTIFY_PIPE_CREATED: + if (skb->len != 5) { + status = NCI_HCI_ANY_E_NOK; + goto exit; + } + create_info = (struct nci_hci_create_pipe_resp *)skb->data; + dest_gate = create_info->dest_gate; + new_pipe = create_info->pipe; + + /* Save the new created pipe and bind with local gate, + * the description for skb->data[3] is destination gate id + * but since we received this cmd from host controller, we + * are the destination and it is our local gate + */ + ndev->hci_dev->gate2pipe[dest_gate] = new_pipe; + ndev->hci_dev->pipes[new_pipe].gate = dest_gate; + ndev->hci_dev->pipes[new_pipe].host = + create_info->src_host; + break; + case NCI_HCI_ANY_OPEN_PIPE: + /* If the pipe is not created report an error */ + if (gate == NCI_HCI_INVALID_GATE) { + status = NCI_HCI_ANY_E_NOK; + goto exit; + } + break; + case NCI_HCI_ADM_NOTIFY_PIPE_DELETED: + if (skb->len != 1) { + status = NCI_HCI_ANY_E_NOK; + goto exit; + } + delete_info = (struct nci_hci_delete_pipe_noti *)skb->data; + + ndev->hci_dev->pipes[delete_info->pipe].gate = + NCI_HCI_INVALID_GATE; + ndev->hci_dev->pipes[delete_info->pipe].host = + NCI_HCI_INVALID_HOST; + break; + case NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED: + if (skb->len != 1) { + status = NCI_HCI_ANY_E_NOK; + goto exit; + } + + cleared_info = + (struct nci_hci_all_pipe_cleared_noti *)skb->data; + nci_hci_reset_pipes_per_host(ndev, cleared_info->host); + break; + default: + pr_debug("Discarded unknown cmd %x to gate %x\n", cmd, gate); + break; + } + + if (ndev->ops->hci_cmd_received) + ndev->ops->hci_cmd_received(ndev, pipe, cmd, skb); + +exit: + nci_hci_send_data(ndev, pipe, status, NULL, 0); + + kfree_skb(skb); +} + +static void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe, + u8 result, struct sk_buff *skb) +{ + struct nci_conn_info *conn_info; + u8 status = result; + + if (result != NCI_HCI_ANY_OK) + goto exit; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) { + status = NCI_STATUS_REJECTED; + goto exit; + } + + conn_info->rx_skb = skb; + +exit: + nci_req_complete(ndev, status); +} + +/* Receive hcp message for pipe, with type and cmd. + * skb contains optional message data only. + */ +static void nci_hci_hcp_message_rx(struct nci_dev *ndev, u8 pipe, + u8 type, u8 instruction, struct sk_buff *skb) +{ + switch (type) { + case NCI_HCI_HCP_RESPONSE: + nci_hci_resp_received(ndev, pipe, instruction, skb); + break; + case NCI_HCI_HCP_COMMAND: + nci_hci_cmd_received(ndev, pipe, instruction, skb); + break; + case NCI_HCI_HCP_EVENT: + nci_hci_event_received(ndev, pipe, instruction, skb); + break; + default: + pr_err("UNKNOWN MSG Type %d, instruction=%d\n", + type, instruction); + kfree_skb(skb); + break; + } + + nci_req_complete(ndev, 0); +} + +static void nci_hci_msg_rx_work(struct work_struct *work) +{ + struct nci_hci_dev *hdev = + container_of(work, struct nci_hci_dev, msg_rx_work); + struct sk_buff *skb; + struct nci_hcp_message *message; + u8 pipe, type, instruction; + + while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) { + pipe = skb->data[0]; + skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN); + message = (struct nci_hcp_message *)skb->data; + type = NCI_HCP_MSG_GET_TYPE(message->header); + instruction = NCI_HCP_MSG_GET_CMD(message->header); + skb_pull(skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); + + nci_hci_hcp_message_rx(hdev->ndev, pipe, + type, instruction, skb); + } +} + +void nci_hci_data_received_cb(void *context, + struct sk_buff *skb, int err) +{ + struct nci_dev *ndev = (struct nci_dev *)context; + struct nci_hcp_packet *packet; + u8 pipe, type, instruction; + struct sk_buff *hcp_skb; + struct sk_buff *frag_skb; + int msg_len; + + pr_debug("\n"); + + if (err) { + nci_req_complete(ndev, err); + return; + } + + packet = (struct nci_hcp_packet *)skb->data; + if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) { + skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); + return; + } + + /* it's the last fragment. Does it need re-aggregation? */ + if (skb_queue_len(&ndev->hci_dev->rx_hcp_frags)) { + pipe = packet->header & NCI_HCI_FRAGMENT; + skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); + + msg_len = 0; + skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) { + msg_len += (frag_skb->len - + NCI_HCI_HCP_PACKET_HEADER_LEN); + } + + hcp_skb = nfc_alloc_recv_skb(NCI_HCI_HCP_PACKET_HEADER_LEN + + msg_len, GFP_KERNEL); + if (!hcp_skb) { + nci_req_complete(ndev, -ENOMEM); + return; + } + + *skb_put(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN) = pipe; + + skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) { + msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN; + memcpy(skb_put(hcp_skb, msg_len), frag_skb->data + + NCI_HCI_HCP_PACKET_HEADER_LEN, msg_len); + } + + skb_queue_purge(&ndev->hci_dev->rx_hcp_frags); + } else { + packet->header &= NCI_HCI_FRAGMENT; + hcp_skb = skb; + } + + /* if this is a response, dispatch immediately to + * unblock waiting cmd context. Otherwise, enqueue to dispatch + * in separate context where handler can also execute command. + */ + packet = (struct nci_hcp_packet *)hcp_skb->data; + type = NCI_HCP_MSG_GET_TYPE(packet->message.header); + if (type == NCI_HCI_HCP_RESPONSE) { + pipe = packet->header; + instruction = NCI_HCP_MSG_GET_CMD(packet->message.header); + skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN + + NCI_HCI_HCP_MESSAGE_HEADER_LEN); + nci_hci_hcp_message_rx(ndev, pipe, type, instruction, hcp_skb); + } else { + skb_queue_tail(&ndev->hci_dev->msg_rx_queue, hcp_skb); + schedule_work(&ndev->hci_dev->msg_rx_work); + } +} + +int nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe) +{ + struct nci_data data; + struct nci_conn_info *conn_info; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + return -EPROTO; + + data.conn_id = conn_info->conn_id; + data.pipe = pipe; + data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, + NCI_HCI_ANY_OPEN_PIPE); + data.data = NULL; + data.data_len = 0; + + return nci_request(ndev, nci_hci_send_data_req, + (unsigned long)&data, + msecs_to_jiffies(NCI_DATA_TIMEOUT)); +} +EXPORT_SYMBOL(nci_hci_open_pipe); + +int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx, + const u8 *param, size_t param_len) +{ + struct nci_conn_info *conn_info; + struct nci_data data; + int r; + u8 *tmp; + u8 pipe = ndev->hci_dev->gate2pipe[gate]; + + pr_debug("idx=%d to gate %d\n", idx, gate); + + if (pipe == NCI_HCI_INVALID_PIPE) + return -EADDRNOTAVAIL; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + return -EPROTO; + + tmp = kmalloc(1 + param_len, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + *tmp = idx; + memcpy(tmp + 1, param, param_len); + + data.conn_id = conn_info->conn_id; + data.pipe = pipe; + data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, + NCI_HCI_ANY_SET_PARAMETER); + data.data = tmp; + data.data_len = param_len + 1; + + r = nci_request(ndev, nci_hci_send_data_req, + (unsigned long)&data, + msecs_to_jiffies(NCI_DATA_TIMEOUT)); + + kfree(tmp); + return r; +} +EXPORT_SYMBOL(nci_hci_set_param); + +int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx, + struct sk_buff **skb) +{ + struct nci_conn_info *conn_info; + struct nci_data data; + int r; + u8 pipe = ndev->hci_dev->gate2pipe[gate]; + + pr_debug("idx=%d to gate %d\n", idx, gate); + + if (pipe == NCI_HCI_INVALID_PIPE) + return -EADDRNOTAVAIL; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + return -EPROTO; + + data.conn_id = conn_info->conn_id; + data.pipe = pipe; + data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, + NCI_HCI_ANY_GET_PARAMETER); + data.data = &idx; + data.data_len = 1; + + r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, + msecs_to_jiffies(NCI_DATA_TIMEOUT)); + + if (r == NCI_STATUS_OK) + *skb = conn_info->rx_skb; + + return r; +} +EXPORT_SYMBOL(nci_hci_get_param); + +int nci_hci_connect_gate(struct nci_dev *ndev, + u8 dest_host, u8 dest_gate, u8 pipe) +{ + int r; + + if (pipe == NCI_HCI_DO_NOT_OPEN_PIPE) + return 0; + + if (ndev->hci_dev->gate2pipe[dest_gate] != NCI_HCI_INVALID_PIPE) + return -EADDRINUSE; + + if (pipe != NCI_HCI_INVALID_PIPE) + goto open_pipe; + + switch (dest_gate) { + case NCI_HCI_LINK_MGMT_GATE: + pipe = NCI_HCI_LINK_MGMT_PIPE; + break; + case NCI_HCI_ADMIN_GATE: + pipe = NCI_HCI_ADMIN_PIPE; + break; + } + +open_pipe: + r = nci_hci_open_pipe(ndev, pipe); + if (r < 0) + return r; + + ndev->hci_dev->pipes[pipe].gate = dest_gate; + ndev->hci_dev->pipes[pipe].host = dest_host; + ndev->hci_dev->gate2pipe[dest_gate] = pipe; + + return 0; +} +EXPORT_SYMBOL(nci_hci_connect_gate); + +static int nci_hci_dev_connect_gates(struct nci_dev *ndev, + u8 gate_count, + struct nci_hci_gate *gates) +{ + int r; + + while (gate_count--) { + r = nci_hci_connect_gate(ndev, gates->dest_host, + gates->gate, gates->pipe); + if (r < 0) + return r; + gates++; + } + + return 0; +} + +int nci_hci_dev_session_init(struct nci_dev *ndev) +{ + struct sk_buff *skb; + int r; + + ndev->hci_dev->count_pipes = 0; + ndev->hci_dev->expected_pipes = 0; + + nci_hci_reset_pipes(ndev->hci_dev); + + if (ndev->hci_dev->init_data.gates[0].gate != NCI_HCI_ADMIN_GATE) + return -EPROTO; + + r = nci_hci_connect_gate(ndev, + ndev->hci_dev->init_data.gates[0].dest_host, + ndev->hci_dev->init_data.gates[0].gate, + ndev->hci_dev->init_data.gates[0].pipe); + if (r < 0) + goto exit; + + r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, &skb); + if (r < 0) + goto exit; + + if (skb->len && + skb->len == strlen(ndev->hci_dev->init_data.session_id) && + memcmp(ndev->hci_dev->init_data.session_id, + skb->data, skb->len) == 0 && + ndev->ops->hci_load_session) { + /* Restore gate<->pipe table from some proprietary location. */ + r = ndev->ops->hci_load_session(ndev); + if (r < 0) + goto exit; + } else { + r = nci_hci_dev_connect_gates(ndev, + ndev->hci_dev->init_data.gate_count, + ndev->hci_dev->init_data.gates); + if (r < 0) + goto exit; + + r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, + ndev->hci_dev->init_data.session_id, + strlen(ndev->hci_dev->init_data.session_id)); + } + if (r == 0) + goto exit; + +exit: + kfree_skb(skb); + + return r; +} +EXPORT_SYMBOL(nci_hci_dev_session_init); + +struct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev) +{ + struct nci_hci_dev *hdev; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return NULL; + + skb_queue_head_init(&hdev->rx_hcp_frags); + INIT_WORK(&hdev->msg_rx_work, nci_hci_msg_rx_work); + skb_queue_head_init(&hdev->msg_rx_queue); + hdev->ndev = ndev; + + return hdev; +} diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 4c0be7e82d29..6e041ac49e17 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -723,18 +723,30 @@ static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev, pr_debug("\n"); - conn_info = devm_kzalloc(&ndev->nfc_dev->dev, - sizeof(struct nci_conn_info), GFP_KERNEL); - if (!conn_info) { - status = NCI_STATUS_REJECTED; - goto exit; - } + /* NFCForum NCI 9.2.1 HCI Network Specific Handling + * If the NFCC supports the HCI Network, it SHALL return one, + * and only one, NFCEE_DISCOVER_NTF with a Protocol type of + * “HCI Access”, even if the HCI Network contains multiple NFCEEs. + */ + if (!ndev->hci_dev->conn_info) { + conn_info = devm_kzalloc(&ndev->nfc_dev->dev, + sizeof(*conn_info), GFP_KERNEL); + if (!conn_info) { + status = NCI_STATUS_REJECTED; + goto exit; + } - conn_info->id = nfcee_ntf->nfcee_id; - conn_info->conn_id = NCI_INVALID_CONN_ID; + conn_info->id = nfcee_ntf->nfcee_id; + conn_info->conn_id = NCI_INVALID_CONN_ID; - INIT_LIST_HEAD(&conn_info->list); - list_add(&conn_info->list, &ndev->conn_info_list); + conn_info->data_exchange_cb = nci_hci_data_received_cb; + conn_info->data_exchange_cb_context = ndev; + + INIT_LIST_HEAD(&conn_info->list); + list_add(&conn_info->list, &ndev->conn_info_list); + + ndev->hci_dev->conn_info = conn_info; + } exit: nci_req_complete(ndev, status); -- cgit v1.2.3 From 447b27c4f29b510b98e99395120d635f009ed563 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:16 +0100 Subject: NFC: Forward NFC_EVT_TRANSACTION to user space NFC_EVT_TRANSACTION is sent through netlink in order for a specific application running on a secure element to notify userspace of an event. Typically the secure element application counterpart on the host could interpret that event and act upon it. Forwarded information contains: - SE host generating the event - Application IDentifier doing the operation - Applications parameters Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/core.c | 21 +++++++++++++++++++++ net/nfc/netlink.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ net/nfc/nfc.h | 2 ++ 3 files changed, 70 insertions(+) (limited to 'net') diff --git a/net/nfc/core.c b/net/nfc/core.c index 7f1b6351755c..cff3f1614ad4 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -932,6 +932,27 @@ int nfc_remove_se(struct nfc_dev *dev, u32 se_idx) } EXPORT_SYMBOL(nfc_remove_se); +int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx, + struct nfc_evt_transaction *evt_transaction) +{ + int rc; + + pr_debug("transaction: %x\n", se_idx); + + device_lock(&dev->dev); + + if (!evt_transaction) { + rc = -EPROTO; + goto out; + } + + rc = nfc_genl_se_transaction(dev, se_idx, evt_transaction); +out: + device_unlock(&dev->dev); + return rc; +} +EXPORT_SYMBOL(nfc_se_transaction); + static void nfc_release(struct device *d) { struct nfc_dev *dev = to_nfc_dev(d); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index be387e6219a0..14a2d11581da 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -497,6 +497,53 @@ free_msg: return -EMSGSIZE; } +int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx, + struct nfc_evt_transaction *evt_transaction) +{ + struct nfc_se *se; + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, + NFC_EVENT_SE_TRANSACTION); + if (!hdr) + goto free_msg; + + se = nfc_find_se(dev, se_idx); + if (!se) + goto free_msg; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || + nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) || + nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type) || + nla_put(msg, NFC_ATTR_SE_AID, evt_transaction->aid_len, + evt_transaction->aid) || + nla_put(msg, NFC_ATTR_SE_PARAMS, evt_transaction->params_len, + evt_transaction->params)) + goto nla_put_failure; + + /* evt_transaction is no more used */ + devm_kfree(&dev->dev, evt_transaction); + + genlmsg_end(msg, hdr); + + genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +free_msg: + /* evt_transaction is no more used */ + devm_kfree(&dev->dev, evt_transaction); + nlmsg_free(msg); + return -EMSGSIZE; +} + static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, u32 portid, u32 seq, struct netlink_callback *cb, diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 88d60064890e..a8ce80b47720 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -100,6 +100,8 @@ int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list); int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type); int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx); +int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx, + struct nfc_evt_transaction *evt_transaction); struct nfc_dev *nfc_get_device(unsigned int idx); -- cgit v1.2.3 From a41bb8448ebaebe1d0d9a268d340fad73c247e09 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:17 +0100 Subject: NFC: nci: Add RF NFCEE action notification support The NFCC sends an NCI_OP_RF_NFCEE_ACTION_NTF notification to the host (DH) to let it know that for example an RF transaction with a payment reader is done. For now the notification handler is empty. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/ntf.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'net') diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 6e041ac49e17..5924b812fb6a 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -752,6 +752,12 @@ exit: nci_req_complete(ndev, status); } +static void nci_nfcee_action_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + pr_debug("\n"); +} + void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u16 ntf_opcode = nci_opcode(skb->data); @@ -793,6 +799,11 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) case NCI_OP_NFCEE_DISCOVER_NTF: nci_nfcee_discover_ntf_packet(ndev, skb); break; + + case NCI_OP_RF_NFCEE_ACTION_NTF: + nci_nfcee_action_ntf_packet(ndev, skb); + break; + default: pr_err("unknown ntf opcode 0x%x\n", ntf_opcode); break; -- cgit v1.2.3 From 6095b0f07d9b1abd98484bc33b329e06a684115b Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 1 Feb 2015 22:26:18 +0100 Subject: NFC: nci: Change NCI state machine to LISTEN_ACTIVE When receiving an interface activation notification, if the RF interface is NCI_RF_INTERFACE_NFCEE_DIRECT, we need to ignore the following parameters and change the NCI state machine to NCI_LISTEN_ACTIVE. According to the NCI specification, the parameters should be 0 and shall be ignored. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/ntf.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net') diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 5924b812fb6a..33f5f00ecf4c 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -541,6 +541,13 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, pr_debug("rf_tech_specific_params_len %d\n", ntf.rf_tech_specific_params_len); + /* If this contains a value of 0x00 (NFCEE Direct RF + * Interface) then all following parameters SHALL contain a + * value of 0 and SHALL be ignored. + */ + if (ntf.rf_interface == NCI_RF_INTERFACE_NFCEE_DIRECT) + goto listen; + if (ntf.rf_tech_specific_params_len > 0) { switch (ntf.activation_rf_tech_and_mode) { case NCI_NFC_A_PASSIVE_POLL_MODE: @@ -653,6 +660,7 @@ exit: nci_req_complete(ndev, err); } } else { +listen: /* Listen mode */ atomic_set(&ndev->state, NCI_LISTEN_ACTIVE); if (err == NCI_STATUS_OK && -- cgit v1.2.3 From 12bdf27d46c9d5e490fa164551642e065105db78 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 3 Feb 2015 19:48:04 +0100 Subject: NFC: nci: Add reference to the RF logical connection The NCI_STATIC_RF_CONN_ID logical connection is the most used connection. Keeping it directly accessible in the nci_dev structure will simplify and optimize the access. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 2 +- net/nfc/nci/ntf.c | 6 ++---- net/nfc/nci/rsp.c | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f74d420e2ead..17ff5f83393c 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -803,7 +803,7 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, int rc; struct nci_conn_info *conn_info; - conn_info = nci_get_conn_info_by_conn_id(ndev, NCI_STATIC_RF_CONN_ID); + conn_info = ndev->rf_conn_info; if (!conn_info) return -EPROTO; diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 33f5f00ecf4c..6bbbf6fdacc0 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -625,8 +625,7 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, exit: if (err == NCI_STATUS_OK) { - conn_info = nci_get_conn_info_by_conn_id(ndev, - NCI_STATIC_RF_CONN_ID); + conn_info = ndev->rf_conn_info; if (!conn_info) return; @@ -684,8 +683,7 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); - conn_info = - nci_get_conn_info_by_conn_id(ndev, NCI_STATIC_RF_CONN_ID); + conn_info = ndev->rf_conn_info; if (!conn_info) return; diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 31ccf7d05e82..05268eb473df 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -148,8 +148,7 @@ static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) if (status == NCI_STATUS_OK) { atomic_set(&ndev->state, NCI_DISCOVERY); - conn_info = nci_get_conn_info_by_conn_id(ndev, - NCI_STATIC_RF_CONN_ID); + conn_info = ndev->rf_conn_info; if (!conn_info) { conn_info = devm_kzalloc(&ndev->nfc_dev->dev, sizeof(struct nci_conn_info), @@ -161,6 +160,7 @@ static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) conn_info->conn_id = NCI_STATIC_RF_CONN_ID; INIT_LIST_HEAD(&conn_info->list); list_add(&conn_info->list, &ndev->conn_info_list); + ndev->rf_conn_info = conn_info; } } -- cgit v1.2.3 From b16ae7160a836c4a1e443ea6efca31421e86bae1 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 3 Feb 2015 19:48:05 +0100 Subject: NFC: nci: Support all destinations type when creating a connection The current implementation limits nci_core_conn_create_req() to only manage NCI_DESTINATION_NFCEE. Add new parameters to nci_core_conn_create() to support all destination types described in the NCI specification. Because there are some parameters with variable size dynamic buffer allocation is needed. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 17ff5f83393c..ddfe91e43c88 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -41,6 +41,11 @@ #include #include +struct core_conn_create_data { + int length; + struct nci_core_conn_create_cmd *cmd; +}; + static void nci_cmd_work(struct work_struct *work); static void nci_rx_work(struct work_struct *work); static void nci_tx_work(struct work_struct *work); @@ -509,25 +514,38 @@ EXPORT_SYMBOL(nci_nfcee_mode_set); static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt) { - struct nci_core_conn_create_cmd cmd; - struct core_conn_create_dest_spec_params *params = - (struct core_conn_create_dest_spec_params *)opt; - - cmd.destination_type = NCI_DESTINATION_NFCEE; - cmd.number_destination_params = 1; - memcpy(&cmd.params.type, params, - sizeof(struct core_conn_create_dest_spec_params)); - nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, - sizeof(struct nci_core_conn_create_cmd), &cmd); + struct core_conn_create_data *data = + (struct core_conn_create_data *)opt; + + nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd); } -int nci_core_conn_create(struct nci_dev *ndev, +int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type, + u8 number_destination_params, + size_t params_len, struct core_conn_create_dest_spec_params *params) { - ndev->cur_id = params->value.id; - return nci_request(ndev, nci_core_conn_create_req, - (unsigned long)params, - msecs_to_jiffies(NCI_CMD_TIMEOUT)); + int r; + struct nci_core_conn_create_cmd *cmd; + struct core_conn_create_data data; + + data.length = params_len + sizeof(struct nci_core_conn_create_cmd); + cmd = kzalloc(data.length, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->destination_type = destination_type; + cmd->number_destination_params = number_destination_params; + memcpy(cmd->params, params, params_len); + + data.cmd = cmd; + ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX]; + + r = __nci_request(ndev, nci_core_conn_create_req, + (unsigned long)&data, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); + kfree(cmd); + return r; } EXPORT_SYMBOL(nci_core_conn_create); -- cgit v1.2.3 From 3ba5c8466b320c3fd5d5861b34aa8a31dd0cf6b3 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 3 Feb 2015 19:48:06 +0100 Subject: NFC: nci: Change credits field to credits_cnt For consistency sake change nci_core_conn_create_rsp structure credits field to credits_cnt. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/rsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 05268eb473df..b419fed77ea3 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -245,7 +245,7 @@ static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev, conn_info->conn_id = rsp->conn_id; conn_info->max_pkt_payload_len = rsp->max_ctrl_pkt_payload_len; - atomic_set(&conn_info->credits_cnt, rsp->credits); + atomic_set(&conn_info->credits_cnt, rsp->credits_cnt); } exit: -- cgit v1.2.3 From 15d4a8da0e440faf589a26346c8287e1ed0abe6c Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 3 Feb 2015 19:48:07 +0100 Subject: NFC: nci: Move logical connection structure allocation conn_info is currently allocated only after nfcee_discovery_ntf which is not generic enough for logical connection other than NFCEE. The corresponding conn_info is now created in nci_core_conn_create_rsp(). Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 8 ++++++++ net/nfc/nci/ntf.c | 23 ++--------------------- net/nfc/nci/rsp.c | 29 ++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index ecf253942606..ed54ec533836 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -615,12 +615,20 @@ static int nci_hci_dev_connect_gates(struct nci_dev *ndev, int nci_hci_dev_session_init(struct nci_dev *ndev) { + struct nci_conn_info *conn_info; struct sk_buff *skb; int r; ndev->hci_dev->count_pipes = 0; ndev->hci_dev->expected_pipes = 0; + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + return -EPROTO; + + conn_info->data_exchange_cb = nci_hci_data_received_cb; + conn_info->data_exchange_cb_context = ndev; + nci_hci_reset_pipes(ndev->hci_dev); if (ndev->hci_dev->init_data.gates[0].gate != NCI_HCI_ADMIN_GATE) diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 6bbbf6fdacc0..3218071072ac 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -723,7 +723,6 @@ static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { u8 status = NCI_STATUS_OK; - struct nci_conn_info *conn_info; struct nci_nfcee_discover_ntf *nfcee_ntf = (struct nci_nfcee_discover_ntf *)skb->data; @@ -734,27 +733,9 @@ static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev, * and only one, NFCEE_DISCOVER_NTF with a Protocol type of * “HCI Access”, even if the HCI Network contains multiple NFCEEs. */ - if (!ndev->hci_dev->conn_info) { - conn_info = devm_kzalloc(&ndev->nfc_dev->dev, - sizeof(*conn_info), GFP_KERNEL); - if (!conn_info) { - status = NCI_STATUS_REJECTED; - goto exit; - } - - conn_info->id = nfcee_ntf->nfcee_id; - conn_info->conn_id = NCI_INVALID_CONN_ID; - - conn_info->data_exchange_cb = nci_hci_data_received_cb; - conn_info->data_exchange_cb_context = ndev; + ndev->hci_dev->nfcee_id = nfcee_ntf->nfcee_id; + ndev->cur_id = nfcee_ntf->nfcee_id; - INIT_LIST_HEAD(&conn_info->list); - list_add(&conn_info->list, &ndev->conn_info_list); - - ndev->hci_dev->conn_info = conn_info; - } - -exit: nci_req_complete(ndev, status); } diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index b419fed77ea3..02486bc2ceea 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -233,16 +233,27 @@ static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev, if (status == NCI_STATUS_OK) { rsp = (struct nci_core_conn_create_rsp *)skb->data; - list_for_each_entry(conn_info, &ndev->conn_info_list, list) { - if (conn_info->id == ndev->cur_id) - break; - } - if (!conn_info || conn_info->id != ndev->cur_id) { + conn_info = devm_kzalloc(&ndev->nfc_dev->dev, + sizeof(*conn_info), GFP_KERNEL); + if (!conn_info) { status = NCI_STATUS_REJECTED; goto exit; } + conn_info->id = ndev->cur_id; + conn_info->conn_id = rsp->conn_id; + + /* Note: data_exchange_cb and data_exchange_cb_context need to + * be specify out of nci_core_conn_create_rsp_packet + */ + + INIT_LIST_HEAD(&conn_info->list); + list_add(&conn_info->list, &ndev->conn_info_list); + + if (ndev->cur_id == ndev->hci_dev->nfcee_id) + ndev->hci_dev->conn_info = conn_info; + conn_info->conn_id = rsp->conn_id; conn_info->max_pkt_payload_len = rsp->max_ctrl_pkt_payload_len; atomic_set(&conn_info->credits_cnt, rsp->credits_cnt); @@ -255,9 +266,17 @@ exit: static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { + struct nci_conn_info *conn_info; __u8 status = skb->data[0]; pr_debug("status 0x%x\n", status); + if (status == NCI_STATUS_OK) { + conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_id); + if (conn_info) { + list_del(&conn_info->list); + devm_kfree(&ndev->nfc_dev->dev, conn_info); + } + } nci_req_complete(ndev, status); } -- cgit v1.2.3 From fa00e8fed457841cb24219dbe3cfba7d56de6317 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 3 Feb 2015 19:48:08 +0100 Subject: NFC: nci: Move NFCEE discovery logic NFCEE_DISCOVER_CMD is a specified NCI command used to discover NFCEE IDs. Move nci_nfcee_discover() call to nci_discover_se() in order to guarantee: - NFCEE_DISCOVER_CMD run when the NCI state machine is initialized - NFCEE_DISCOVER_CMD is not run in case there is not discover_se hook defined by a NFC device driver. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index ddfe91e43c88..9575a1892607 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -880,10 +880,16 @@ static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) static int nci_discover_se(struct nfc_dev *nfc_dev) { + int r; struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - if (ndev->ops->discover_se) + if (ndev->ops->discover_se) { + r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE); + if (r != NCI_STATUS_OK) + return -EPROTO; + return ndev->ops->discover_se(ndev); + } return 0; } -- cgit v1.2.3