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 <christophe-h.ricard@st.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
#define NCI_MAX_SUPPORTED_RF_INTERFACES 4
#define NCI_MAX_DISCOVERED_TARGETS 10
+#define NCI_MAX_NUM_NFCEE 255
+#define NCI_MAX_CONN_ID 7
+
+struct nci_conn_info {
+ struct list_head list;
+ __u8 id; /* can be an RF Discovery ID or an NFCEE ID */
+ __u8 conn_id;
+ __u8 max_pkt_payload_len;
+
+ atomic_t credits_cnt;
+ __u8 initial_num_credits;
+
+ data_exchange_cb_t data_exchange_cb;
+ void *data_exchange_cb_context;
+
+ struct sk_buff *rx_skb;
+};
/* NCI Core structures */
struct nci_dev {
unsigned long flags;
atomic_t cmd_cnt;
- atomic_t credits_cnt;
+ __u8 cur_conn_id;
+
+ struct list_head conn_info_list;
struct timer_list cmd_timer;
struct timer_list data_timer;
__u8 manufact_id;
__u32 manufact_specific_info;
- /* received during NCI_OP_RF_INTF_ACTIVATED_NTF */
- __u8 max_data_pkt_payload_size;
- __u8 initial_num_credits;
+ /* Save RF Discovery ID or NFCEE ID under conn_create */
+ __u8 cur_id;
/* stored during nci_data_exchange */
- data_exchange_cb_t data_exchange_cb;
- void *data_exchange_cb_context;
struct sk_buff *rx_data_reassembly;
/* stored during intf_activated_ntf */
int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload);
int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
- int err);
+ __u8 conn_id, int err);
void nci_clear_target_list(struct nci_dev *ndev);
/* ----- NCI requests ----- */
#define NCI_REQ_CANCELED 2
void nci_req_complete(struct nci_dev *ndev, int result);
+struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev,
+ int conn_id);
/* ----- NCI status code ----- */
int nci_to_errno(__u8 code);
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)
{
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);
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)
(unsigned long) ndev);
mutex_init(&ndev->req_lock);
+ INIT_LIST_HEAD(&ndev->conn_info_list);
rc = nfc_register_device(ndev->nfc_dev);
if (rc)
*/
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);
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),
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);
}
/* 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);
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) {
kfree_skb(skb);
}
+exit:
clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
}
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;
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),
/* 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);
}
}
+ ndev->cur_conn_id = conn_id;
queue_work(ndev->tx_wq, &ndev->tx_work);
goto 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;
}
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);
}
}
{
__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);
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);
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));
}
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);
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 */
/* 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,
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;
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) {
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);
/* 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:
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);
}