NFC: st21nfca: Adding P2P support to st21nfca in Initiator & Target mode
authorChristophe Ricard <christophe.ricard@gmail.com>
Tue, 20 May 2014 20:21:59 +0000 (22:21 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Tue, 22 Jul 2014 23:14:41 +0000 (01:14 +0200)
Support for Initiator and Target mode with ISO18092 commands support:
- ATR_REQ/ATR_RES
- PSL_REQ/PSL_RES
- DEP_REQ/DEP_RES

Work based on net/nfc/digital_dep.c.
st21nfca is using:
- Gate reader F for P2P in initiator mode.
- Gate card F for P2P in target mode.

Felica tag and p2p are differentiated with NFCID2.
When starting with 01FE it is acting in p2p mode.

On complete_target_discovered on ST21NFCA_RF_READER_F_GATE
supported_protocols is set to NFC_PROTO_NFC_DEP_MASK
for P2P.

Tested against: Nexus S, Galaxy S2, Galaxy S3, Galaxy S3 Mini,
Nexus 4 & Nexus 5.

Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
drivers/nfc/st21nfca/Makefile
drivers/nfc/st21nfca/st21nfca.c
drivers/nfc/st21nfca/st21nfca.h
drivers/nfc/st21nfca/st21nfca_dep.c [new file with mode: 0644]
drivers/nfc/st21nfca/st21nfca_dep.h [new file with mode: 0644]

index 038ed093a119f24f69d0e41a4f67a7c68c0817e7..db7a38ae05f7a5cee13241b43ae1a675dc79ab1f 100644 (file)
@@ -4,5 +4,5 @@
 
 st21nfca_i2c-objs  = i2c.o
 
-obj-$(CONFIG_NFC_ST21NFCA)     += st21nfca.o
+obj-$(CONFIG_NFC_ST21NFCA)     += st21nfca.o st21nfca_dep.o
 obj-$(CONFIG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o
index 7ff1dcdd16bac42bb86a87abed0ce909962b4667..a902b0551c86a10b0d158c76c568613144f4d254 100644 (file)
@@ -22,6 +22,7 @@
 #include <net/nfc/llc.h>
 
 #include "st21nfca.h"
+#include "st21nfca_dep.h"
 
 #define DRIVER_DESC "HCI NFC driver for ST21NFCA"
 
@@ -73,6 +74,7 @@ static struct nfc_hci_gate st21nfca_gates[] = {
        {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
        {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
        {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
+       {ST21NFCA_RF_CARD_F_GATE, NFC_HCI_INVALID_PIPE},
 };
 
 struct st21nfca_pipe_info {
@@ -300,6 +302,9 @@ static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
                                   u32 im_protocols, u32 tm_protocols)
 {
        int r;
+       u32 pol_req;
+       u8 param[19];
+       struct sk_buff *datarate_skb;
 
        pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
                __func__, im_protocols, tm_protocols);
@@ -332,6 +337,31 @@ static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
                                        ST21NFCA_RF_READER_F_GATE);
                        if (r < 0)
                                return r;
+               } else {
+                       hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
+                                                              &hdev->gb_len);
+
+                       if (hdev->gb == NULL || hdev->gb_len == 0) {
+                               im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+                               tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+                       }
+
+                       param[0] = ST21NFCA_RF_READER_F_DATARATE_106 |
+                           ST21NFCA_RF_READER_F_DATARATE_212 |
+                           ST21NFCA_RF_READER_F_DATARATE_424;
+                       r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE,
+                                             ST21NFCA_RF_READER_F_DATARATE,
+                                             param, 1);
+                       if (r < 0)
+                               return r;
+
+                       pol_req =
+                           be32_to_cpu(ST21NFCA_RF_READER_F_POL_REQ_DEFAULT);
+                       r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE,
+                                             ST21NFCA_RF_READER_F_POL_REQ,
+                                             (u8 *) &pol_req, 4);
+                       if (r < 0)
+                               return r;
                }
 
                if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) {
@@ -354,6 +384,95 @@ static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
                        nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
                                           NFC_HCI_EVT_END_OPERATION, NULL, 0);
        }
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               r = nfc_hci_get_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+                                     ST21NFCA_RF_CARD_F_DATARATE,
+                                     &datarate_skb);
+               if (r < 0)
+                       return r;
+
+               /* Configure the maximum supported datarate to 424Kbps */
+               if (datarate_skb->len > 0 &&
+                   datarate_skb->data[0] !=
+                   ST21NFCA_RF_CARD_F_DATARATE_212_424) {
+                       param[0] = ST21NFCA_RF_CARD_F_DATARATE_212_424;
+                       r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+                                             ST21NFCA_RF_CARD_F_DATARATE,
+                                             param, 1);
+                       if (r < 0)
+                               return r;
+               }
+
+               /*
+                * Configure sens_res
+                *
+                * NFC Forum Digital Spec Table 7:
+                * NFCID1 size: triple (10 bytes)
+                */
+               param[0] = 0x00;
+               param[1] = 0x08;
+               r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+                                     ST21NFCA_RF_CARD_F_SENS_RES, param, 2);
+               if (r < 0)
+                       return r;
+
+               /*
+                * Configure sel_res
+                *
+                * NFC Forum Digistal Spec Table 17:
+                * b3 set to 0b (value b7-b6):
+                * - 10b: Configured for NFC-DEP Protocol
+                */
+               param[0] = 0x40;
+               r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+                                     ST21NFCA_RF_CARD_F_SEL_RES, param, 1);
+               if (r < 0)
+                       return r;
+
+               /* Configure NFCID1 Random uid */
+               r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+                                     ST21NFCA_RF_CARD_F_NFCID1, NULL, 0);
+               if (r < 0)
+                       return r;
+
+               /* Configure NFCID2_LIST */
+               /* System Code */
+               param[0] = 0x00;
+               param[1] = 0x00;
+               /* NFCID2 */
+               param[2] = 0x01;
+               param[3] = 0xfe;
+               param[4] = 'S';
+               param[5] = 'T';
+               param[6] = 'M';
+               param[7] = 'i';
+               param[8] = 'c';
+               param[9] = 'r';
+               /* 8 byte Pad bytes used for polling respone frame */
+
+               /*
+                * Configuration byte:
+                * - bit 0: define the default NFCID2 entry used when the
+                * system code is equal to 'FFFF'
+                * - bit 1: use a random value for lowest 6 bytes of
+                * NFCID2 value
+                * - bit 2: ignore polling request frame if request code
+                * is equal to '01'
+                * - Other bits are RFU
+                */
+               param[18] = 0x01;
+               r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+                                     ST21NFCA_RF_CARD_F_NFCID2_LIST, param,
+                                     19);
+               if (r < 0)
+                       return r;
+
+               param[0] = 0x02;
+               r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
+                                     ST21NFCA_RF_CARD_F_MODE, param, 1);
+       }
+
        return r;
 }
 
