NFC: pn533: Split large Tx frames in chunks

On sending large frames (size > 262), we split it in multiple chunks and
send them asynchronously with MI bit.

Signed-off-by: Olivier Guiter <olivier.guiter@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Olivier Guiter 2013-06-13 13:43:28 +00:00 committed by Samuel Ortiz
parent 1575b9d866
commit 963a82e07d
1 changed files with 130 additions and 14 deletions

View File

@ -363,12 +363,14 @@ struct pn533 {
struct urb *in_urb;
struct sk_buff_head resp_q;
struct sk_buff_head fragment_skb;
struct workqueue_struct *wq;
struct work_struct cmd_work;
struct work_struct cmd_complete_work;
struct work_struct poll_work;
struct work_struct mi_work;
struct work_struct mi_rx_work;
struct work_struct mi_tx_work;
struct work_struct tg_work;
struct work_struct rf_work;
@ -378,6 +380,7 @@ struct pn533 {
struct mutex cmd_lock; /* protects cmd queue */
void *cmd_complete_mi_arg;
void *cmd_complete_dep_arg;
struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
u8 poll_mod_count;
@ -2328,7 +2331,15 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
if (mi) {
dev->cmd_complete_mi_arg = arg;
queue_work(dev->wq, &dev->mi_work);
queue_work(dev->wq, &dev->mi_rx_work);
return -EINPROGRESS;
}
/* Prepare for the next round */
if (skb_queue_len(&dev->fragment_skb) > 0) {
dev->cmd_complete_dep_arg = arg;
queue_work(dev->wq, &dev->mi_tx_work);
return -EINPROGRESS;
}
@ -2349,6 +2360,50 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
return rc;
}
/* Split the Tx skb into small chunks */
static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
{
struct sk_buff *frag;
int frag_size;
do {
/* Remaining size */
if (skb->len > PN533_CMD_DATAFRAME_MAXLEN)
frag_size = PN533_CMD_DATAFRAME_MAXLEN;
else
frag_size = skb->len;
/* Allocate and reserve */
frag = pn533_alloc_skb(dev, frag_size);
if (!frag) {
skb_queue_purge(&dev->fragment_skb);
break;
}
/* 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 */
memcpy(skb_put(frag, frag_size), skb->data, frag_size);
/* Reduce the size of incoming buffer */
skb_pull(skb, frag_size);
/* Add this to skb_queue */
skb_queue_tail(&dev->fragment_skb, frag);
} while (skb->len > 0);
dev_kfree_skb(skb);
return skb_queue_len(&dev->fragment_skb);
}
static int pn533_transceive(struct nfc_dev *nfc_dev,
struct nfc_target *target, struct sk_buff *skb,
data_exchange_cb_t cb, void *cb_context)
@ -2359,15 +2414,6 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
/* TODO: Implement support to multi-part data exchange */
nfc_dev_err(&dev->interface->dev,
"Data length greater than the max allowed: %d",
PN533_CMD_DATAEXCH_DATA_MAXLEN);
rc = -ENOSYS;
goto error;
}
if (!dev->tgt_active_prot) {
nfc_dev_err(&dev->interface->dev,
"Can't exchange data if there is no active target");
@ -2395,7 +2441,20 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
break;
}
default:
*skb_push(skb, sizeof(u8)) = 1; /*TG*/
/* jumbo frame ? */
if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
rc = pn533_fill_fragment_skbs(dev, skb);
if (rc <= 0)
goto error;
skb = skb_dequeue(&dev->fragment_skb);
if (!skb) {
rc = -EIO;
goto error;
}
} else {
*skb_push(skb, sizeof(u8)) = 1; /* TG */
}
rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
skb, pn533_data_exchange_complete,
@ -2466,7 +2525,7 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
static void pn533_wq_mi_recv(struct work_struct *work)
{
struct pn533 *dev = container_of(work, struct pn533, mi_work);
struct pn533 *dev = container_of(work, struct pn533, mi_rx_work);
struct sk_buff *skb;
int rc;
@ -2514,6 +2573,61 @@ static void pn533_wq_mi_recv(struct work_struct *work)
queue_work(dev->wq, &dev->cmd_work);
}
static void pn533_wq_mi_send(struct work_struct *work)
{
struct pn533 *dev = container_of(work, struct pn533, mi_tx_work);
struct sk_buff *skb;
int rc;
nfc_dev_dbg(&dev->interface->dev, "%s", __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;
}
switch (dev->device_type) {
case PN533_DEVICE_PASORI:
if (dev->tgt_active_prot != NFC_PROTO_FELICA) {
rc = -EIO;
break;
}
rc = pn533_send_cmd_direct_async(dev, PN533_CMD_IN_COMM_THRU,
skb,
pn533_data_exchange_complete,
dev->cmd_complete_dep_arg);
break;
default:
/* Still some fragments? */
rc = pn533_send_cmd_direct_async(dev,PN533_CMD_IN_DATA_EXCHANGE,
skb,
pn533_data_exchange_complete,
dev->cmd_complete_dep_arg);
break;
}
if (rc == 0) /* success */
return;
nfc_dev_err(&dev->interface->dev,
"Error %d when trying to perform data_exchange", rc);
dev_kfree_skb(skb);
kfree(dev->cmd_complete_dep_arg);
error:
pn533_send_ack(dev, GFP_KERNEL);
queue_work(dev->wq, &dev->cmd_work);
}
static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
u8 cfgdata_len)
{
@ -2816,7 +2930,8 @@ static int pn533_probe(struct usb_interface *interface,
INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
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->poll_work, pn533_wq_poll);
INIT_WORK(&dev->rf_work, pn533_wq_rf);
@ -2829,6 +2944,7 @@ static int pn533_probe(struct usb_interface *interface,
dev->listen_timer.function = pn533_listen_mode_timer;
skb_queue_head_init(&dev->resp_q);
skb_queue_head_init(&dev->fragment_skb);
INIT_LIST_HEAD(&dev->cmd_queue);