aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nfc
diff options
context:
space:
mode:
authorOlivier Guiter <olivier.guiter@linux.intel.com>2013-09-23 06:24:39 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2013-09-25 10:02:24 -0400
commit93ad42020c2d3fb4a277cc04c014e1ea0d8b0850 (patch)
tree2268dfc0712d6d1c7149866f8b69613ade88e52c /drivers/nfc
parent3c13b244de968d88659b5aece0c0c4a6b97f220e (diff)
NFC: pn533: Target mode Tx fragmentation support
In target mode, when we want to send frames larger than the max length (PN533_CMD_DATAEXCH_DATA_MAXLEN), we have to split the frame in smaller chunks and send them, using a specific working queue, with the TgSetMetaData command. TgSetMetaData sets his own MI bit in the PFB. The last chunk is sent using the TgSetData command. Signed-off-by: Olivier Guiter <olivier.guiter@linux.intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/pn533.c77
1 files changed, 70 insertions, 7 deletions
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
index cd20641e7443..2daf04c07338 100644
--- a/drivers/nfc/pn533.c
+++ b/drivers/nfc/pn533.c
@@ -150,6 +150,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
150#define PN533_CMD_TG_INIT_AS_TARGET 0x8c 150#define PN533_CMD_TG_INIT_AS_TARGET 0x8c
151#define PN533_CMD_TG_GET_DATA 0x86 151#define PN533_CMD_TG_GET_DATA 0x86
152#define PN533_CMD_TG_SET_DATA 0x8e 152#define PN533_CMD_TG_SET_DATA 0x8e
153#define PN533_CMD_TG_SET_META_DATA 0x94
153#define PN533_CMD_UNDEF 0xff 154#define PN533_CMD_UNDEF 0xff
154 155
155#define PN533_CMD_RESPONSE(cmd) (cmd + 1) 156#define PN533_CMD_RESPONSE(cmd) (cmd + 1)
@@ -374,6 +375,7 @@ struct pn533 {
374 struct work_struct mi_rx_work; 375 struct work_struct mi_rx_work;
375 struct work_struct mi_tx_work; 376 struct work_struct mi_tx_work;
376 struct work_struct mi_tm_rx_work; 377 struct work_struct mi_tm_rx_work;
378 struct work_struct mi_tm_tx_work;
377 struct work_struct tg_work; 379 struct work_struct tg_work;
378 struct work_struct rf_work; 380 struct work_struct rf_work;
379 381
@@ -1702,6 +1704,46 @@ static void pn533_wq_tm_mi_recv(struct work_struct *work)
1702 return; 1704 return;
1703} 1705}
1704 1706
1707static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
1708 struct sk_buff *resp);
1709static void pn533_wq_tm_mi_send(struct work_struct *work)
1710{
1711 struct pn533 *dev = container_of(work, struct pn533, mi_tm_tx_work);
1712 struct sk_buff *skb;
1713 int rc;
1714
1715 dev_dbg(&dev->interface->dev, "%s\n", __func__);
1716
1717 /* Grab the first skb in the queue */
1718 skb = skb_dequeue(&dev->fragment_skb);
1719 if (skb == NULL) { /* No more data */
1720 /* Reset the queue for future use */
1721 skb_queue_head_init(&dev->fragment_skb);
1722 goto error;
1723 }
1724
1725 /* last entry - remove MI bit */
1726 if (skb_queue_len(&dev->fragment_skb) == 0) {
1727 rc = pn533_send_cmd_direct_async(dev, PN533_CMD_TG_SET_DATA,
1728 skb, pn533_tm_send_complete, NULL);
1729 } else
1730 rc = pn533_send_cmd_direct_async(dev,
1731 PN533_CMD_TG_SET_META_DATA,
1732 skb, pn533_tm_send_complete, NULL);
1733
1734 if (rc == 0) /* success */
1735 return;
1736
1737 dev_err(&dev->interface->dev,
1738 "Error %d when trying to perform set meta data_exchange", rc);
1739
1740 dev_kfree_skb(skb);
1741
1742error:
1743 pn533_send_ack(dev, GFP_KERNEL);
1744 queue_work(dev->wq, &dev->cmd_work);
1745}
1746
1705static void pn533_wq_tg_get_data(struct work_struct *work) 1747static void pn533_wq_tg_get_data(struct work_struct *work)
1706{ 1748{
1707 struct pn533 *dev = container_of(work, struct pn533, tg_work); 1749 struct pn533 *dev = container_of(work, struct pn533, tg_work);
@@ -2668,6 +2710,11 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
2668 2710
2669 status = resp->data[0]; 2711 status = resp->data[0];
2670 2712
2713 /* Prepare for the next round */
2714 if (skb_queue_len(&dev->fragment_skb) > 0) {
2715 queue_work(dev->wq, &dev->mi_tm_tx_work);
2716 return -EINPROGRESS;
2717 }
2671 dev_kfree_skb(resp); 2718 dev_kfree_skb(resp);
2672 2719
2673 if (status != 0) { 2720 if (status != 0) {
@@ -2690,17 +2737,32 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
2690 2737
2691 dev_dbg(&dev->interface->dev, "%s\n", __func__); 2738 dev_dbg(&dev->interface->dev, "%s\n", __func__);
2692 2739
2740 /* let's split in multiple chunks if size's too big */
2693 if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) { 2741 if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
2694 nfc_err(&dev->interface->dev, 2742 rc = pn533_fill_fragment_skbs(dev, skb);
2695 "Data length greater than the max allowed: %d\n", 2743 if (rc <= 0)
2696 PN533_CMD_DATAEXCH_DATA_MAXLEN); 2744 goto error;
2697 return -ENOSYS; 2745
2746 /* get the first skb */
2747 skb = skb_dequeue(&dev->fragment_skb);
2748 if (!skb) {
2749 rc = -EIO;
2750 goto error;
2751 }
2752
2753 rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_META_DATA, skb,
2754 pn533_tm_send_complete, NULL);
2755 } else {
2756 /* Send th skb */
2757 rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
2758 pn533_tm_send_complete, NULL);
2698 } 2759 }
2699 2760
2700 rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb, 2761error:
2701 pn533_tm_send_complete, NULL); 2762 if (rc < 0) {
2702 if (rc < 0)
2703 dev_kfree_skb(skb); 2763 dev_kfree_skb(skb);
2764 skb_queue_purge(&dev->fragment_skb);
2765 }
2704 2766
2705 return rc; 2767 return rc;
2706} 2768}
@@ -3111,6 +3173,7 @@ static int pn533_probe(struct usb_interface *interface,
3111 INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send); 3173 INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send);
3112 INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); 3174 INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
3113 INIT_WORK(&dev->mi_tm_rx_work, pn533_wq_tm_mi_recv); 3175 INIT_WORK(&dev->mi_tm_rx_work, pn533_wq_tm_mi_recv);
3176 INIT_WORK(&dev->mi_tm_tx_work, pn533_wq_tm_mi_send);
3114 INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll); 3177 INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll);
3115 INIT_WORK(&dev->rf_work, pn533_wq_rf); 3178 INIT_WORK(&dev->rf_work, pn533_wq_rf);
3116 dev->wq = alloc_ordered_workqueue("pn533", 0); 3179 dev->wq = alloc_ordered_workqueue("pn533", 0);