@@ -458,6 +577,26 @@ exit:
        return r;
 }
 
+static int st21nfca_hci_dep_link_up(struct nfc_hci_dev *hdev,
+                                   struct nfc_target *target, u8 comm_mode,
+                                   u8 *gb, size_t gb_len)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       info->dep_info.idx = target->idx;
+       return st21nfca_im_send_atr_req(hdev, gb, gb_len);
+}
+
+static int st21nfca_hci_dep_link_down(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       info->state = ST21NFCA_ST_READY;
+
+       return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                               ST21NFCA_DM_DISCONNECT, NULL, 0, NULL);
+}
+
 static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
                                         struct nfc_target *target)
 {
@@ -512,6 +651,69 @@ static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
        return 0;
 }
 
+static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
+                                               u8 gate,
+                                               struct nfc_target *target)
+{
+       int r;
+       struct sk_buff *nfcid2_skb = NULL, *nfcid1_skb;
+
+       if (gate == ST21NFCA_RF_READER_F_GATE) {
+               r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
+                               ST21NFCA_RF_READER_F_NFCID2, &nfcid2_skb);
+               if (r < 0)
+                       goto exit;
+
+               if (nfcid2_skb->len > NFC_SENSF_RES_MAXSIZE) {
+                       r = -EPROTO;
+                       goto exit;
+               }
+
+               /*
+                * - After the recepton of polling response for type F frame
+                * at 212 or 424 Kbit/s, NFCID2 registry parameters will be
+                * updated.
+                * - After the reception of SEL_RES with NFCIP-1 compliant bit
+                * set for type A frame NFCID1 will be updated
+                */
+               if (nfcid2_skb->len > 0) {
+                       /* P2P in type F */
+                       memcpy(target->sensf_res, nfcid2_skb->data,
+                               nfcid2_skb->len);
+                       target->sensf_res_len = nfcid2_skb->len;
+                       /* NFC Forum Digital Protocol Table 44 */
+                       if (target->sensf_res[0] == 0x01 &&
+                           target->sensf_res[1] == 0xfe)
+                               target->supported_protocols =
+                                                       NFC_PROTO_NFC_DEP_MASK;
+                       else
+                               target->supported_protocols =
+                                                       NFC_PROTO_FELICA_MASK;
+               } else {
+                       /* P2P in type A */
+                       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
+                                       ST21NFCA_RF_READER_F_NFCID1,
+                                       &nfcid1_skb);
+                       if (r < 0)
+                               goto exit;
+
+                       if (nfcid1_skb->len > NFC_NFCID1_MAXSIZE) {
+                               r = -EPROTO;
+                               goto exit;
+                       }
+                       memcpy(target->sensf_res, nfcid1_skb->data,
+                               nfcid1_skb->len);
+                       target->sensf_res_len = nfcid1_skb->len;
+                       target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+               }
+               target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE;
+       }
+       r = 1;
+exit:
+       kfree_skb(nfcid2_skb);
+       return r;
+}
+
 #define ST21NFCA_CB_TYPE_READER_ISO15693 1
 static void st21nfca_hci_data_exchange_cb(void *context, struct sk_buff *skb,
                                          int err)
