diff options
author | Olivier Guiter <olivier.guiter@linux.intel.com> | 2013-09-23 06:24:39 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-09-25 10:02:24 -0400 |
commit | 93ad42020c2d3fb4a277cc04c014e1ea0d8b0850 (patch) | |
tree | 2268dfc0712d6d1c7149866f8b69613ade88e52c /drivers/nfc | |
parent | 3c13b244de968d88659b5aece0c0c4a6b97f220e (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.c | 77 |
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 | ||
1707 | static int pn533_tm_send_complete(struct pn533 *dev, void *arg, | ||
1708 | struct sk_buff *resp); | ||
1709 | static 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 | |||
1742 | error: | ||
1743 | pn533_send_ack(dev, GFP_KERNEL); | ||
1744 | queue_work(dev->wq, &dev->cmd_work); | ||
1745 | } | ||
1746 | |||
1705 | static void pn533_wq_tg_get_data(struct work_struct *work) | 1747 | static 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, | 2761 | error: |
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); |