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 | |
| 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')
| -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); |