@@ -548,6 +750,9 @@ static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev,
 
        switch (target->hci_reader_gate) {
        case ST21NFCA_RF_READER_F_GATE:
+               if (target->supported_protocols == NFC_PROTO_NFC_DEP_MASK)
+                       return st21nfca_im_send_dep_req(hdev, skb);
+
                *skb_push(skb, 1) = 0x1a;
                return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
                                              ST21NFCA_WR_XCHG_DATA, skb->data,
@@ -576,6 +781,11 @@ static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev,
        }
 }
 
+static int st21nfca_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       return st21nfca_tm_send_dep_res(hdev, skb);
+}
+
 static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
                                       struct nfc_target *target)
 {
@@ -601,6 +811,50 @@ static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
        }
 }
 
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
+                                      u8 event, struct sk_buff *skb)
+{
+       int r;
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       pr_debug("hci event: %d\n", event);
+
+       switch (event) {
+       case ST21NFCA_EVT_CARD_ACTIVATED:
+               if (gate == ST21NFCA_RF_CARD_F_GATE)
+                       info->dep_info.curr_nfc_dep_pni = 0;
+               break;
+       case ST21NFCA_EVT_CARD_DEACTIVATED:
+               break;
+       case ST21NFCA_EVT_FIELD_ON:
+               break;
+       case ST21NFCA_EVT_FIELD_OFF:
+               break;
+       case ST21NFCA_EVT_SEND_DATA:
+               if (gate == ST21NFCA_RF_CARD_F_GATE) {
+                       r = st21nfca_tm_event_send_data(hdev, skb, gate);
+                       if (r < 0)
+                               goto exit;
+                       return 0;
+               } else {
+                       info->dep_info.curr_nfc_dep_pni = 0;
+                       return 1;
+               }
+               break;
+       default:
+               return 1;
+       }
+       kfree_skb(skb);
+       return 0;
+exit:
+       return r;
+}
+
 static struct nfc_hci_ops st21nfca_hci_ops = {
        .open = st21nfca_hci_open,
        .close = st21nfca_hci_close,
@@ -609,9 +863,14 @@ static struct nfc_hci_ops st21nfca_hci_ops = {
        .xmit = st21nfca_hci_xmit,
        .start_poll = st21nfca_hci_start_poll,
        .stop_poll = st21nfca_hci_stop_poll,
+       .dep_link_up = st21nfca_hci_dep_link_up,
+       .dep_link_down = st21nfca_hci_dep_link_down,
        .target_from_gate = st21nfca_hci_target_from_gate,
+       .complete_target_discovered = st21nfca_hci_complete_target_discovered,
        .im_transceive = st21nfca_hci_im_transceive,
+       .tm_send = st21nfca_hci_tm_send,
        .check_presence = st21nfca_hci_check_presence,
+       .event_received = st21nfca_hci_event_received,
 };
 
 int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
@@ -656,7 +915,8 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
            NFC_PROTO_FELICA_MASK |
            NFC_PROTO_ISO14443_MASK |
            NFC_PROTO_ISO14443_B_MASK |
-           NFC_PROTO_ISO15693_MASK;
+           NFC_PROTO_ISO15693_MASK |
+           NFC_PROTO_NFC_DEP_MASK;
 
        set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks);
 
@@ -679,6 +939,7 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
                goto err_regdev;
 
        *hdev = info->hdev;
+       st21nfca_dep_init(info->hdev);
 
        return 0;
 
@@ -696,6 +957,7 @@ void st21nfca_hci_remove(struct nfc_hci_dev *hdev)
 {
        struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
 
+       st21nfca_dep_deinit(hdev);
        nfc_hci_unregister_device(hdev);
        nfc_hci_free_device(hdev);
        kfree(info);
index 334cd90bcc8c3ec5e29c624f8d7b0b1ac61de728..96fe5a62dc0dced5dd7ed41ee2f33496e1a1d61e 100644 (file)
@@ -19,6 +19,8 @@
 
 #include <net/nfc/hci.h>
 
+#include "st21nfca_dep.h"
+
 #define HCI_MODE 0
 
 /* framing in HCI mode */
@@ -73,7 +75,8 @@ struct st21nfca_hci_info {
        data_exchange_cb_t async_cb;
        void *async_cb_context;
 
-} __packed;
+       struct st21nfca_dep_info dep_info;
+};
 
 /* Reader RF commands */
 #define ST21NFCA_WR_XCHG_DATA            0x10
