diff options
Diffstat (limited to 'drivers/nfc/pn544_hci.c')
| -rw-r--r-- | drivers/nfc/pn544_hci.c | 177 | 
1 files changed, 120 insertions, 57 deletions
diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index aa71807189b..c9c8570273a 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -29,7 +29,7 @@  #include <linux/nfc.h>  #include <net/nfc/hci.h> -#include <net/nfc/shdlc.h> +#include <net/nfc/llc.h>  #include <linux/nfc/pn544.h> @@ -128,10 +128,12 @@ static struct nfc_hci_gate pn544_gates[] = {  /* Largest headroom needed for outgoing custom commands */  #define PN544_CMDS_HEADROOM	2 +#define PN544_FRAME_HEADROOM 1 +#define PN544_FRAME_TAILROOM 2  struct pn544_hci_info {  	struct i2c_client *i2c_dev; -	struct nfc_shdlc *shdlc; +	struct nfc_hci_dev *hdev;  	enum pn544_state state; @@ -146,6 +148,9 @@ struct pn544_hci_info {  				 * < 0 if hardware error occured (e.g. i2c err)  				 * and prevents normal operation.  				 */ +	int async_cb_type; +	data_exchange_cb_t async_cb; +	void *async_cb_context;  };  static void pn544_hci_platform_init(struct pn544_hci_info *info) @@ -230,8 +235,12 @@ static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len)  		r = i2c_master_send(client, buf, len);  	} -	if (r >= 0 && r != len) -		r = -EREMOTEIO; +	if (r >= 0) { +		if (r != len) +			return -EREMOTEIO; +		else +			return 0; +	}  	return r;  } @@ -341,13 +350,16 @@ flush:  static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)  {  	struct pn544_hci_info *info = dev_id; -	struct i2c_client *client = info->i2c_dev; +	struct i2c_client *client;  	struct sk_buff *skb = NULL;  	int r; -	BUG_ON(!info); -	BUG_ON(irq != info->i2c_dev->irq); +	if (!info || irq != info->i2c_dev->irq) { +		WARN_ON_ONCE(1); +		return IRQ_NONE; +	} +	client = info->i2c_dev;  	dev_dbg(&client->dev, "IRQ\n");  	if (info->hard_fault != 0) @@ -357,21 +369,21 @@ static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)  	if (r == -EREMOTEIO) {  		info->hard_fault = r; -		nfc_shdlc_recv_frame(info->shdlc, NULL); +		nfc_hci_recv_frame(info->hdev, NULL);  		return IRQ_HANDLED;  	} else if ((r == -ENOMEM) || (r == -EBADMSG)) {  		return IRQ_HANDLED;  	} -	nfc_shdlc_recv_frame(info->shdlc, skb); +	nfc_hci_recv_frame(info->hdev, skb);  	return IRQ_HANDLED;  } -static int pn544_hci_open(struct nfc_shdlc *shdlc) +static int pn544_hci_open(struct nfc_hci_dev *hdev)  { -	struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); +	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);  	int r = 0;  	mutex_lock(&info->info_lock); @@ -391,9 +403,9 @@ out:  	return r;  } -static void pn544_hci_close(struct nfc_shdlc *shdlc) +static void pn544_hci_close(struct nfc_hci_dev *hdev)  { -	struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); +	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);  	mutex_lock(&info->info_lock); @@ -408,9 +420,8 @@ out:  	mutex_unlock(&info->info_lock);  } -static int pn544_hci_ready(struct nfc_shdlc *shdlc) +static int pn544_hci_ready(struct nfc_hci_dev *hdev)  { -	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);  	struct sk_buff *skb;  	static struct hw_config {  		u8 adr[2]; @@ -576,21 +587,45 @@ static int pn544_hci_ready(struct nfc_shdlc *shdlc)  	return 0;  } -static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb) +static void pn544_hci_add_len_crc(struct sk_buff *skb)  { -	struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); +	u16 crc; +	int len; + +	len = skb->len + 2; +	*skb_push(skb, 1) = len; + +	crc = crc_ccitt(0xffff, skb->data, skb->len); +	crc = ~crc; +	*skb_put(skb, 1) = crc & 0xff; +	*skb_put(skb, 1) = crc >> 8; +} + +static void pn544_hci_remove_len_crc(struct sk_buff *skb) +{ +	skb_pull(skb, PN544_FRAME_HEADROOM); +	skb_trim(skb, PN544_FRAME_TAILROOM); +} + +static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ +	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);  	struct i2c_client *client = info->i2c_dev; +	int r;  	if (info->hard_fault != 0)  		return info->hard_fault; -	return pn544_hci_i2c_write(client, skb->data, skb->len); +	pn544_hci_add_len_crc(skb); +	r = pn544_hci_i2c_write(client, skb->data, skb->len); +	pn544_hci_remove_len_crc(skb); + +	return r;  } -static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, +static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,  				u32 im_protocols, u32 tm_protocols)  { -	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);  	u8 phases = 0;  	int r;  	u8 duration[2]; @@ -641,7 +676,7 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,  	return r;  } -static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate, +static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,  				      struct nfc_target *target)  {  	switch (gate) { @@ -659,11 +694,10 @@ static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,  	return 0;  } -static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, +static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,  						u8 gate,  						struct nfc_target *target)  { -	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);  	struct sk_buff *uid_skb;  	int r = 0; @@ -704,6 +738,26 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,  	return r;  } +#define PN544_CB_TYPE_READER_F 1 + +static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, +				       int err) +{ +	struct pn544_hci_info *info = context; + +	switch (info->async_cb_type) { +	case PN544_CB_TYPE_READER_F: +		if (err == 0) +			skb_pull(skb, 1); +		info->async_cb(info->async_cb_context, skb, err); +		break; +	default: +		if (err == 0) +			kfree_skb(skb); +		break; +	} +} +  #define MIFARE_CMD_AUTH_KEY_A	0x60  #define MIFARE_CMD_AUTH_KEY_B	0x61  #define MIFARE_CMD_HEADER	2 @@ -715,13 +769,12 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,   * <= 0: driver handled the data exchange   *    1: driver doesn't especially handle, please do standard processing   */ -static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, +static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev,  				   struct nfc_target *target, -				   struct sk_buff *skb, -				   struct sk_buff **res_skb) +				   struct sk_buff *skb, data_exchange_cb_t cb, +				   void *cb_context)  { -	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); -	int r; +	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);  	pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,  		target->hci_reader_gate); @@ -746,41 +799,43 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,  				memcpy(data, uid, MIFARE_UID_LEN);  			} -			return nfc_hci_send_cmd(hdev, target->hci_reader_gate, -						PN544_MIFARE_CMD, -						skb->data, skb->len, res_skb); +			return nfc_hci_send_cmd_async(hdev, +						      target->hci_reader_gate, +						      PN544_MIFARE_CMD, +						      skb->data, skb->len, +						      cb, cb_context);  		} else  			return 1;  	case PN544_RF_READER_F_GATE:  		*skb_push(skb, 1) = 0;  		*skb_push(skb, 1) = 0; -		r = nfc_hci_send_cmd(hdev, target->hci_reader_gate, -				     PN544_FELICA_RAW, -				     skb->data, skb->len, res_skb); -		if (r == 0) -			skb_pull(*res_skb, 1); -		return r; +		info->async_cb_type = PN544_CB_TYPE_READER_F; +		info->async_cb = cb; +		info->async_cb_context = cb_context; + +		return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, +					      PN544_FELICA_RAW, skb->data, +					      skb->len, +					      pn544_hci_data_exchange_cb, info);  	case PN544_RF_READER_JEWEL_GATE: -		return nfc_hci_send_cmd(hdev, target->hci_reader_gate, -					PN544_JEWEL_RAW_CMD, -					skb->data, skb->len, res_skb); +		return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, +					      PN544_JEWEL_RAW_CMD, skb->data, +					      skb->len, cb, cb_context);  	default:  		return 1;  	}  } -static int pn544_hci_check_presence(struct nfc_shdlc *shdlc, +static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,  				   struct nfc_target *target)  { -	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); -  	return nfc_hci_send_cmd(hdev, target->hci_reader_gate,  				PN544_RF_READER_CMD_PRESENCE_CHECK,  				NULL, 0, NULL);  } -static struct nfc_shdlc_ops pn544_shdlc_ops = { +static struct nfc_hci_ops pn544_hci_ops = {  	.open = pn544_hci_open,  	.close = pn544_hci_close,  	.hci_ready = pn544_hci_ready, @@ -848,8 +903,8 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,  	pn544_hci_platform_init(info);  	r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn, -				 IRQF_TRIGGER_RISING, PN544_HCI_DRIVER_NAME, -				 info); +				 IRQF_TRIGGER_RISING | IRQF_ONESHOT, +				 PN544_HCI_DRIVER_NAME, info);  	if (r < 0) {  		dev_err(&client->dev, "Unable to register IRQ handler\n");  		goto err_rti; @@ -872,22 +927,30 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,  		    NFC_PROTO_ISO14443_B_MASK |  		    NFC_PROTO_NFC_DEP_MASK; -	info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops, -					 &init_data, protocols, -					 PN544_CMDS_HEADROOM, 0, -					 PN544_HCI_LLC_MAX_PAYLOAD, -					 dev_name(&client->dev)); -	if (!info->shdlc) { -		dev_err(&client->dev, "Cannot allocate nfc shdlc.\n"); +	info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, +					     protocols, LLC_SHDLC_NAME, +					     PN544_FRAME_HEADROOM + +					     PN544_CMDS_HEADROOM, +					     PN544_FRAME_TAILROOM, +					     PN544_HCI_LLC_MAX_PAYLOAD); +	if (!info->hdev) { +		dev_err(&client->dev, "Cannot allocate nfc hdev.\n");  		r = -ENOMEM; -		goto err_allocshdlc; +		goto err_alloc_hdev;  	} -	nfc_shdlc_set_clientdata(info->shdlc, info); +	nfc_hci_set_clientdata(info->hdev, info); + +	r = nfc_hci_register_device(info->hdev); +	if (r) +		goto err_regdev;  	return 0; -err_allocshdlc: +err_regdev: +	nfc_hci_free_device(info->hdev); + +err_alloc_hdev:  	free_irq(client->irq, info);  err_rti: @@ -908,7 +971,7 @@ static __devexit int pn544_hci_remove(struct i2c_client *client)  	dev_dbg(&client->dev, "%s\n", __func__); -	nfc_shdlc_free(info->shdlc); +	nfc_hci_free_device(info->hdev);  	if (info->state != PN544_ST_COLD) {  		if (pdata->disable)  |