summaryrefslogtreecommitdiffstats
path: root/net/nfc/digital_core.c
diff options
context:
space:
mode:
authorThierry Escande <thierry.escande@linux.intel.com>2013-09-19 17:55:30 +0200
committerSamuel Ortiz <sameo@linux.intel.com>2013-09-25 02:02:28 +0200
commit1c7a4c24fbfd99442cc6e14dc80fcb00f118e8b8 (patch)
treed8115482922f1d4b5c7da4a6aa76faca7f77c032 /net/nfc/digital_core.c
parent7d0911c02fa2a448a28d7844d2a0c439ff8397b1 (diff)
downloadlinux-1c7a4c24fbfd99442cc6e14dc80fcb00f118e8b8.tar.bz2
NFC Digital: Add target NFC-DEP support
This adds support for NFC-DEP target mode for NFC-A and NFC-F technologies. If the driver provides it, the stack uses an automatic mode for technology detection and automatic anti-collision. Otherwise the stack tries to use non-automatic synchronization and listens for SENS_REQ and SENSF_REQ commands. The detection, activation, and data exchange procedures work exactly the same way as in initiator mode, as described in the previous commits, except that the digital stack waits for commands and sends responses back to the peer device. Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'net/nfc/digital_core.c')
-rw-r--r--net/nfc/digital_core.c81
1 files changed, 78 insertions, 3 deletions
diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
index dccfcccf6998..66151fc5d990 100644
--- a/net/nfc/digital_core.c
+++ b/net/nfc/digital_core.c
@@ -32,6 +32,7 @@ struct digital_cmd {
u16 timeout;
struct sk_buff *req;
struct sk_buff *resp;
+ struct digital_tg_mdaa_params *mdaa_params;
nfc_digital_cmd_complete_t cmd_cb;
void *cb_context;
@@ -131,6 +132,7 @@ static void digital_wq_cmd_complete(struct work_struct *work)
cmd->cmd_cb(ddev, cmd->cb_context, cmd->resp);
+ kfree(cmd->mdaa_params);
kfree(cmd);
schedule_work(&ddev->cmd_work);
@@ -150,6 +152,7 @@ static void digital_wq_cmd(struct work_struct *work)
{
int rc;
struct digital_cmd *cmd;
+ struct digital_tg_mdaa_params *params;
struct nfc_digital_dev *ddev = container_of(work,
struct nfc_digital_dev,
cmd_work);
@@ -174,6 +177,24 @@ static void digital_wq_cmd(struct work_struct *work)
rc = ddev->ops->in_send_cmd(ddev, cmd->req, cmd->timeout,
digital_send_cmd_complete, cmd);
break;
+
+ case DIGITAL_CMD_TG_SEND:
+ rc = ddev->ops->tg_send_cmd(ddev, cmd->req, cmd->timeout,
+ digital_send_cmd_complete, cmd);
+ break;
+
+ case DIGITAL_CMD_TG_LISTEN:
+ rc = ddev->ops->tg_listen(ddev, cmd->timeout,
+ digital_send_cmd_complete, cmd);
+ break;
+
+ case DIGITAL_CMD_TG_LISTEN_MDAA:
+ params = cmd->mdaa_params;
+
+ rc = ddev->ops->tg_listen_mdaa(ddev, params, cmd->timeout,
+ digital_send_cmd_complete, cmd);
+ break;
+
default:
PR_ERR("Unknown cmd type %d", cmd->type);
return;
@@ -189,14 +210,16 @@ static void digital_wq_cmd(struct work_struct *work)
mutex_unlock(&ddev->cmd_lock);
kfree_skb(cmd->req);
+ kfree(cmd->mdaa_params);
kfree(cmd);
schedule_work(&ddev->cmd_work);
}
int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type,
- struct sk_buff *skb, u16 timeout,
- nfc_digital_cmd_complete_t cmd_cb, void *cb_context)
+ struct sk_buff *skb, struct digital_tg_mdaa_params *params,
+ u16 timeout, nfc_digital_cmd_complete_t cmd_cb,
+ void *cb_context)
{
struct digital_cmd *cmd;
@@ -207,6 +230,7 @@ int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type,
cmd->type = cmd_type;
cmd->timeout = timeout;
cmd->req = skb;
+ cmd->mdaa_params = params;
cmd->cmd_cb = cmd_cb;
cmd->cb_context = cb_context;
INIT_LIST_HEAD(&cmd->queue);
@@ -231,6 +255,38 @@ int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param)
return rc;
}
+int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param)
+{
+ int rc;
+
+ rc = ddev->ops->tg_configure_hw(ddev, type, param);
+ if (rc)
+ PR_ERR("tg_configure_hw failed: %d", rc);
+
+ return rc;
+}
+
+static int digital_tg_listen_mdaa(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+ struct digital_tg_mdaa_params *params;
+
+ params = kzalloc(sizeof(struct digital_tg_mdaa_params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ params->sens_res = DIGITAL_SENS_RES_NFC_DEP;
+ get_random_bytes(params->nfcid1, sizeof(params->nfcid1));
+ params->sel_res = DIGITAL_SEL_RES_NFC_DEP;
+
+ params->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1;
+ params->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2;
+ get_random_bytes(params->nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2);
+ params->sc = DIGITAL_SENSF_FELICA_SC;
+
+ return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN_MDAA, NULL, params,
+ 500, digital_tg_recv_atr_req, NULL);
+}
+
int digital_target_found(struct nfc_digital_dev *ddev,
struct nfc_target *target, u8 protocol)
{
@@ -412,6 +468,22 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols,
digital_in_send_sensf_req);
}
+ if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ if (ddev->ops->tg_listen_mdaa) {
+ digital_add_poll_tech(ddev, 0,
+ digital_tg_listen_mdaa);
+ } else {
+ digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
+ digital_tg_listen_nfca);
+
+ digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
+ digital_tg_listen_nfcf);
+
+ digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F,
+ digital_tg_listen_nfcf);
+ }
+ }
+
if (!ddev->poll_tech_count) {
PR_ERR("Unsupported protocols: im=0x%x, tm=0x%x",
matching_im_protocols, matching_tm_protocols);
@@ -496,7 +568,9 @@ static void digital_deactivate_target(struct nfc_dev *nfc_dev,
static int digital_tg_send(struct nfc_dev *dev, struct sk_buff *skb)
{
- return -EOPNOTSUPP;
+ struct nfc_digital_dev *ddev = nfc_get_drvdata(dev);
+
+ return digital_tg_send_dep_res(ddev, skb);
}
static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg,
@@ -654,6 +728,7 @@ void nfc_digital_unregister_device(struct nfc_digital_dev *ddev)
list_for_each_entry_safe(cmd, n, &ddev->cmd_queue, queue) {
list_del(&cmd->queue);
+ kfree(cmd->mdaa_params);
kfree(cmd);
}
}