mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									1575b9d866
								
							
						
					
					
						commit
						963a82e07d
					
				
					 1 changed files with 130 additions and 14 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue