]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/nfc/pn533.c
Merge remote-tracking branch 'input/next'
[karo-tx-linux.git] / drivers / nfc / pn533.c
index 8cffd73690b8d499aef65a3aa1baed48b33d8416..2daf04c073383c53ae4374d8a9613546fe13d09b 100644 (file)
@@ -150,6 +150,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_TG_INIT_AS_TARGET 0x8c
 #define PN533_CMD_TG_GET_DATA 0x86
 #define PN533_CMD_TG_SET_DATA 0x8e
+#define PN533_CMD_TG_SET_META_DATA 0x94
 #define PN533_CMD_UNDEF 0xff
 
 #define PN533_CMD_RESPONSE(cmd) (cmd + 1)
@@ -373,6 +374,8 @@ struct pn533 {
        struct delayed_work poll_work;
        struct work_struct mi_rx_work;
        struct work_struct mi_tx_work;
+       struct work_struct mi_tm_rx_work;
+       struct work_struct mi_tm_tx_work;
        struct work_struct tg_work;
        struct work_struct rf_work;
 
@@ -1624,27 +1627,121 @@ static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev)
 
 #define PN533_CMD_DATAEXCH_HEAD_LEN 1
 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+static void pn533_wq_tm_mi_recv(struct work_struct *work);
+static struct sk_buff *pn533_build_response(struct pn533 *dev);
+
 static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
                                      struct sk_buff *resp)
 {
-       u8 status;
+       struct sk_buff *skb;
+       u8 status, ret, mi;
+       int rc;
 
        dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
-       if (IS_ERR(resp))
+       if (IS_ERR(resp)) {
+               skb_queue_purge(&dev->resp_q);
                return PTR_ERR(resp);
+       }
 
        status = resp->data[0];
+
+       ret = status & PN533_CMD_RET_MASK;
+       mi = status & PN533_CMD_MI_MASK;
+
        skb_pull(resp, sizeof(status));
 
-       if (status != 0) {
-               nfc_tm_deactivated(dev->nfc_dev);
-               dev->tgt_mode = 0;
-               dev_kfree_skb(resp);
-               return 0;
+       if (ret != PN533_CMD_RET_SUCCESS) {
+               rc = -EIO;
+               goto error;
+       }
+
+       skb_queue_tail(&dev->resp_q, resp);
+
+       if (mi) {
+               queue_work(dev->wq, &dev->mi_tm_rx_work);
+               return -EINPROGRESS;
+       }
+
+       skb = pn533_build_response(dev);
+       if (!skb) {
+               rc = -EIO;
+               goto error;
+       }
+
+       return nfc_tm_data_received(dev->nfc_dev, skb);
+
+error:
+       nfc_tm_deactivated(dev->nfc_dev);
+       dev->tgt_mode = 0;
+       skb_queue_purge(&dev->resp_q);
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static void pn533_wq_tm_mi_recv(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, mi_tm_rx_work);
+       struct sk_buff *skb;
+       int rc;
+
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+       skb = pn533_alloc_skb(dev, 0);
+       if (!skb)
+               return;
+
+       rc = pn533_send_cmd_direct_async(dev,
+                                       PN533_CMD_TG_GET_DATA,
+                                       skb,
+                                       pn533_tm_get_data_complete,
+                                       NULL);
+
+       if (rc < 0)
+               dev_kfree_skb(skb);
+
+       return;
+}
+
+static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
+                                 struct sk_buff *resp);
+static void pn533_wq_tm_mi_send(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, mi_tm_tx_work);
+       struct sk_buff *skb;
+       int rc;
+
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+       /* Grab the first skb in the queue */
+       skb = skb_dequeue(&dev->fragment_skb);
+       if (skb == NULL) {      /* No more data */
+               /* Reset the queue for future use */
+               skb_queue_head_init(&dev->fragment_skb);
+               goto error;
        }
 
-       return nfc_tm_data_received(dev->nfc_dev, resp);
+       /* last entry - remove MI bit */
+       if (skb_queue_len(&dev->fragment_skb) == 0) {
+               rc = pn533_send_cmd_direct_async(dev, PN533_CMD_TG_SET_DATA,
+                                       skb, pn533_tm_send_complete, NULL);
+       } else
+               rc = pn533_send_cmd_direct_async(dev,
+                                       PN533_CMD_TG_SET_META_DATA,
+                                       skb, pn533_tm_send_complete, NULL);
+
+       if (rc == 0) /* success */
+               return;
+
+       dev_err(&dev->interface->dev,
+               "Error %d when trying to perform set meta data_exchange", rc);
+
+       dev_kfree_skb(skb);
+
+error:
+       pn533_send_ack(dev, GFP_KERNEL);
+       queue_work(dev->wq, &dev->cmd_work);
 }
 
 static void pn533_wq_tg_get_data(struct work_struct *work)
@@ -2027,7 +2124,9 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
                            u32 im_protocols, u32 tm_protocols)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct pn533_poll_modulations *cur_mod;
        u8 rand_mod;
+       int rc;
 
        dev_dbg(&dev->interface->dev,
                "%s: im protocols 0x%x tm protocols 0x%x\n",
@@ -2060,7 +2159,15 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
        rand_mod %= dev->poll_mod_count;
        dev->poll_mod_curr = rand_mod;
 
-       return pn533_send_poll_frame(dev);
+       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       rc = pn533_send_poll_frame(dev);
+
+       /* Start listen timer */
+       if (!rc && cur_mod->len == 0 && dev->poll_mod_count > 1)
+               mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ);
+
+       return rc;
 }
 
 static void pn533_stop_poll(struct nfc_dev *nfc_dev)
@@ -2494,14 +2601,17 @@ static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
                        break;
                }
 
-               /* Reserve the TG/MI byte */
-               skb_reserve(frag, 1);
+               if (!dev->tgt_mode) {
+                       /* Reserve the TG/MI byte */
+                       skb_reserve(frag, 1);
 
-               /* MI + TG */
-               if (frag_size  == PN533_CMD_DATAFRAME_MAXLEN)
-                       *skb_push(frag, sizeof(u8)) = (PN533_CMD_MI_MASK | 1);
-               else
-                       *skb_push(frag, sizeof(u8)) =  1; /* TG */
+                       /* MI + TG */
+                       if (frag_size  == PN533_CMD_DATAFRAME_MAXLEN)
+                               *skb_push(frag, sizeof(u8)) =
+                                                       (PN533_CMD_MI_MASK | 1);
+                       else
+                               *skb_push(frag, sizeof(u8)) =  1; /* TG */
+               }
 
                memcpy(skb_put(frag, frag_size), skb->data, frag_size);
 
@@ -2600,6 +2710,11 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
 
        status = resp->data[0];
 
+       /* Prepare for the next round */
+       if (skb_queue_len(&dev->fragment_skb) > 0) {
+               queue_work(dev->wq, &dev->mi_tm_tx_work);
+               return -EINPROGRESS;
+       }
        dev_kfree_skb(resp);
 
        if (status != 0) {
@@ -2622,17 +2737,32 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 
        dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
+       /* let's split in multiple chunks if size's too big */
        if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
-               nfc_err(&dev->interface->dev,
-                       "Data length greater than the max allowed: %d\n",
-                       PN533_CMD_DATAEXCH_DATA_MAXLEN);
-               return -ENOSYS;
+               rc = pn533_fill_fragment_skbs(dev, skb);
+               if (rc <= 0)
+                       goto error;
+
+               /* get the first skb */
+               skb = skb_dequeue(&dev->fragment_skb);
+               if (!skb) {
+                       rc = -EIO;
+                       goto error;
+               }
+
+               rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_META_DATA, skb,
+                                               pn533_tm_send_complete, NULL);
+       } else {
+               /* Send th skb */
+               rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
+                                               pn533_tm_send_complete, NULL);
        }
 
-       rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
-                                  pn533_tm_send_complete, NULL);
-       if (rc < 0)
+error:
+       if (rc < 0) {
                dev_kfree_skb(skb);
+               skb_queue_purge(&dev->fragment_skb);
+       }
 
        return rc;
 }
@@ -2895,12 +3025,12 @@ static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf)
        return rc;
 }
 
-int pn533_dev_up(struct nfc_dev *nfc_dev)
+static int pn533_dev_up(struct nfc_dev *nfc_dev)
 {
        return pn533_rf_field(nfc_dev, 1);
 }
 
-int pn533_dev_down(struct nfc_dev *nfc_dev)
+static int pn533_dev_down(struct nfc_dev *nfc_dev)
 {
        return pn533_rf_field(nfc_dev, 0);
 }
@@ -3042,6 +3172,8 @@ static int pn533_probe(struct usb_interface *interface,
        INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv);
        INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send);
        INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
+       INIT_WORK(&dev->mi_tm_rx_work, pn533_wq_tm_mi_recv);
+       INIT_WORK(&dev->mi_tm_tx_work, pn533_wq_tm_mi_send);
        INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll);
        INIT_WORK(&dev->rf_work, pn533_wq_rf);
        dev->wq = alloc_ordered_workqueue("pn533", 0);