@@ -83,5 +86,26 @@ struct st21nfca_hci_info {
 #define ST21NFCA_RF_READER_F_DATARATE_106 0x01
 #define ST21NFCA_RF_READER_F_DATARATE_212 0x02
 #define ST21NFCA_RF_READER_F_DATARATE_424 0x04
+#define ST21NFCA_RF_READER_F_POL_REQ    0x02
+#define ST21NFCA_RF_READER_F_POL_REQ_DEFAULT    0xffff0000
+#define ST21NFCA_RF_READER_F_NFCID2 0x03
+#define ST21NFCA_RF_READER_F_NFCID1 0x04
+#define ST21NFCA_RF_READER_F_SENS_RES 0x05
+
+#define ST21NFCA_RF_CARD_F_GATE 0x24
+#define ST21NFCA_RF_CARD_F_MODE 0x01
+#define ST21NFCA_RF_CARD_F_NFCID2_LIST 0x04
+#define ST21NFCA_RF_CARD_F_NFCID1 0x05
+#define ST21NFCA_RF_CARD_F_SENS_RES 0x06
+#define ST21NFCA_RF_CARD_F_SEL_RES 0x07
+#define ST21NFCA_RF_CARD_F_DATARATE 0x08
+#define ST21NFCA_RF_CARD_F_DATARATE_106 0x00
+#define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01
+
+#define ST21NFCA_EVT_SEND_DATA 0x10
+#define ST21NFCA_EVT_FIELD_ON 0x11
+#define ST21NFCA_EVT_CARD_DEACTIVATED 0x12
+#define ST21NFCA_EVT_CARD_ACTIVATED 0x13
+#define ST21NFCA_EVT_FIELD_OFF 0x14
 
 #endif /* __LOCAL_ST21NFCA_H_ */
diff --git a/drivers/nfc/st21nfca/st21nfca_dep.c b/drivers/nfc/st21nfca/st21nfca_dep.c
new file mode 100644 (file)
index 0000000..b2d9957
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <net/nfc/hci.h>
+
+#include "st21nfca.h"
+#include "st21nfca_dep.h"
+
+#define ST21NFCA_NFCIP1_INITIATOR 0x00
+#define ST21NFCA_NFCIP1_REQ 0xd4
+#define ST21NFCA_NFCIP1_RES 0xd5
+#define ST21NFCA_NFCIP1_ATR_REQ 0x00
+#define ST21NFCA_NFCIP1_ATR_RES 0x01
+#define ST21NFCA_NFCIP1_PSL_REQ 0x04
+#define ST21NFCA_NFCIP1_PSL_RES 0x05
+#define ST21NFCA_NFCIP1_DEP_REQ 0x06
+#define ST21NFCA_NFCIP1_DEP_RES 0x07
+
+#define ST21NFCA_NFC_DEP_PFB_PNI(pfb)     ((pfb) & 0x03)
+#define ST21NFCA_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
+#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
+                               ((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT)
+#define ST21NFCA_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define ST21NFCA_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
+#define ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+
+#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
+                               ((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT)
+
+#define ST21NFCA_NFC_DEP_PFB_I_PDU          0x00
+#define ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU   0x40
+#define ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU 0x80
+
+#define ST21NFCA_ATR_REQ_MIN_SIZE 17
+#define ST21NFCA_ATR_REQ_MAX_SIZE 65
+#define ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B 0x30
+#define ST21NFCA_GB_BIT  0x02
+
+#define ST21NFCA_EVT_CARD_F_BITRATE 0x16
+#define ST21NFCA_EVT_READER_F_BITRATE 0x13
+#define        ST21NFCA_PSL_REQ_SEND_SPEED(brs) (brs & 0x38)
+#define ST21NFCA_PSL_REQ_RECV_SPEED(brs) (brs & 0x07)
+#define ST21NFCA_PP2LRI(pp) ((pp & 0x30) >> 4)
+#define ST21NFCA_CARD_BITRATE_212 0x01
+#define ST21NFCA_CARD_BITRATE_424 0x02
+
+#define ST21NFCA_DEFAULT_TIMEOUT 0x0a
+
+
+#define PROTOCOL_ERR(req) pr_err("%d: ST21NFCA Protocol error: %s\n", \
+                                __LINE__, req)
+
+struct st21nfca_atr_req {
+       u8 length;
+       u8 cmd0;
+       u8 cmd1;
+       u8 nfcid3[NFC_NFCID3_MAXSIZE];
+       u8 did;
+       u8 bsi;
+       u8 bri;
+       u8 ppi;
+       u8 gbi[0];
+} __packed;
+
+struct st21nfca_atr_res {
+       u8 length;
+       u8 cmd0;
+       u8 cmd1;
+       u8 nfcid3[NFC_NFCID3_MAXSIZE];
+       u8 did;
+       u8 bsi;
+       u8 bri;
+       u8 to;
+       u8 ppi;
+       u8 gbi[0];
+} __packed;
+
+struct st21nfca_psl_req {
+       u8 length;
+       u8 cmd0;
+       u8 cmd1;
+       u8 did;
+       u8 brs;
+       u8 fsl;
+} __packed;
+
+struct st21nfca_psl_res {
+       u8 length;
+       u8 cmd0;
+       u8 cmd1;
+       u8 did;
+} __packed;
+
+struct st21nfca_dep_req_res {
+       u8 length;
+       u8 cmd0;
+       u8 cmd1;
+       u8 pfb;
+       u8 did;
+       u8 nad;
+} __packed;
+
+static void st21nfca_tx_work(struct work_struct *work)
+{
+       struct st21nfca_hci_info *info = container_of(work,
+                                               struct st21nfca_hci_info,
+                                               dep_info.tx_work);
+
+       struct nfc_dev *dev;
+       struct sk_buff *skb;
+       if (info) {
+               dev = info->hdev->ndev;
+               skb = info->dep_info.tx_pending;
+
+               device_lock(&dev->dev);
+
+               nfc_hci_send_cmd_async(info->hdev, ST21NFCA_RF_READER_F_GATE,
+                                                       ST21NFCA_WR_XCHG_DATA,
+                                                       skb->data, skb->len,
+                                                       info->async_cb, info);
+               device_unlock(&dev->dev);
+               kfree_skb(skb);
+       }
+}
+
+static void st21nfca_im_send_pdu(struct st21nfca_hci_info *info,
+                                               struct sk_buff *skb)
+{
+       info->dep_info.tx_pending = skb;
+       schedule_work(&info->dep_info.tx_work);
+}
+
+static int st21nfca_tm_send_atr_res(struct nfc_hci_dev *hdev,
+                                   struct st21nfca_atr_req *atr_req)
+{
+       struct st21nfca_atr_res *atr_res;
+       struct sk_buff *skb;
+       size_t gb_len;
+       int r;
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       gb_len = atr_req->length - sizeof(struct st21nfca_atr_req);
+       skb = alloc_skb(atr_req->length + 1, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct st21nfca_atr_res));
+
+       atr_res = (struct st21nfca_atr_res *)skb->data;
+       memset(atr_res, 0, sizeof(struct st21nfca_atr_res));
+
+       atr_res->length = atr_req->length + 1;
+       atr_res->cmd0 = ST21NFCA_NFCIP1_RES;
+       atr_res->cmd1 = ST21NFCA_NFCIP1_ATR_RES;
+
+       memcpy(atr_res->nfcid3, atr_req->nfcid3, 6);
+       atr_res->bsi = 0x00;
+       atr_res->bri = 0x00;
+       atr_res->to = ST21NFCA_DEFAULT_TIMEOUT;
+       atr_res->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B;
+
+       if (gb_len) {
+               skb_put(skb, gb_len);
+
+               atr_res->ppi |= ST21NFCA_GB_BIT;
+               memcpy(atr_res->gbi, atr_req->gbi, gb_len);
+               r = nfc_set_remote_general_bytes(hdev->ndev, atr_res->gbi,
+                                                 gb_len);
+               if (r < 0)
+                       return r;
+       }
+
+       info->dep_info.curr_nfc_dep_pni = 0;
+
+       return nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+                               ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
+}
+
+static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev,
+                                   struct sk_buff *skb)
+{
+       struct st21nfca_atr_req *atr_req;
+       size_t gb_len;
+       int r;
+
+       skb_trim(skb, skb->len - 1);
+       if (IS_ERR(skb)) {
+               r = PTR_ERR(skb);
+               goto exit;
+       }
+
+       if (!skb->len) {
+               r = -EIO;
+               goto exit;
+       }
+
+       if (skb->len < ST21NFCA_ATR_REQ_MIN_SIZE) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       atr_req = (struct st21nfca_atr_req *)skb->data;
+
+       r = st21nfca_tm_send_atr_res(hdev, atr_req);
+       if (r)
+               goto exit;
+
+       gb_len = skb->len - sizeof(struct st21nfca_atr_req);
+
+       r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+                             NFC_COMM_PASSIVE, atr_req->gbi, gb_len);
+       if (r)
+               goto exit;
+
+       r = 0;
+
+exit:
+       return r;
+}
+
+static int st21nfca_tm_send_psl_res(struct nfc_hci_dev *hdev,
+                                   struct st21nfca_psl_req *psl_req)
+{
+       struct st21nfca_psl_res *psl_res;
+       struct sk_buff *skb;
+       u8 bitrate[2] = {0, 0};
+
+       int r;
+
+       skb = alloc_skb(sizeof(struct st21nfca_psl_res), GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+       skb_put(skb, sizeof(struct st21nfca_psl_res));
+
+       psl_res = (struct st21nfca_psl_res *)skb->data;
+
+       psl_res->length = sizeof(struct st21nfca_psl_res);
+       psl_res->cmd0 = ST21NFCA_NFCIP1_RES;
+       psl_res->cmd1 = ST21NFCA_NFCIP1_PSL_RES;
+       psl_res->did = psl_req->did;
+
+       r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+                               ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
+
+       /*
+        * ST21NFCA only support P2P passive.
+        * PSL_REQ BRS value != 0 has only a meaning to
+        * change technology to type F.
+        * We change to BITRATE 424Kbits.
+        * In other case switch to BITRATE 106Kbits.
+        */
+       if (ST21NFCA_PSL_REQ_SEND_SPEED(psl_req->brs) &&
+           ST21NFCA_PSL_REQ_RECV_SPEED(psl_req->brs)) {
+               bitrate[0] = ST21NFCA_CARD_BITRATE_424;
+               bitrate[1] = ST21NFCA_CARD_BITRATE_424;
+       }
+
+       /* Send an event to change bitrate change event to card f */
+       return nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+                       ST21NFCA_EVT_CARD_F_BITRATE, bitrate, 2);
+}
+
+static int st21nfca_tm_recv_psl_req(struct nfc_hci_dev *hdev,
+                                   struct sk_buff *skb)
+{
+       struct st21nfca_psl_req *psl_req;
+       int r;
+
+       skb_trim(skb, skb->len - 1);
+       if (IS_ERR(skb)) {
+               r = PTR_ERR(skb);
+               skb = NULL;
+               goto exit;
+       }
+
+       if (!skb->len) {
+               r = -EIO;
+               goto exit;
+       }
+
+       psl_req = (struct st21nfca_psl_req *)skb->data;
+
+       if (skb->len < sizeof(struct st21nfca_psl_req)) {
+               r = -EIO;
+               goto exit;
+       }
+
+       r = st21nfca_tm_send_psl_res(hdev, psl_req);
+exit:
+       return r;
+}
+
+int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       int r;
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       *skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni;
+       *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_RES;
+       *skb_push(skb, 1) = ST21NFCA_NFCIP1_RES;
+       *skb_push(skb, 1) = skb->len;
+
+       r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
+                               ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
+       kfree_skb(skb);
+
+       return r;
+}
+EXPORT_SYMBOL(st21nfca_tm_send_dep_res);
+
+static int st21nfca_tm_recv_dep_req(struct nfc_hci_dev *hdev,
+                                   struct sk_buff *skb)
+{
+       struct st21nfca_dep_req_res *dep_req;
+       u8 size;
+       int r;
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       skb_trim(skb, skb->len - 1);
+       if (IS_ERR(skb)) {
+               r = PTR_ERR(skb);
+               skb = NULL;
+               goto exit;
+       }
+
+       size = 4;
+
+       dep_req = (struct st21nfca_dep_req_res *)skb->data;
+       if (skb->len < size) {
+               r = -EIO;
+               goto exit;
+       }
+
+       if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_req->pfb))
+               size++;
+       if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_req->pfb))
+               size++;
+
+       if (skb->len < size) {
+               r = -EIO;
+               goto exit;
+       }
+
+       /* Receiving DEP_REQ - Decoding */
+       switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
+       case ST21NFCA_NFC_DEP_PFB_I_PDU:
+               info->dep_info.curr_nfc_dep_pni =
+                               ST21NFCA_NFC_DEP_PFB_PNI(dep_req->pfb);
+               break;
+       case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU:
+               pr_err("Received a ACK/NACK PDU\n");
+               break;
+       case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU:
+               pr_err("Received a SUPERVISOR PDU\n");
+               break;
+       }
+
+       if (IS_ERR(skb)) {
+               r = PTR_ERR(skb);
+               skb = NULL;
+               goto exit;
+       }
+
+       skb_pull(skb, size);
+
+       return nfc_tm_data_received(hdev->ndev, skb);
+exit:
+       return r;
+}
+
+int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
+                               u8 gate)
+{
+       u8 cmd0, cmd1;
+       int r;
+
+       cmd0 = skb->data[1];
+       switch (cmd0) {
+       case ST21NFCA_NFCIP1_REQ:
+               cmd1 = skb->data[2];
+               switch (cmd1) {
+               case ST21NFCA_NFCIP1_ATR_REQ:
+                       r = st21nfca_tm_recv_atr_req(hdev, skb);
+                       break;
+               case ST21NFCA_NFCIP1_PSL_REQ:
+                       r = st21nfca_tm_recv_psl_req(hdev, skb);
+                       break;
+               case ST21NFCA_NFCIP1_DEP_REQ:
+                       r = st21nfca_tm_recv_dep_req(hdev, skb);
+                       break;
+               default:
+                       return 1;
+               }
+       default:
+               return 1;
+       }
+       return r;
+}
+EXPORT_SYMBOL(st21nfca_tm_event_send_data);
+
+static void st21nfca_im_send_psl_req(struct nfc_hci_dev *hdev, u8 did, u8 bsi,
+                                    u8 bri, u8 lri)
+{
+       struct sk_buff *skb;
+       struct st21nfca_psl_req *psl_req;
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       skb =
+           alloc_skb(sizeof(struct st21nfca_psl_req) + 1, GFP_KERNEL);
+       if (!skb)
+               return;
+       skb_reserve(skb, 1);
+
+       skb_put(skb, sizeof(struct st21nfca_psl_req));
+       psl_req = (struct st21nfca_psl_req *) skb->data;
+
+       psl_req->length = sizeof(struct st21nfca_psl_req);
+       psl_req->cmd0 = ST21NFCA_NFCIP1_REQ;
+       psl_req->cmd1 = ST21NFCA_NFCIP1_PSL_REQ;
+       psl_req->did = did;
+       psl_req->brs = (0x30 & bsi << 4) | (bri & 0x03);
+       psl_req->fsl = lri;
+
+       *skb_push(skb, 1) = info->dep_info.to | 0x10;
+
+       st21nfca_im_send_pdu(info, skb);
+
+       kfree_skb(skb);
+}
+
+#define ST21NFCA_CB_TYPE_READER_F 1
+static void st21nfca_im_recv_atr_res_cb(void *context, struct sk_buff *skb,
+                                       int err)
+{
+       struct st21nfca_hci_info *info = context;
+       struct st21nfca_atr_res *atr_res;
+       int r;
+
+       if (err != 0)
+               return;
+
+       if (IS_ERR(skb))
+               return;
+
+       switch (info->async_cb_type) {
+       case ST21NFCA_CB_TYPE_READER_F:
+               skb_trim(skb, skb->len - 1);
+               atr_res = (struct st21nfca_atr_res *)skb->data;
+               r = nfc_set_remote_general_bytes(info->hdev->ndev,
+                               atr_res->gbi,
+                               skb->len - sizeof(struct st21nfca_atr_res));
+               if (r < 0)
+                       return;
+
+               if (atr_res->to >= 0x0e)
+                       info->dep_info.to = 0x0e;
+               else
+                       info->dep_info.to = atr_res->to + 1;
+
+               info->dep_info.to |= 0x10;
+
+               r = nfc_dep_link_is_up(info->hdev->ndev, info->dep_info.idx,
+                                       NFC_COMM_PASSIVE, NFC_RF_INITIATOR);
+               if (r < 0)
+                       return;
+
+               info->dep_info.curr_nfc_dep_pni = 0;
+               if (ST21NFCA_PP2LRI(atr_res->ppi) != info->dep_info.lri)
+                       st21nfca_im_send_psl_req(info->hdev, atr_res->did,
+                                               atr_res->bsi, atr_res->bri,
+                                               ST21NFCA_PP2LRI(atr_res->ppi));
+               break;
+       default:
+               if (err == 0)
+                       kfree_skb(skb);
+               break;
+       }
+}
+
+int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len)
+{
+       struct sk_buff *skb;
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+       struct st21nfca_atr_req *atr_req;
+       struct nfc_target *target;
+       uint size;
+
+       info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT;
+       size = ST21NFCA_ATR_REQ_MIN_SIZE + gb_len;
+       if (size > ST21NFCA_ATR_REQ_MAX_SIZE) {
+               PROTOCOL_ERR("14.6.1.1");
+               return -EINVAL;
+       }
+
+       skb =
+           alloc_skb(sizeof(struct st21nfca_atr_req) + gb_len + 1, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, 1);
+
+       skb_put(skb, sizeof(struct st21nfca_atr_req));
+
+       atr_req = (struct st21nfca_atr_req *)skb->data;
+       memset(atr_req, 0, sizeof(struct st21nfca_atr_req));
+
+       atr_req->cmd0 = ST21NFCA_NFCIP1_REQ;
+       atr_req->cmd1 = ST21NFCA_NFCIP1_ATR_REQ;
+       memset(atr_req->nfcid3, 0, NFC_NFCID3_MAXSIZE);
+       target = hdev->ndev->targets;
+
+       if (target->sensf_res)
+               memcpy(atr_req->nfcid3, target->sensf_res,
+                               target->sensf_res_len);
+       else
+               get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE);
+
+       atr_req->did = 0x0;
+
+       atr_req->bsi = 0x00;
+       atr_req->bri = 0x00;
+       atr_req->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B;
+       if (gb_len) {
+               atr_req->ppi |= ST21NFCA_GB_BIT;
+               memcpy(skb_put(skb, gb_len), gb, gb_len);
+       }
+       atr_req->length = sizeof(struct st21nfca_atr_req) + hdev->gb_len;
+
+       *skb_push(skb, 1) = info->dep_info.to | 0x10; /* timeout */
+
+       info->async_cb_type = ST21NFCA_CB_TYPE_READER_F;
+       info->async_cb_context = info;
+       info->async_cb = st21nfca_im_recv_atr_res_cb;
+       info->dep_info.bri = atr_req->bri;
+       info->dep_info.bsi = atr_req->bsi;
+       info->dep_info.lri = ST21NFCA_PP2LRI(atr_req->ppi);
+
+       return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE,
+                               ST21NFCA_WR_XCHG_DATA, skb->data,
+                               skb->len, info->async_cb, info);
+}
+EXPORT_SYMBOL(st21nfca_im_send_atr_req);
+
+static void st21nfca_im_recv_dep_res_cb(void *context, struct sk_buff *skb,
+                                       int err)
+{
+       struct st21nfca_hci_info *info = context;
+       struct st21nfca_dep_req_res *dep_res;
+
+       int size;
+
+       if (err != 0)
+               return;
+
+       if (IS_ERR(skb))
+               return;
+
+       switch (info->async_cb_type) {
+       case ST21NFCA_CB_TYPE_READER_F:
+               dep_res = (struct st21nfca_dep_req_res *)skb->data;
+
+               size = 3;
+               if (skb->len < size)
+                       goto exit;
+
+               if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_res->pfb))
+                       size++;
+               if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_res->pfb))
+                       size++;
+
+               if (skb->len < size)
+                       goto exit;
+
+               skb_trim(skb, skb->len - 1);
+
+               /* Receiving DEP_REQ - Decoding */
+               switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_res->pfb)) {
+               case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU:
+                       pr_err("Received a ACK/NACK PDU\n");
+               case ST21NFCA_NFC_DEP_PFB_I_PDU:
+                       info->dep_info.curr_nfc_dep_pni =
+                           ST21NFCA_NFC_DEP_PFB_PNI(dep_res->pfb + 1);
+                       size++;
+                       skb_pull(skb, size);
+                       nfc_tm_data_received(info->hdev->ndev, skb);
+                       break;
+               case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU:
+                       pr_err("Received a SUPERVISOR PDU\n");
+                       skb_pull(skb, size);
+                       *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ;
+                       *skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ;
+                       *skb_push(skb, 1) = skb->len;
+                       *skb_push(skb, 1) = info->dep_info.to | 0x10;
+
+                       st21nfca_im_send_pdu(info, skb);
+                       break;
+               }
+
+               return;
+       default:
+               break;
+       }
+
+exit:
+       if (err == 0)
+               kfree_skb(skb);
+}
+
+int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       info->async_cb_type = ST21NFCA_CB_TYPE_READER_F;
+       info->async_cb_context = info;
+       info->async_cb = st21nfca_im_recv_dep_res_cb;
+
+       *skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni;
+       *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ;
+       *skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ;
+       *skb_push(skb, 1) = skb->len;
+
+       *skb_push(skb, 1) = info->dep_info.to | 0x10;
+
+       return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE,
+                                     ST21NFCA_WR_XCHG_DATA,
+                                     skb->data, skb->len,
+                                     info->async_cb, info);
+}
+EXPORT_SYMBOL(st21nfca_im_send_dep_req);
+
+void st21nfca_dep_init(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       INIT_WORK(&info->dep_info.tx_work, st21nfca_tx_work);
+       info->dep_info.curr_nfc_dep_pni = 0;
+       info->dep_info.idx = 0;
+       info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT;
+}
+EXPORT_SYMBOL(st21nfca_dep_init);
+
+void st21nfca_dep_deinit(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       cancel_work_sync(&info->dep_info.tx_work);
+}
+EXPORT_SYMBOL(st21nfca_dep_deinit);
diff --git a/drivers/nfc/st21nfca/st21nfca_dep.h b/drivers/nfc/st21nfca/st21nfca_dep.h
new file mode 100644 (file)
index 0000000..ca213de
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ST21NFCA_DEP_H
+#define __ST21NFCA_DEP_H
+
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+
+struct st21nfca_dep_info {
+       struct sk_buff *tx_pending;
+       struct work_struct tx_work;
+       u8 curr_nfc_dep_pni;
+       u32 idx;
+       u8 to;
+       u8 did;
+       u8 bsi;
+       u8 bri;
+       u8 lri;
+} __packed;
+
+int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
+                               u8 gate);
+int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+
+int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
+int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+void st21nfca_dep_init(struct nfc_hci_dev *hdev);
+void st21nfca_dep_deinit(struct nfc_hci_dev *hdev);
+#endif /* __ST21NFCA_DEP_H */