diff options
Diffstat (limited to 'net/nfc')
| -rw-r--r-- | net/nfc/Kconfig | 14 | ||||
| -rw-r--r-- | net/nfc/Makefile | 2 | ||||
| -rw-r--r-- | net/nfc/af_nfc.c | 4 | ||||
| -rw-r--r-- | net/nfc/core.c | 43 | ||||
| -rw-r--r-- | net/nfc/digital.h | 177 | ||||
| -rw-r--r-- | net/nfc/digital_core.c | 826 | ||||
| -rw-r--r-- | net/nfc/digital_dep.c | 746 | ||||
| -rw-r--r-- | net/nfc/digital_technology.c | 1239 | ||||
| -rw-r--r-- | net/nfc/hci/command.c | 10 | ||||
| -rw-r--r-- | net/nfc/hci/core.c | 58 | ||||
| -rw-r--r-- | net/nfc/hci/hci.h | 4 | ||||
| -rw-r--r-- | net/nfc/hci/hcp.c | 4 | ||||
| -rw-r--r-- | net/nfc/hci/llc.c | 8 | ||||
| -rw-r--r-- | net/nfc/hci/llc.h | 4 | ||||
| -rw-r--r-- | net/nfc/hci/llc_nop.c | 4 | ||||
| -rw-r--r-- | net/nfc/hci/llc_shdlc.c | 6 | ||||
| -rw-r--r-- | net/nfc/llcp.h | 4 | ||||
| -rw-r--r-- | net/nfc/llcp_commands.c | 12 | ||||
| -rw-r--r-- | net/nfc/llcp_core.c | 36 | ||||
| -rw-r--r-- | net/nfc/llcp_sock.c | 15 | ||||
| -rw-r--r-- | net/nfc/nci/core.c | 42 | ||||
| -rw-r--r-- | net/nfc/nci/data.c | 3 | ||||
| -rw-r--r-- | net/nfc/nci/lib.c | 3 | ||||
| -rw-r--r-- | net/nfc/nci/ntf.c | 10 | ||||
| -rw-r--r-- | net/nfc/nci/rsp.c | 3 | ||||
| -rw-r--r-- | net/nfc/nci/spi.c | 240 | ||||
| -rw-r--r-- | net/nfc/netlink.c | 142 | ||||
| -rw-r--r-- | net/nfc/nfc.h | 10 | ||||
| -rw-r--r-- | net/nfc/rawsock.c | 107 | 
29 files changed, 3447 insertions, 329 deletions
diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig index 5948b2fc72f..6e0fa0cce19 100644 --- a/net/nfc/Kconfig +++ b/net/nfc/Kconfig @@ -14,6 +14,20 @@ menuconfig NFC  	  To compile this support as a module, choose M here: the module will  	  be called nfc. +config NFC_DIGITAL +	depends on NFC +	select CRC_CCITT +	select CRC_ITU_T +	tristate "NFC Digital Protocol stack support" +	default n +	help +	  Say Y if you want to build NFC digital protocol stack support. +	  This is needed by NFC chipsets whose firmware only implement +	  the NFC analog layer. + +	  To compile this support as a module, choose M here: the module will +	  be called nfc_digital. +  source "net/nfc/nci/Kconfig"  source "net/nfc/hci/Kconfig" diff --git a/net/nfc/Makefile b/net/nfc/Makefile index a76f4533cb6..2555ff8e721 100644 --- a/net/nfc/Makefile +++ b/net/nfc/Makefile @@ -5,7 +5,9 @@  obj-$(CONFIG_NFC) += nfc.o  obj-$(CONFIG_NFC_NCI) += nci/  obj-$(CONFIG_NFC_HCI) += hci/ +obj-$(CONFIG_NFC_DIGITAL) += nfc_digital.o  nfc-objs := core.o netlink.o af_nfc.o rawsock.o llcp_core.o llcp_commands.o \  		llcp_sock.o +nfc_digital-objs := digital_core.o digital_technology.o digital_dep.o diff --git a/net/nfc/af_nfc.c b/net/nfc/af_nfc.c index 9d68441e2a5..2277276f52b 100644 --- a/net/nfc/af_nfc.c +++ b/net/nfc/af_nfc.c @@ -16,9 +16,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/nfc.h> diff --git a/net/nfc/core.c b/net/nfc/core.c index e92923cf3e0..819b87702b7 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -16,9 +16,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ @@ -135,11 +133,8 @@ int nfc_dev_up(struct nfc_dev *dev)  		dev->dev_up = true;  	/* We have to enable the device before discovering SEs */ -	if (dev->ops->discover_se) { -		rc = dev->ops->discover_se(dev); -		if (rc) -			pr_warn("SE discovery failed\n"); -	} +	if (dev->ops->discover_se && dev->ops->discover_se(dev)) +		pr_err("SE discovery failed\n");  error:  	device_unlock(&dev->dev); @@ -285,9 +280,6 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)  {  	int i; -	if (dev->n_targets == 0) -		return NULL; -  	for (i = 0; i < dev->n_targets; i++) {  		if (dev->targets[i].idx == target_idx)  			return &dev->targets[i]; @@ -384,6 +376,19 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,  {  	dev->dep_link_up = true; +	if (!dev->active_target && rf_mode == NFC_RF_INITIATOR) { +		struct nfc_target *target; + +		target = nfc_find_target(dev, target_idx); +		if (target == NULL) +			return -ENOTCONN; + +		dev->active_target = target; +	} + +	dev->polling = false; +	dev->rf_mode = rf_mode; +  	nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode);  	return nfc_genl_dep_link_up_event(dev, target_idx, comm_mode, rf_mode); @@ -536,16 +541,17 @@ error:  	return rc;  } -static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx) +struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx)  { -	struct nfc_se *se, *n; +	struct nfc_se *se; -	list_for_each_entry_safe(se, n, &dev->secure_elements, list) +	list_for_each_entry(se, &dev->secure_elements, list)  		if (se->idx == se_idx)  			return se;  	return NULL;  } +EXPORT_SYMBOL(nfc_find_se);  int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)  { @@ -577,7 +583,7 @@ int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)  		goto error;  	} -	se = find_se(dev, se_idx); +	se = nfc_find_se(dev, se_idx);  	if (!se) {  		rc = -EINVAL;  		goto error; @@ -622,7 +628,7 @@ int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)  		goto error;  	} -	se = find_se(dev, se_idx); +	se = nfc_find_se(dev, se_idx);  	if (!se) {  		rc = -EINVAL;  		goto error; @@ -646,9 +652,6 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)  {  	pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len); -	if (gb_len > NFC_MAX_GT_LEN) -		return -EINVAL; -  	return nfc_llcp_set_remote_gb(dev, gb, gb_len);  }  EXPORT_SYMBOL(nfc_set_remote_general_bytes); @@ -881,7 +884,7 @@ int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)  	pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx); -	se = find_se(dev, se_idx); +	se = nfc_find_se(dev, se_idx);  	if (se)  		return -EALREADY; diff --git a/net/nfc/digital.h b/net/nfc/digital.h new file mode 100644 index 00000000000..71ad7eefddd --- /dev/null +++ b/net/nfc/digital.h @@ -0,0 +1,177 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#ifndef __DIGITAL_H +#define __DIGITAL_H + +#include <net/nfc/nfc.h> +#include <net/nfc/digital.h> + +#include <linux/crc-ccitt.h> +#include <linux/crc-itu-t.h> + +#define PROTOCOL_ERR(req) pr_err("%d: NFC Digital Protocol error: %s\n", \ +				 __LINE__, req) + +#define DIGITAL_CMD_IN_SEND        0 +#define DIGITAL_CMD_TG_SEND        1 +#define DIGITAL_CMD_TG_LISTEN      2 +#define DIGITAL_CMD_TG_LISTEN_MDAA 3 + +#define DIGITAL_MAX_HEADER_LEN 7 +#define DIGITAL_CRC_LEN        2 + +#define DIGITAL_SENSF_NFCID2_NFC_DEP_B1 0x01 +#define DIGITAL_SENSF_NFCID2_NFC_DEP_B2 0xFE + +#define DIGITAL_SENS_RES_NFC_DEP 0x0100 +#define DIGITAL_SEL_RES_NFC_DEP  0x40 +#define DIGITAL_SENSF_FELICA_SC  0xFFFF + +#define DIGITAL_DRV_CAPS_IN_CRC(ddev) \ +	((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_IN_CRC) +#define DIGITAL_DRV_CAPS_TG_CRC(ddev) \ +	((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_TG_CRC) + +struct digital_data_exch { +	data_exchange_cb_t cb; +	void *cb_context; +}; + +struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev, +				  unsigned int len); + +int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type, +		     struct sk_buff *skb, struct digital_tg_mdaa_params *params, +		     u16 timeout, nfc_digital_cmd_complete_t cmd_cb, +		     void *cb_context); + +int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param); +static inline int digital_in_send_cmd(struct nfc_digital_dev *ddev, +				      struct sk_buff *skb, u16 timeout, +				      nfc_digital_cmd_complete_t cmd_cb, +				      void *cb_context) +{ +	return digital_send_cmd(ddev, DIGITAL_CMD_IN_SEND, skb, NULL, timeout, +				cmd_cb, cb_context); +} + +void digital_poll_next_tech(struct nfc_digital_dev *ddev); + +int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech); +int digital_in_send_sensb_req(struct nfc_digital_dev *ddev, u8 rf_tech); +int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech); +int digital_in_send_iso15693_inv_req(struct nfc_digital_dev *ddev, u8 rf_tech); + +int digital_in_iso_dep_pull_sod(struct nfc_digital_dev *ddev, +				struct sk_buff *skb); +int digital_in_iso_dep_push_sod(struct nfc_digital_dev *ddev, +				struct sk_buff *skb); + +int digital_target_found(struct nfc_digital_dev *ddev, +			 struct nfc_target *target, u8 protocol); + +int digital_in_recv_mifare_res(struct sk_buff *resp); + +int digital_in_send_atr_req(struct nfc_digital_dev *ddev, +			    struct nfc_target *target, __u8 comm_mode, __u8 *gb, +			    size_t gb_len); +int digital_in_send_dep_req(struct nfc_digital_dev *ddev, +			    struct nfc_target *target, struct sk_buff *skb, +			    struct digital_data_exch *data_exch); + +int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param); +static inline int digital_tg_send_cmd(struct nfc_digital_dev *ddev, +			struct sk_buff *skb, u16 timeout, +			nfc_digital_cmd_complete_t cmd_cb, void *cb_context) +{ +	return digital_send_cmd(ddev, DIGITAL_CMD_TG_SEND, skb, NULL, timeout, +				cmd_cb, cb_context); +} + +void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg, +			      struct sk_buff *resp); + +void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg, +			       struct sk_buff *resp); + +static inline int digital_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, +				    nfc_digital_cmd_complete_t cb, void *arg) +{ +	return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN, NULL, NULL, +				timeout, cb, arg); +} + +void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, +			     struct sk_buff *resp); + +int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb); + +int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech); +int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech); + +typedef u16 (*crc_func_t)(u16, const u8 *, size_t); + +#define CRC_A_INIT 0x6363 +#define CRC_B_INIT 0xFFFF +#define CRC_F_INIT 0x0000 + +void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init, +			 u8 bitwise_inv, u8 msb_first); + +static inline void digital_skb_add_crc_a(struct sk_buff *skb) +{ +	digital_skb_add_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0); +} + +static inline void digital_skb_add_crc_b(struct sk_buff *skb) +{ +	digital_skb_add_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0); +} + +static inline void digital_skb_add_crc_f(struct sk_buff *skb) +{ +	digital_skb_add_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1); +} + +static inline void digital_skb_add_crc_none(struct sk_buff *skb) +{ +	return; +} + +int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func, +			  u16 crc_init, u8 bitwise_inv, u8 msb_first); + +static inline int digital_skb_check_crc_a(struct sk_buff *skb) +{ +	return digital_skb_check_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0); +} + +static inline int digital_skb_check_crc_b(struct sk_buff *skb) +{ +	return digital_skb_check_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0); +} + +static inline int digital_skb_check_crc_f(struct sk_buff *skb) +{ +	return digital_skb_check_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1); +} + +static inline int digital_skb_check_crc_none(struct sk_buff *skb) +{ +	return 0; +} + +#endif /* __DIGITAL_H */ diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c new file mode 100644 index 00000000000..a6ce3c627e4 --- /dev/null +++ b/net/nfc/digital_core.c @@ -0,0 +1,826 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) "digital: %s: " fmt, __func__ + +#include <linux/module.h> + +#include "digital.h" + +#define DIGITAL_PROTO_NFCA_RF_TECH \ +	(NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK) + +#define DIGITAL_PROTO_NFCB_RF_TECH	NFC_PROTO_ISO14443_B_MASK + +#define DIGITAL_PROTO_NFCF_RF_TECH \ +	(NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK) + +#define DIGITAL_PROTO_ISO15693_RF_TECH	NFC_PROTO_ISO15693_MASK + +struct digital_cmd { +	struct list_head queue; + +	u8 type; +	u8 pending; + +	u16 timeout; +	struct sk_buff *req; +	struct sk_buff *resp; +	struct digital_tg_mdaa_params *mdaa_params; + +	nfc_digital_cmd_complete_t cmd_cb; +	void *cb_context; +}; + +struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev, +				  unsigned int len) +{ +	struct sk_buff *skb; + +	skb = alloc_skb(len + ddev->tx_headroom + ddev->tx_tailroom, +			GFP_KERNEL); +	if (skb) +		skb_reserve(skb, ddev->tx_headroom); + +	return skb; +} + +void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init, +			 u8 bitwise_inv, u8 msb_first) +{ +	u16 crc; + +	crc = crc_func(init, skb->data, skb->len); + +	if (bitwise_inv) +		crc = ~crc; + +	if (msb_first) +		crc = __fswab16(crc); + +	*skb_put(skb, 1) = crc & 0xFF; +	*skb_put(skb, 1) = (crc >> 8) & 0xFF; +} + +int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func, +			  u16 crc_init, u8 bitwise_inv, u8 msb_first) +{ +	int rc; +	u16 crc; + +	if (skb->len <= 2) +		return -EIO; + +	crc = crc_func(crc_init, skb->data, skb->len - 2); + +	if (bitwise_inv) +		crc = ~crc; + +	if (msb_first) +		crc = __swab16(crc); + +	rc = (skb->data[skb->len - 2] - (crc & 0xFF)) + +	     (skb->data[skb->len - 1] - ((crc >> 8) & 0xFF)); + +	if (rc) +		return -EIO; + +	skb_trim(skb, skb->len - 2); + +	return 0; +} + +static inline void digital_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ +	ddev->ops->switch_rf(ddev, on); +} + +static inline void digital_abort_cmd(struct nfc_digital_dev *ddev) +{ +	ddev->ops->abort_cmd(ddev); +} + +static void digital_wq_cmd_complete(struct work_struct *work) +{ +	struct digital_cmd *cmd; +	struct nfc_digital_dev *ddev = container_of(work, +						    struct nfc_digital_dev, +						    cmd_complete_work); + +	mutex_lock(&ddev->cmd_lock); + +	cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd, +				       queue); +	if (!cmd) { +		mutex_unlock(&ddev->cmd_lock); +		return; +	} + +	list_del(&cmd->queue); + +	mutex_unlock(&ddev->cmd_lock); + +	if (!IS_ERR(cmd->resp)) +		print_hex_dump_debug("DIGITAL RX: ", DUMP_PREFIX_NONE, 16, 1, +				     cmd->resp->data, cmd->resp->len, false); + +	cmd->cmd_cb(ddev, cmd->cb_context, cmd->resp); + +	kfree(cmd->mdaa_params); +	kfree(cmd); + +	schedule_work(&ddev->cmd_work); +} + +static void digital_send_cmd_complete(struct nfc_digital_dev *ddev, +				      void *arg, struct sk_buff *resp) +{ +	struct digital_cmd *cmd = arg; + +	cmd->resp = resp; + +	schedule_work(&ddev->cmd_complete_work); +} + +static void digital_wq_cmd(struct work_struct *work) +{ +	int rc; +	struct digital_cmd *cmd; +	struct digital_tg_mdaa_params *params; +	struct nfc_digital_dev *ddev = container_of(work, +						    struct nfc_digital_dev, +						    cmd_work); + +	mutex_lock(&ddev->cmd_lock); + +	cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd, +				       queue); +	if (!cmd || cmd->pending) { +		mutex_unlock(&ddev->cmd_lock); +		return; +	} + +	mutex_unlock(&ddev->cmd_lock); + +	if (cmd->req) +		print_hex_dump_debug("DIGITAL TX: ", DUMP_PREFIX_NONE, 16, 1, +				     cmd->req->data, cmd->req->len, false); + +	switch (cmd->type) { +	case DIGITAL_CMD_IN_SEND: +		rc = ddev->ops->in_send_cmd(ddev, cmd->req, cmd->timeout, +					    digital_send_cmd_complete, cmd); +		break; + +	case DIGITAL_CMD_TG_SEND: +		rc = ddev->ops->tg_send_cmd(ddev, cmd->req, cmd->timeout, +					    digital_send_cmd_complete, cmd); +		break; + +	case DIGITAL_CMD_TG_LISTEN: +		rc = ddev->ops->tg_listen(ddev, cmd->timeout, +					  digital_send_cmd_complete, cmd); +		break; + +	case DIGITAL_CMD_TG_LISTEN_MDAA: +		params = cmd->mdaa_params; + +		rc = ddev->ops->tg_listen_mdaa(ddev, params, cmd->timeout, +					       digital_send_cmd_complete, cmd); +		break; + +	default: +		pr_err("Unknown cmd type %d\n", cmd->type); +		return; +	} + +	if (!rc) +		return; + +	pr_err("in_send_command returned err %d\n", rc); + +	mutex_lock(&ddev->cmd_lock); +	list_del(&cmd->queue); +	mutex_unlock(&ddev->cmd_lock); + +	kfree_skb(cmd->req); +	kfree(cmd->mdaa_params); +	kfree(cmd); + +	schedule_work(&ddev->cmd_work); +} + +int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type, +		     struct sk_buff *skb, struct digital_tg_mdaa_params *params, +		     u16 timeout, nfc_digital_cmd_complete_t cmd_cb, +		     void *cb_context) +{ +	struct digital_cmd *cmd; + +	cmd = kzalloc(sizeof(struct digital_cmd), GFP_KERNEL); +	if (!cmd) +		return -ENOMEM; + +	cmd->type = cmd_type; +	cmd->timeout = timeout; +	cmd->req = skb; +	cmd->mdaa_params = params; +	cmd->cmd_cb = cmd_cb; +	cmd->cb_context = cb_context; +	INIT_LIST_HEAD(&cmd->queue); + +	mutex_lock(&ddev->cmd_lock); +	list_add_tail(&cmd->queue, &ddev->cmd_queue); +	mutex_unlock(&ddev->cmd_lock); + +	schedule_work(&ddev->cmd_work); + +	return 0; +} + +int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param) +{ +	int rc; + +	rc = ddev->ops->in_configure_hw(ddev, type, param); +	if (rc) +		pr_err("in_configure_hw failed: %d\n", rc); + +	return rc; +} + +int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param) +{ +	int rc; + +	rc = ddev->ops->tg_configure_hw(ddev, type, param); +	if (rc) +		pr_err("tg_configure_hw failed: %d\n", rc); + +	return rc; +} + +static int digital_tg_listen_mdaa(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	struct digital_tg_mdaa_params *params; + +	params = kzalloc(sizeof(struct digital_tg_mdaa_params), GFP_KERNEL); +	if (!params) +		return -ENOMEM; + +	params->sens_res = DIGITAL_SENS_RES_NFC_DEP; +	get_random_bytes(params->nfcid1, sizeof(params->nfcid1)); +	params->sel_res = DIGITAL_SEL_RES_NFC_DEP; + +	params->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1; +	params->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2; +	get_random_bytes(params->nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2); +	params->sc = DIGITAL_SENSF_FELICA_SC; + +	return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN_MDAA, NULL, params, +				500, digital_tg_recv_atr_req, NULL); +} + +int digital_target_found(struct nfc_digital_dev *ddev, +			 struct nfc_target *target, u8 protocol) +{ +	int rc; +	u8 framing; +	u8 rf_tech; +	int (*check_crc)(struct sk_buff *skb); +	void (*add_crc)(struct sk_buff *skb); + +	rf_tech = ddev->poll_techs[ddev->poll_tech_index].rf_tech; + +	switch (protocol) { +	case NFC_PROTO_JEWEL: +		framing = NFC_DIGITAL_FRAMING_NFCA_T1T; +		check_crc = digital_skb_check_crc_b; +		add_crc = digital_skb_add_crc_b; +		break; + +	case NFC_PROTO_MIFARE: +		framing = NFC_DIGITAL_FRAMING_NFCA_T2T; +		check_crc = digital_skb_check_crc_a; +		add_crc = digital_skb_add_crc_a; +		break; + +	case NFC_PROTO_FELICA: +		framing = NFC_DIGITAL_FRAMING_NFCF_T3T; +		check_crc = digital_skb_check_crc_f; +		add_crc = digital_skb_add_crc_f; +		break; + +	case NFC_PROTO_NFC_DEP: +		if (rf_tech == NFC_DIGITAL_RF_TECH_106A) { +			framing = NFC_DIGITAL_FRAMING_NFCA_NFC_DEP; +			check_crc = digital_skb_check_crc_a; +			add_crc = digital_skb_add_crc_a; +		} else { +			framing = NFC_DIGITAL_FRAMING_NFCF_NFC_DEP; +			check_crc = digital_skb_check_crc_f; +			add_crc = digital_skb_add_crc_f; +		} +		break; + +	case NFC_PROTO_ISO15693: +		framing = NFC_DIGITAL_FRAMING_ISO15693_T5T; +		check_crc = digital_skb_check_crc_b; +		add_crc = digital_skb_add_crc_b; +		break; + +	case NFC_PROTO_ISO14443: +		framing = NFC_DIGITAL_FRAMING_NFCA_T4T; +		check_crc = digital_skb_check_crc_a; +		add_crc = digital_skb_add_crc_a; +		break; + +	case NFC_PROTO_ISO14443_B: +		framing = NFC_DIGITAL_FRAMING_NFCB_T4T; +		check_crc = digital_skb_check_crc_b; +		add_crc = digital_skb_add_crc_b; +		break; + +	default: +		pr_err("Invalid protocol %d\n", protocol); +		return -EINVAL; +	} + +	pr_debug("rf_tech=%d, protocol=%d\n", rf_tech, protocol); + +	ddev->curr_rf_tech = rf_tech; + +	if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) { +		ddev->skb_add_crc = digital_skb_add_crc_none; +		ddev->skb_check_crc = digital_skb_check_crc_none; +	} else { +		ddev->skb_add_crc = add_crc; +		ddev->skb_check_crc = check_crc; +	} + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, framing); +	if (rc) +		return rc; + +	target->supported_protocols = (1 << protocol); +	rc = nfc_targets_found(ddev->nfc_dev, target, 1); +	if (rc) +		return rc; + +	ddev->poll_tech_count = 0; + +	return 0; +} + +void digital_poll_next_tech(struct nfc_digital_dev *ddev) +{ +	u8 rand_mod; + +	digital_switch_rf(ddev, 0); + +	mutex_lock(&ddev->poll_lock); + +	if (!ddev->poll_tech_count) { +		mutex_unlock(&ddev->poll_lock); +		return; +	} + +	get_random_bytes(&rand_mod, sizeof(rand_mod)); +	ddev->poll_tech_index = rand_mod % ddev->poll_tech_count; + +	mutex_unlock(&ddev->poll_lock); + +	schedule_work(&ddev->poll_work); +} + +static void digital_wq_poll(struct work_struct *work) +{ +	int rc; +	struct digital_poll_tech *poll_tech; +	struct nfc_digital_dev *ddev = container_of(work, +						    struct nfc_digital_dev, +						    poll_work); +	mutex_lock(&ddev->poll_lock); + +	if (!ddev->poll_tech_count) { +		mutex_unlock(&ddev->poll_lock); +		return; +	} + +	poll_tech = &ddev->poll_techs[ddev->poll_tech_index]; + +	mutex_unlock(&ddev->poll_lock); + +	rc = poll_tech->poll_func(ddev, poll_tech->rf_tech); +	if (rc) +		digital_poll_next_tech(ddev); +} + +static void digital_add_poll_tech(struct nfc_digital_dev *ddev, u8 rf_tech, +				  digital_poll_t poll_func) +{ +	struct digital_poll_tech *poll_tech; + +	if (ddev->poll_tech_count >= NFC_DIGITAL_POLL_MODE_COUNT_MAX) +		return; + +	poll_tech = &ddev->poll_techs[ddev->poll_tech_count++]; + +	poll_tech->rf_tech = rf_tech; +	poll_tech->poll_func = poll_func; +} + +/** + * start_poll operation + * + * For every supported protocol, the corresponding polling function is added + * to the table of polling technologies (ddev->poll_techs[]) using + * digital_add_poll_tech(). + * When a polling function fails (by timeout or protocol error) the next one is + * schedule by digital_poll_next_tech() on the poll workqueue (ddev->poll_work). + */ +static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols, +			      __u32 tm_protocols) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); +	u32 matching_im_protocols, matching_tm_protocols; + +	pr_debug("protocols: im 0x%x, tm 0x%x, supported 0x%x\n", im_protocols, +		 tm_protocols, ddev->protocols); + +	matching_im_protocols = ddev->protocols & im_protocols; +	matching_tm_protocols = ddev->protocols & tm_protocols; + +	if (!matching_im_protocols && !matching_tm_protocols) { +		pr_err("Unknown protocol\n"); +		return -EINVAL; +	} + +	if (ddev->poll_tech_count) { +		pr_err("Already polling\n"); +		return -EBUSY; +	} + +	if (ddev->curr_protocol) { +		pr_err("A target is already active\n"); +		return -EBUSY; +	} + +	ddev->poll_tech_count = 0; +	ddev->poll_tech_index = 0; + +	if (matching_im_protocols & DIGITAL_PROTO_NFCA_RF_TECH) +		digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A, +				      digital_in_send_sens_req); + +	if (matching_im_protocols & DIGITAL_PROTO_NFCB_RF_TECH) +		digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106B, +				      digital_in_send_sensb_req); + +	if (matching_im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) { +		digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F, +				      digital_in_send_sensf_req); + +		digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F, +				      digital_in_send_sensf_req); +	} + +	if (matching_im_protocols & DIGITAL_PROTO_ISO15693_RF_TECH) +		digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_ISO15693, +				      digital_in_send_iso15693_inv_req); + +	if (matching_tm_protocols & NFC_PROTO_NFC_DEP_MASK) { +		if (ddev->ops->tg_listen_mdaa) { +			digital_add_poll_tech(ddev, 0, +					      digital_tg_listen_mdaa); +		} else { +			digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A, +					      digital_tg_listen_nfca); + +			digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F, +					      digital_tg_listen_nfcf); + +			digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F, +					      digital_tg_listen_nfcf); +		} +	} + +	if (!ddev->poll_tech_count) { +		pr_err("Unsupported protocols: im=0x%x, tm=0x%x\n", +		       matching_im_protocols, matching_tm_protocols); +		return -EINVAL; +	} + +	schedule_work(&ddev->poll_work); + +	return 0; +} + +static void digital_stop_poll(struct nfc_dev *nfc_dev) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + +	mutex_lock(&ddev->poll_lock); + +	if (!ddev->poll_tech_count) { +		pr_err("Polling operation was not running\n"); +		mutex_unlock(&ddev->poll_lock); +		return; +	} + +	ddev->poll_tech_count = 0; + +	mutex_unlock(&ddev->poll_lock); + +	cancel_work_sync(&ddev->poll_work); + +	digital_abort_cmd(ddev); +} + +static int digital_dev_up(struct nfc_dev *nfc_dev) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + +	digital_switch_rf(ddev, 1); + +	return 0; +} + +static int digital_dev_down(struct nfc_dev *nfc_dev) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + +	digital_switch_rf(ddev, 0); + +	return 0; +} + +static int digital_dep_link_up(struct nfc_dev *nfc_dev, +			       struct nfc_target *target, +			       __u8 comm_mode, __u8 *gb, size_t gb_len) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); +	int rc; + +	rc = digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len); + +	if (!rc) +		ddev->curr_protocol = NFC_PROTO_NFC_DEP; + +	return rc; +} + +static int digital_dep_link_down(struct nfc_dev *nfc_dev) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + +	ddev->curr_protocol = 0; + +	return 0; +} + +static int digital_activate_target(struct nfc_dev *nfc_dev, +				   struct nfc_target *target, __u32 protocol) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + +	if (ddev->poll_tech_count) { +		pr_err("Can't activate a target while polling\n"); +		return -EBUSY; +	} + +	if (ddev->curr_protocol) { +		pr_err("A target is already active\n"); +		return -EBUSY; +	} + +	ddev->curr_protocol = protocol; + +	return 0; +} + +static void digital_deactivate_target(struct nfc_dev *nfc_dev, +				      struct nfc_target *target) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + +	if (!ddev->curr_protocol) { +		pr_err("No active target\n"); +		return; +	} + +	ddev->curr_protocol = 0; +} + +static int digital_tg_send(struct nfc_dev *dev, struct sk_buff *skb) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(dev); + +	return digital_tg_send_dep_res(ddev, skb); +} + +static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg, +				     struct sk_buff *resp) +{ +	struct digital_data_exch *data_exch = arg; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto done; +	} + +	if (ddev->curr_protocol == NFC_PROTO_MIFARE) { +		rc = digital_in_recv_mifare_res(resp); +		/* crc check is done in digital_in_recv_mifare_res() */ +		goto done; +	} + +	if ((ddev->curr_protocol == NFC_PROTO_ISO14443) || +	    (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) { +		rc = digital_in_iso_dep_pull_sod(ddev, resp); +		if (rc) +			goto done; +	} + +	rc = ddev->skb_check_crc(resp); + +done: +	if (rc) { +		kfree_skb(resp); +		resp = NULL; +	} + +	data_exch->cb(data_exch->cb_context, resp, rc); + +	kfree(data_exch); +} + +static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target, +			   struct sk_buff *skb, data_exchange_cb_t cb, +			   void *cb_context) +{ +	struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); +	struct digital_data_exch *data_exch; +	int rc; + +	data_exch = kzalloc(sizeof(struct digital_data_exch), GFP_KERNEL); +	if (!data_exch) { +		pr_err("Failed to allocate data_exch struct\n"); +		return -ENOMEM; +	} + +	data_exch->cb = cb; +	data_exch->cb_context = cb_context; + +	if (ddev->curr_protocol == NFC_PROTO_NFC_DEP) { +		rc = digital_in_send_dep_req(ddev, target, skb, data_exch); +		goto exit; +	} + +	if ((ddev->curr_protocol == NFC_PROTO_ISO14443) || +	    (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) { +		rc = digital_in_iso_dep_push_sod(ddev, skb); +		if (rc) +			goto exit; +	} + +	ddev->skb_add_crc(skb); + +	rc = digital_in_send_cmd(ddev, skb, 500, digital_in_send_complete, +				 data_exch); + +exit: +	if (rc) +		kfree(data_exch); + +	return rc; +} + +static struct nfc_ops digital_nfc_ops = { +	.dev_up = digital_dev_up, +	.dev_down = digital_dev_down, +	.start_poll = digital_start_poll, +	.stop_poll = digital_stop_poll, +	.dep_link_up = digital_dep_link_up, +	.dep_link_down = digital_dep_link_down, +	.activate_target = digital_activate_target, +	.deactivate_target = digital_deactivate_target, +	.tm_send = digital_tg_send, +	.im_transceive = digital_in_send, +}; + +struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops, +					    __u32 supported_protocols, +					    __u32 driver_capabilities, +					    int tx_headroom, int tx_tailroom) +{ +	struct nfc_digital_dev *ddev; + +	if (!ops->in_configure_hw || !ops->in_send_cmd || !ops->tg_listen || +	    !ops->tg_configure_hw || !ops->tg_send_cmd || !ops->abort_cmd || +	    !ops->switch_rf) +		return NULL; + +	ddev = kzalloc(sizeof(struct nfc_digital_dev), GFP_KERNEL); +	if (!ddev) +		return NULL; + +	ddev->driver_capabilities = driver_capabilities; +	ddev->ops = ops; + +	mutex_init(&ddev->cmd_lock); +	INIT_LIST_HEAD(&ddev->cmd_queue); + +	INIT_WORK(&ddev->cmd_work, digital_wq_cmd); +	INIT_WORK(&ddev->cmd_complete_work, digital_wq_cmd_complete); + +	mutex_init(&ddev->poll_lock); +	INIT_WORK(&ddev->poll_work, digital_wq_poll); + +	if (supported_protocols & NFC_PROTO_JEWEL_MASK) +		ddev->protocols |= NFC_PROTO_JEWEL_MASK; +	if (supported_protocols & NFC_PROTO_MIFARE_MASK) +		ddev->protocols |= NFC_PROTO_MIFARE_MASK; +	if (supported_protocols & NFC_PROTO_FELICA_MASK) +		ddev->protocols |= NFC_PROTO_FELICA_MASK; +	if (supported_protocols & NFC_PROTO_NFC_DEP_MASK) +		ddev->protocols |= NFC_PROTO_NFC_DEP_MASK; +	if (supported_protocols & NFC_PROTO_ISO15693_MASK) +		ddev->protocols |= NFC_PROTO_ISO15693_MASK; +	if (supported_protocols & NFC_PROTO_ISO14443_MASK) +		ddev->protocols |= NFC_PROTO_ISO14443_MASK; +	if (supported_protocols & NFC_PROTO_ISO14443_B_MASK) +		ddev->protocols |= NFC_PROTO_ISO14443_B_MASK; + +	ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN; +	ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN; + +	ddev->nfc_dev = nfc_allocate_device(&digital_nfc_ops, ddev->protocols, +					    ddev->tx_headroom, +					    ddev->tx_tailroom); +	if (!ddev->nfc_dev) { +		pr_err("nfc_allocate_device failed\n"); +		goto free_dev; +	} + +	nfc_set_drvdata(ddev->nfc_dev, ddev); + +	return ddev; + +free_dev: +	kfree(ddev); + +	return NULL; +} +EXPORT_SYMBOL(nfc_digital_allocate_device); + +void nfc_digital_free_device(struct nfc_digital_dev *ddev) +{ +	nfc_free_device(ddev->nfc_dev); +	kfree(ddev); +} +EXPORT_SYMBOL(nfc_digital_free_device); + +int nfc_digital_register_device(struct nfc_digital_dev *ddev) +{ +	return nfc_register_device(ddev->nfc_dev); +} +EXPORT_SYMBOL(nfc_digital_register_device); + +void nfc_digital_unregister_device(struct nfc_digital_dev *ddev) +{ +	struct digital_cmd *cmd, *n; + +	nfc_unregister_device(ddev->nfc_dev); + +	mutex_lock(&ddev->poll_lock); +	ddev->poll_tech_count = 0; +	mutex_unlock(&ddev->poll_lock); + +	cancel_work_sync(&ddev->poll_work); +	cancel_work_sync(&ddev->cmd_work); +	cancel_work_sync(&ddev->cmd_complete_work); + +	list_for_each_entry_safe(cmd, n, &ddev->cmd_queue, queue) { +		list_del(&cmd->queue); +		kfree(cmd->mdaa_params); +		kfree(cmd); +	} +} +EXPORT_SYMBOL(nfc_digital_unregister_device); + +MODULE_LICENSE("GPL"); diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c new file mode 100644 index 00000000000..171cb9949ab --- /dev/null +++ b/net/nfc/digital_dep.c @@ -0,0 +1,746 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) "digital: %s: " fmt, __func__ + +#include "digital.h" + +#define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4 +#define DIGITAL_NFC_DEP_FRAME_DIR_IN  0xD5 + +#define DIGITAL_NFC_DEP_NFCA_SOD_SB   0xF0 + +#define DIGITAL_CMD_ATR_REQ 0x00 +#define DIGITAL_CMD_ATR_RES 0x01 +#define DIGITAL_CMD_PSL_REQ 0x04 +#define DIGITAL_CMD_PSL_RES 0x05 +#define DIGITAL_CMD_DEP_REQ 0x06 +#define DIGITAL_CMD_DEP_RES 0x07 + +#define DIGITAL_ATR_REQ_MIN_SIZE 16 +#define DIGITAL_ATR_REQ_MAX_SIZE 64 + +#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30 +#define DIGITAL_GB_BIT	0x02 + +#define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0) + +#define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10 + +#define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \ +				((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT) +#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb)  ((pfb) & 0x10) +#define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08) +#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04) +#define DIGITAL_NFC_DEP_PFB_PNI(pfb)     ((pfb) & 0x03) + +#define DIGITAL_NFC_DEP_PFB_I_PDU          0x00 +#define DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU   0x40 +#define DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU 0x80 + +struct digital_atr_req { +	u8 dir; +	u8 cmd; +	u8 nfcid3[10]; +	u8 did; +	u8 bs; +	u8 br; +	u8 pp; +	u8 gb[0]; +} __packed; + +struct digital_atr_res { +	u8 dir; +	u8 cmd; +	u8 nfcid3[10]; +	u8 did; +	u8 bs; +	u8 br; +	u8 to; +	u8 pp; +	u8 gb[0]; +} __packed; + +struct digital_psl_req { +	u8 dir; +	u8 cmd; +	u8 did; +	u8 brs; +	u8 fsl; +} __packed; + +struct digital_psl_res { +	u8 dir; +	u8 cmd; +	u8 did; +} __packed; + +struct digital_dep_req_res { +	u8 dir; +	u8 cmd; +	u8 pfb; +} __packed; + +static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp); + +static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev, +				     struct sk_buff *skb) +{ +	skb_push(skb, sizeof(u8)); + +	skb->data[0] = skb->len; + +	if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A) +		*skb_push(skb, sizeof(u8)) = DIGITAL_NFC_DEP_NFCA_SOD_SB; +} + +static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev, +				    struct sk_buff *skb) +{ +	u8 size; + +	if (skb->len < 2) +		return -EIO; + +	if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A) +		skb_pull(skb, sizeof(u8)); + +	size = skb->data[0]; +	if (size != skb->len) +		return -EIO; + +	skb_pull(skb, sizeof(u8)); + +	return 0; +} + +static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, +				 struct sk_buff *resp) +{ +	struct nfc_target *target = arg; +	struct digital_atr_res *atr_res; +	u8 gb_len; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	rc = ddev->skb_check_crc(resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.6"); +		goto exit; +	} + +	rc = digital_skb_pull_dep_sod(ddev, resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.2"); +		goto exit; +	} + +	if (resp->len < sizeof(struct digital_atr_res)) { +		rc = -EIO; +		goto exit; +	} + +	gb_len = resp->len - sizeof(struct digital_atr_res); + +	atr_res = (struct digital_atr_res *)resp->data; + +	rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len); +	if (rc) +		goto exit; + +	rc = nfc_dep_link_is_up(ddev->nfc_dev, target->idx, NFC_COMM_ACTIVE, +				NFC_RF_INITIATOR); + +	ddev->curr_nfc_dep_pni = 0; + +exit: +	dev_kfree_skb(resp); + +	if (rc) +		ddev->curr_protocol = 0; +} + +int digital_in_send_atr_req(struct nfc_digital_dev *ddev, +			    struct nfc_target *target, __u8 comm_mode, __u8 *gb, +			    size_t gb_len) +{ +	struct sk_buff *skb; +	struct digital_atr_req *atr_req; +	uint size; + +	size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len; + +	if (size > DIGITAL_ATR_REQ_MAX_SIZE) { +		PROTOCOL_ERR("14.6.1.1"); +		return -EINVAL; +	} + +	skb = digital_skb_alloc(ddev, size); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, sizeof(struct digital_atr_req)); + +	atr_req = (struct digital_atr_req *)skb->data; +	memset(atr_req, 0, sizeof(struct digital_atr_req)); + +	atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; +	atr_req->cmd = DIGITAL_CMD_ATR_REQ; +	if (target->nfcid2_len) +		memcpy(atr_req->nfcid3, target->nfcid2, NFC_NFCID2_MAXSIZE); +	else +		get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE); + +	atr_req->did = 0; +	atr_req->bs = 0; +	atr_req->br = 0; + +	atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B; + +	if (gb_len) { +		atr_req->pp |= DIGITAL_GB_BIT; +		memcpy(skb_put(skb, gb_len), gb, gb_len); +	} + +	digital_skb_push_dep_sod(ddev, skb); + +	ddev->skb_add_crc(skb); + +	return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, +				   target); +} + +static int digital_in_send_rtox(struct nfc_digital_dev *ddev, +				struct digital_data_exch *data_exch, u8 rtox) +{ +	struct digital_dep_req_res *dep_req; +	struct sk_buff *skb; +	int rc; + +	skb = digital_skb_alloc(ddev, 1); +	if (!skb) +		return -ENOMEM; + +	*skb_put(skb, 1) = rtox; + +	skb_push(skb, sizeof(struct digital_dep_req_res)); + +	dep_req = (struct digital_dep_req_res *)skb->data; + +	dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; +	dep_req->cmd = DIGITAL_CMD_DEP_REQ; +	dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU | +		       DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT; + +	digital_skb_push_dep_sod(ddev, skb); + +	ddev->skb_add_crc(skb); + +	rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, +				 data_exch); + +	return rc; +} + +static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp) +{ +	struct digital_data_exch *data_exch = arg; +	struct digital_dep_req_res *dep_res; +	u8 pfb; +	uint size; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	rc = ddev->skb_check_crc(resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.6"); +		goto error; +	} + +	rc = digital_skb_pull_dep_sod(ddev, resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.2"); +		goto exit; +	} + +	dep_res = (struct digital_dep_req_res *)resp->data; + +	if (resp->len < sizeof(struct digital_dep_req_res) || +	    dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN || +	    dep_res->cmd != DIGITAL_CMD_DEP_RES) { +		rc = -EIO; +		goto error; +	} + +	pfb = dep_res->pfb; + +	switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) { +	case DIGITAL_NFC_DEP_PFB_I_PDU: +		if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) { +			PROTOCOL_ERR("14.12.3.3"); +			rc = -EIO; +			goto error; +		} + +		ddev->curr_nfc_dep_pni = +			DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); +		rc = 0; +		break; + +	case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: +		pr_err("Received a ACK/NACK PDU\n"); +		rc = -EIO; +		goto error; + +	case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: +		if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { +			rc = -EINVAL; +			goto error; +		} + +		rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]); +		if (rc) +			goto error; + +		kfree_skb(resp); +		return; +	} + +	if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) { +		pr_err("MI bit set. Chained PDU not supported\n"); +		rc = -EIO; +		goto error; +	} + +	size = sizeof(struct digital_dep_req_res); + +	if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) +		size++; + +	if (size > resp->len) { +		rc = -EIO; +		goto error; +	} + +	skb_pull(resp, size); + +exit: +	data_exch->cb(data_exch->cb_context, resp, rc); + +error: +	kfree(data_exch); + +	if (rc) +		kfree_skb(resp); +} + +int digital_in_send_dep_req(struct nfc_digital_dev *ddev, +			    struct nfc_target *target, struct sk_buff *skb, +			    struct digital_data_exch *data_exch) +{ +	struct digital_dep_req_res *dep_req; + +	skb_push(skb, sizeof(struct digital_dep_req_res)); + +	dep_req = (struct digital_dep_req_res *)skb->data; +	dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; +	dep_req->cmd = DIGITAL_CMD_DEP_REQ; +	dep_req->pfb = ddev->curr_nfc_dep_pni; + +	digital_skb_push_dep_sod(ddev, skb); + +	ddev->skb_add_crc(skb); + +	return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, +				   data_exch); +} + +static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	ddev->curr_rf_tech = rf_tech; + +	ddev->skb_add_crc = digital_skb_add_crc_none; +	ddev->skb_check_crc = digital_skb_check_crc_none; + +	if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) +		return; + +	switch (ddev->curr_rf_tech) { +	case NFC_DIGITAL_RF_TECH_106A: +		ddev->skb_add_crc = digital_skb_add_crc_a; +		ddev->skb_check_crc = digital_skb_check_crc_a; +		break; + +	case NFC_DIGITAL_RF_TECH_212F: +	case NFC_DIGITAL_RF_TECH_424F: +		ddev->skb_add_crc = digital_skb_add_crc_f; +		ddev->skb_check_crc = digital_skb_check_crc_f; +		break; + +	default: +		break; +	} +} + +static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp) +{ +	int rc; +	struct digital_dep_req_res *dep_req; +	size_t size; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	rc = ddev->skb_check_crc(resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.6"); +		goto exit; +	} + +	rc = digital_skb_pull_dep_sod(ddev, resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.2"); +		goto exit; +	} + +	size = sizeof(struct digital_dep_req_res); +	dep_req = (struct digital_dep_req_res *)resp->data; + +	if (resp->len < size || dep_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT || +	    dep_req->cmd != DIGITAL_CMD_DEP_REQ) { +		rc = -EIO; +		goto exit; +	} + +	if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb)) +		size++; + +	if (resp->len < size) { +		rc = -EIO; +		goto exit; +	} + +	switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) { +	case DIGITAL_NFC_DEP_PFB_I_PDU: +		pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n"); +		ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb); +		break; +	case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: +		pr_err("Received a ACK/NACK PDU\n"); +		rc = -EINVAL; +		goto exit; +		break; +	case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: +		pr_err("Received a SUPERVISOR PDU\n"); +		rc = -EINVAL; +		goto exit; +		break; +	} + +	skb_pull(resp, size); + +	rc = nfc_tm_data_received(ddev->nfc_dev, resp); + +exit: +	if (rc) +		kfree_skb(resp); +} + +int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb) +{ +	struct digital_dep_req_res *dep_res; + +	skb_push(skb, sizeof(struct digital_dep_req_res)); +	dep_res = (struct digital_dep_req_res *)skb->data; + +	dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; +	dep_res->cmd = DIGITAL_CMD_DEP_RES; +	dep_res->pfb = ddev->curr_nfc_dep_pni; + +	digital_skb_push_dep_sod(ddev, skb); + +	ddev->skb_add_crc(skb); + +	return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req, +				   NULL); +} + +static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev, +					     void *arg, struct sk_buff *resp) +{ +	u8 rf_tech = (unsigned long)arg; + +	if (IS_ERR(resp)) +		return; + +	digital_tg_set_rf_tech(ddev, rf_tech); + +	digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); + +	digital_tg_listen(ddev, 1500, digital_tg_recv_dep_req, NULL); + +	dev_kfree_skb(resp); +} + +static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did, +				   u8 rf_tech) +{ +	struct digital_psl_res *psl_res; +	struct sk_buff *skb; +	int rc; + +	skb = digital_skb_alloc(ddev, sizeof(struct digital_psl_res)); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, sizeof(struct digital_psl_res)); + +	psl_res = (struct digital_psl_res *)skb->data; + +	psl_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; +	psl_res->cmd = DIGITAL_CMD_PSL_RES; +	psl_res->did = did; + +	digital_skb_push_dep_sod(ddev, skb); + +	ddev->skb_add_crc(skb); + +	rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete, +				 (void *)(unsigned long)rf_tech); + +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp) +{ +	int rc; +	struct digital_psl_req *psl_req; +	u8 rf_tech; +	u8 dsi; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	rc = ddev->skb_check_crc(resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.6"); +		goto exit; +	} + +	rc = digital_skb_pull_dep_sod(ddev, resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.2"); +		goto exit; +	} + +	psl_req = (struct digital_psl_req *)resp->data; + +	if (resp->len != sizeof(struct digital_psl_req) || +	    psl_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT || +	    psl_req->cmd != DIGITAL_CMD_PSL_REQ) { +		rc = -EIO; +		goto exit; +	} + +	dsi = (psl_req->brs >> 3) & 0x07; +	switch (dsi) { +	case 0: +		rf_tech = NFC_DIGITAL_RF_TECH_106A; +		break; +	case 1: +		rf_tech = NFC_DIGITAL_RF_TECH_212F; +		break; +	case 2: +		rf_tech = NFC_DIGITAL_RF_TECH_424F; +		break; +	default: +		pr_err("Unsupported dsi value %d\n", dsi); +		goto exit; +	} + +	rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech); + +exit: +	kfree_skb(resp); +} + +static void digital_tg_send_atr_res_complete(struct nfc_digital_dev *ddev, +					     void *arg, struct sk_buff *resp) +{ +	int offset; + +	if (IS_ERR(resp)) { +		digital_poll_next_tech(ddev); +		return; +	} + +	offset = 2; +	if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) +		offset++; + +	if (resp->data[offset] == DIGITAL_CMD_PSL_REQ) +		digital_tg_recv_psl_req(ddev, arg, resp); +	else +		digital_tg_recv_dep_req(ddev, arg, resp); +} + +static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev, +				   struct digital_atr_req *atr_req) +{ +	struct digital_atr_res *atr_res; +	struct sk_buff *skb; +	u8 *gb; +	size_t gb_len; +	int rc; + +	gb = nfc_get_local_general_bytes(ddev->nfc_dev, &gb_len); +	if (!gb) +		gb_len = 0; + +	skb = digital_skb_alloc(ddev, sizeof(struct digital_atr_res) + gb_len); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, sizeof(struct digital_atr_res)); +	atr_res = (struct digital_atr_res *)skb->data; + +	memset(atr_res, 0, sizeof(struct digital_atr_res)); + +	atr_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; +	atr_res->cmd = DIGITAL_CMD_ATR_RES; +	memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3)); +	atr_res->to = 8; +	atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B; +	if (gb_len) { +		skb_put(skb, gb_len); + +		atr_res->pp |= DIGITAL_GB_BIT; +		memcpy(atr_res->gb, gb, gb_len); +	} + +	digital_skb_push_dep_sod(ddev, skb); + +	ddev->skb_add_crc(skb); + +	rc = digital_tg_send_cmd(ddev, skb, 999, +				 digital_tg_send_atr_res_complete, NULL); +	if (rc) { +		kfree_skb(skb); +		return rc; +	} + +	return rc; +} + +void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, +			     struct sk_buff *resp) +{ +	int rc; +	struct digital_atr_req *atr_req; +	size_t gb_len, min_size; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (!resp->len) { +		rc = -EIO; +		goto exit; +	} + +	if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) { +		min_size = DIGITAL_ATR_REQ_MIN_SIZE + 2; +		digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_106A); +	} else { +		min_size = DIGITAL_ATR_REQ_MIN_SIZE + 1; +		digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_212F); +	} + +	if (resp->len < min_size) { +		rc = -EIO; +		goto exit; +	} + +	ddev->curr_protocol = NFC_PROTO_NFC_DEP_MASK; + +	rc = ddev->skb_check_crc(resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.6"); +		goto exit; +	} + +	rc = digital_skb_pull_dep_sod(ddev, resp); +	if (rc) { +		PROTOCOL_ERR("14.4.1.2"); +		goto exit; +	} + +	atr_req = (struct digital_atr_req *)resp->data; + +	if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT || +	    atr_req->cmd != DIGITAL_CMD_ATR_REQ) { +		rc = -EINVAL; +		goto exit; +	} + +	rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED); +	if (rc) +		goto exit; + +	rc = digital_tg_send_atr_res(ddev, atr_req); +	if (rc) +		goto exit; + +	gb_len = resp->len - sizeof(struct digital_atr_req); +	rc = nfc_tm_activated(ddev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, +			      NFC_COMM_PASSIVE, atr_req->gb, gb_len); +	if (rc) +		goto exit; + +	ddev->poll_tech_count = 0; + +	rc = 0; +exit: +	if (rc) +		digital_poll_next_tech(ddev); + +	dev_kfree_skb(resp); +} diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c new file mode 100644 index 00000000000..c2c1c0189b7 --- /dev/null +++ b/net/nfc/digital_technology.c @@ -0,0 +1,1239 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) "digital: %s: " fmt, __func__ + +#include "digital.h" + +#define DIGITAL_CMD_SENS_REQ    0x26 +#define DIGITAL_CMD_ALL_REQ     0x52 +#define DIGITAL_CMD_SEL_REQ_CL1 0x93 +#define DIGITAL_CMD_SEL_REQ_CL2 0x95 +#define DIGITAL_CMD_SEL_REQ_CL3 0x97 + +#define DIGITAL_SDD_REQ_SEL_PAR 0x20 + +#define DIGITAL_SDD_RES_CT  0x88 +#define DIGITAL_SDD_RES_LEN 5 + +#define DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res) (!((sel_res) & 0x04)) +#define DIGITAL_SEL_RES_IS_T2T(sel_res) (!((sel_res) & 0x60)) +#define DIGITAL_SEL_RES_IS_T4T(sel_res) ((sel_res) & 0x20) +#define DIGITAL_SEL_RES_IS_NFC_DEP(sel_res) ((sel_res) & 0x40) + +#define DIGITAL_SENS_RES_IS_T1T(sens_res) (((sens_res) & 0x0C00) == 0x0C00) +#define DIGITAL_SENS_RES_IS_VALID(sens_res) \ +	((!((sens_res) & 0x001F) && (((sens_res) & 0x0C00) == 0x0C00)) || \ +	(((sens_res) & 0x001F) && ((sens_res) & 0x0C00) != 0x0C00)) + +#define DIGITAL_MIFARE_READ_RES_LEN 16 +#define DIGITAL_MIFARE_ACK_RES	0x0A + +#define DIGITAL_CMD_SENSB_REQ			0x05 +#define DIGITAL_SENSB_ADVANCED			BIT(5) +#define DIGITAL_SENSB_EXTENDED			BIT(4) +#define DIGITAL_SENSB_ALLB_REQ			BIT(3) +#define DIGITAL_SENSB_N(n)			((n) & 0x7) + +#define DIGITAL_CMD_SENSB_RES			0x50 + +#define DIGITAL_CMD_ATTRIB_REQ			0x1D +#define DIGITAL_ATTRIB_P1_TR0_DEFAULT		(0x0 << 6) +#define DIGITAL_ATTRIB_P1_TR1_DEFAULT		(0x0 << 4) +#define DIGITAL_ATTRIB_P1_SUPRESS_EOS		BIT(3) +#define DIGITAL_ATTRIB_P1_SUPRESS_SOS		BIT(2) +#define DIGITAL_ATTRIB_P2_LISTEN_POLL_1		(0x0 << 6) +#define DIGITAL_ATTRIB_P2_POLL_LISTEN_1		(0x0 << 4) +#define DIGITAL_ATTRIB_P2_MAX_FRAME_256		0x8 +#define DIGITAL_ATTRIB_P4_DID(n)		((n) & 0xf) + +#define DIGITAL_CMD_SENSF_REQ	0x00 +#define DIGITAL_CMD_SENSF_RES	0x01 + +#define DIGITAL_SENSF_RES_MIN_LENGTH 17 +#define DIGITAL_SENSF_RES_RD_AP_B1   0x00 +#define DIGITAL_SENSF_RES_RD_AP_B2   0x8F + +#define DIGITAL_SENSF_REQ_RC_NONE 0 +#define DIGITAL_SENSF_REQ_RC_SC   1 +#define DIGITAL_SENSF_REQ_RC_AP   2 + +#define DIGITAL_CMD_ISO15693_INVENTORY_REQ	0x01 + +#define DIGITAL_ISO15693_REQ_FLAG_DATA_RATE	BIT(1) +#define DIGITAL_ISO15693_REQ_FLAG_INVENTORY	BIT(2) +#define DIGITAL_ISO15693_REQ_FLAG_NB_SLOTS	BIT(5) +#define DIGITAL_ISO15693_RES_FLAG_ERROR		BIT(0) +#define DIGITAL_ISO15693_RES_IS_VALID(flags) \ +	(!((flags) & DIGITAL_ISO15693_RES_FLAG_ERROR)) + +#define DIGITAL_ISO_DEP_I_PCB	 0x02 +#define DIGITAL_ISO_DEP_PNI(pni) ((pni) & 0x01) + +#define DIGITAL_ISO_DEP_PCB_TYPE(pcb) ((pcb) & 0xC0) + +#define DIGITAL_ISO_DEP_I_BLOCK 0x00 + +#define DIGITAL_ISO_DEP_BLOCK_HAS_DID(pcb) ((pcb) & 0x08) + +static const u8 digital_ats_fsc[] = { +	 16,  24,  32,  40,  48,  64,  96, 128, +}; + +#define DIGITAL_ATS_FSCI(t0) ((t0) & 0x0F) +#define DIGITAL_SENSB_FSCI(pi2) (((pi2) & 0xF0) >> 4) +#define DIGITAL_ATS_MAX_FSC  256 + +#define DIGITAL_RATS_BYTE1 0xE0 +#define DIGITAL_RATS_PARAM 0x80 + +struct digital_sdd_res { +	u8 nfcid1[4]; +	u8 bcc; +} __packed; + +struct digital_sel_req { +	u8 sel_cmd; +	u8 b2; +	u8 nfcid1[4]; +	u8 bcc; +} __packed; + +struct digital_sensb_req { +	u8 cmd; +	u8 afi; +	u8 param; +} __packed; + +struct digital_sensb_res { +	u8 cmd; +	u8 nfcid0[4]; +	u8 app_data[4]; +	u8 proto_info[3]; +} __packed; + +struct digital_attrib_req { +	u8 cmd; +	u8 nfcid0[4]; +	u8 param1; +	u8 param2; +	u8 param3; +	u8 param4; +} __packed; + +struct digital_attrib_res { +	u8 mbli_did; +} __packed; + +struct digital_sensf_req { +	u8 cmd; +	u8 sc1; +	u8 sc2; +	u8 rc; +	u8 tsn; +} __packed; + +struct digital_sensf_res { +	u8 cmd; +	u8 nfcid2[8]; +	u8 pad0[2]; +	u8 pad1[3]; +	u8 mrti_check; +	u8 mrti_update; +	u8 pad2; +	u8 rd[2]; +} __packed; + +struct digital_iso15693_inv_req { +	u8 flags; +	u8 cmd; +	u8 mask_len; +	u64 mask; +} __packed; + +struct digital_iso15693_inv_res { +	u8 flags; +	u8 dsfid; +	u64 uid; +} __packed; + +static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev, +				   struct nfc_target *target); + +int digital_in_iso_dep_pull_sod(struct nfc_digital_dev *ddev, +				struct sk_buff *skb) +{ +	u8 pcb; +	u8 block_type; + +	if (skb->len < 1) +		return -EIO; + +	pcb = *skb->data; +	block_type = DIGITAL_ISO_DEP_PCB_TYPE(pcb); + +	/* No support fo R-block nor S-block */ +	if (block_type != DIGITAL_ISO_DEP_I_BLOCK) { +		pr_err("ISO_DEP R-block and S-block not supported\n"); +		return -EIO; +	} + +	if (DIGITAL_ISO_DEP_BLOCK_HAS_DID(pcb)) { +		pr_err("DID field in ISO_DEP PCB not supported\n"); +		return -EIO; +	} + +	skb_pull(skb, 1); + +	return 0; +} + +int digital_in_iso_dep_push_sod(struct nfc_digital_dev *ddev, +				struct sk_buff *skb) +{ +	/* +	 * Chaining not supported so skb->len + 1 PCB byte + 2 CRC bytes must +	 * not be greater than remote FSC +	 */ +	if (skb->len + 3 > ddev->target_fsc) +		return -EIO; + +	skb_push(skb, 1); + +	*skb->data = DIGITAL_ISO_DEP_I_PCB | ddev->curr_nfc_dep_pni; + +	ddev->curr_nfc_dep_pni = +		DIGITAL_ISO_DEP_PNI(ddev->curr_nfc_dep_pni + 1); + +	return 0; +} + +static void digital_in_recv_ats(struct nfc_digital_dev *ddev, void *arg, +				struct sk_buff *resp) +{ +	struct nfc_target *target = arg; +	u8 fsdi; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (resp->len < 2) { +		rc = -EIO; +		goto exit; +	} + +	fsdi = DIGITAL_ATS_FSCI(resp->data[1]); +	if (fsdi >= 8) +		ddev->target_fsc = DIGITAL_ATS_MAX_FSC; +	else +		ddev->target_fsc = digital_ats_fsc[fsdi]; + +	ddev->curr_nfc_dep_pni = 0; + +	rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443); + +exit: +	dev_kfree_skb(resp); +	kfree(target); + +	if (rc) +		digital_poll_next_tech(ddev); +} + +static int digital_in_send_rats(struct nfc_digital_dev *ddev, +				struct nfc_target *target) +{ +	int rc; +	struct sk_buff *skb; + +	skb = digital_skb_alloc(ddev, 2); +	if (!skb) +		return -ENOMEM; + +	*skb_put(skb, 1) = DIGITAL_RATS_BYTE1; +	*skb_put(skb, 1) = DIGITAL_RATS_PARAM; + +	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_ats, +				 target); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp) +{ +	struct nfc_target *target = arg; +	int rc; +	u8 sel_res; +	u8 nfc_proto; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) { +		rc = digital_skb_check_crc_a(resp); +		if (rc) { +			PROTOCOL_ERR("4.4.1.3"); +			goto exit; +		} +	} + +	if (!resp->len) { +		rc = -EIO; +		goto exit; +	} + +	sel_res = resp->data[0]; + +	if (!DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res)) { +		rc = digital_in_send_sdd_req(ddev, target); +		if (rc) +			goto exit; + +		goto exit_free_skb; +	} + +	target->sel_res = sel_res; + +	if (DIGITAL_SEL_RES_IS_T2T(sel_res)) { +		nfc_proto = NFC_PROTO_MIFARE; +	} else if (DIGITAL_SEL_RES_IS_T4T(sel_res)) { +		rc = digital_in_send_rats(ddev, target); +		if (rc) +			goto exit; +		/* +		 * Skip target_found and don't free it for now. This will be +		 * done when receiving the ATS +		 */ +		goto exit_free_skb; +	} else if (DIGITAL_SEL_RES_IS_NFC_DEP(sel_res)) { +		nfc_proto = NFC_PROTO_NFC_DEP; +	} else { +		rc = -EOPNOTSUPP; +		goto exit; +	} + +	rc = digital_target_found(ddev, target, nfc_proto); + +exit: +	kfree(target); + +exit_free_skb: +	dev_kfree_skb(resp); + +	if (rc) +		digital_poll_next_tech(ddev); +} + +static int digital_in_send_sel_req(struct nfc_digital_dev *ddev, +				   struct nfc_target *target, +				   struct digital_sdd_res *sdd_res) +{ +	struct sk_buff *skb; +	struct digital_sel_req *sel_req; +	u8 sel_cmd; +	int rc; + +	skb = digital_skb_alloc(ddev, sizeof(struct digital_sel_req)); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, sizeof(struct digital_sel_req)); +	sel_req = (struct digital_sel_req *)skb->data; + +	if (target->nfcid1_len <= 4) +		sel_cmd = DIGITAL_CMD_SEL_REQ_CL1; +	else if (target->nfcid1_len < 10) +		sel_cmd = DIGITAL_CMD_SEL_REQ_CL2; +	else +		sel_cmd = DIGITAL_CMD_SEL_REQ_CL3; + +	sel_req->sel_cmd = sel_cmd; +	sel_req->b2 = 0x70; +	memcpy(sel_req->nfcid1, sdd_res->nfcid1, 4); +	sel_req->bcc = sdd_res->bcc; + +	if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) { +		rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A); +		if (rc) +			goto exit; +	} else { +		digital_skb_add_crc_a(skb); +	} + +	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sel_res, +				 target); +exit: +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_in_recv_sdd_res(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp) +{ +	struct nfc_target *target = arg; +	struct digital_sdd_res *sdd_res; +	int rc; +	u8 offset, size; +	u8 i, bcc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (resp->len < DIGITAL_SDD_RES_LEN) { +		PROTOCOL_ERR("4.7.2.8"); +		rc = -EINVAL; +		goto exit; +	} + +	sdd_res = (struct digital_sdd_res *)resp->data; + +	for (i = 0, bcc = 0; i < 4; i++) +		bcc ^= sdd_res->nfcid1[i]; + +	if (bcc != sdd_res->bcc) { +		PROTOCOL_ERR("4.7.2.6"); +		rc = -EINVAL; +		goto exit; +	} + +	if (sdd_res->nfcid1[0] == DIGITAL_SDD_RES_CT) { +		offset = 1; +		size = 3; +	} else { +		offset = 0; +		size = 4; +	} + +	memcpy(target->nfcid1 + target->nfcid1_len, sdd_res->nfcid1 + offset, +	       size); +	target->nfcid1_len += size; + +	rc = digital_in_send_sel_req(ddev, target, sdd_res); + +exit: +	dev_kfree_skb(resp); + +	if (rc) { +		kfree(target); +		digital_poll_next_tech(ddev); +	} +} + +static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev, +				   struct nfc_target *target) +{ +	int rc; +	struct sk_buff *skb; +	u8 sel_cmd; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_NFCA_STANDARD); +	if (rc) +		return rc; + +	skb = digital_skb_alloc(ddev, 2); +	if (!skb) +		return -ENOMEM; + +	if (target->nfcid1_len == 0) +		sel_cmd = DIGITAL_CMD_SEL_REQ_CL1; +	else if (target->nfcid1_len == 3) +		sel_cmd = DIGITAL_CMD_SEL_REQ_CL2; +	else +		sel_cmd = DIGITAL_CMD_SEL_REQ_CL3; + +	*skb_put(skb, sizeof(u8)) = sel_cmd; +	*skb_put(skb, sizeof(u8)) = DIGITAL_SDD_REQ_SEL_PAR; + +	return digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sdd_res, +				   target); +} + +static void digital_in_recv_sens_res(struct nfc_digital_dev *ddev, void *arg, +				     struct sk_buff *resp) +{ +	struct nfc_target *target = NULL; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (resp->len < sizeof(u16)) { +		rc = -EIO; +		goto exit; +	} + +	target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL); +	if (!target) { +		rc = -ENOMEM; +		goto exit; +	} + +	target->sens_res = __le16_to_cpu(*(__le16 *)resp->data); + +	if (!DIGITAL_SENS_RES_IS_VALID(target->sens_res)) { +		PROTOCOL_ERR("4.6.3.3"); +		rc = -EINVAL; +		goto exit; +	} + +	if (DIGITAL_SENS_RES_IS_T1T(target->sens_res)) +		rc = digital_target_found(ddev, target, NFC_PROTO_JEWEL); +	else +		rc = digital_in_send_sdd_req(ddev, target); + +exit: +	dev_kfree_skb(resp); + +	if (rc) { +		kfree(target); +		digital_poll_next_tech(ddev); +	} +} + +int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	struct sk_buff *skb; +	int rc; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, +				     NFC_DIGITAL_RF_TECH_106A); +	if (rc) +		return rc; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_NFCA_SHORT); +	if (rc) +		return rc; + +	skb = digital_skb_alloc(ddev, 1); +	if (!skb) +		return -ENOMEM; + +	*skb_put(skb, sizeof(u8)) = DIGITAL_CMD_SENS_REQ; + +	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sens_res, NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +int digital_in_recv_mifare_res(struct sk_buff *resp) +{ +	/* Successful READ command response is 16 data bytes + 2 CRC bytes long. +	 * Since the driver can't differentiate a ACK/NACK response from a valid +	 * READ response, the CRC calculation must be handled at digital level +	 * even if the driver supports it for this technology. +	 */ +	if (resp->len == DIGITAL_MIFARE_READ_RES_LEN + DIGITAL_CRC_LEN) { +		if (digital_skb_check_crc_a(resp)) { +			PROTOCOL_ERR("9.4.1.2"); +			return -EIO; +		} + +		return 0; +	} + +	/* ACK response (i.e. successful WRITE). */ +	if (resp->len == 1 && resp->data[0] == DIGITAL_MIFARE_ACK_RES) { +		resp->data[0] = 0; +		return 0; +	} + +	/* NACK and any other responses are treated as error. */ +	return -EIO; +} + +static void digital_in_recv_attrib_res(struct nfc_digital_dev *ddev, void *arg, +				       struct sk_buff *resp) +{ +	struct nfc_target *target = arg; +	struct digital_attrib_res *attrib_res; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (resp->len < sizeof(*attrib_res)) { +		PROTOCOL_ERR("12.6.2"); +		rc = -EIO; +		goto exit; +	} + +	attrib_res = (struct digital_attrib_res *)resp->data; + +	if (attrib_res->mbli_did & 0x0f) { +		PROTOCOL_ERR("12.6.2.1"); +		rc = -EIO; +		goto exit; +	} + +	rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443_B); + +exit: +	dev_kfree_skb(resp); +	kfree(target); + +	if (rc) +		digital_poll_next_tech(ddev); +} + +static int digital_in_send_attrib_req(struct nfc_digital_dev *ddev, +			       struct nfc_target *target, +			       struct digital_sensb_res *sensb_res) +{ +	struct digital_attrib_req *attrib_req; +	struct sk_buff *skb; +	int rc; + +	skb = digital_skb_alloc(ddev, sizeof(*attrib_req)); +	if (!skb) +		return -ENOMEM; + +	attrib_req = (struct digital_attrib_req *)skb_put(skb, +							  sizeof(*attrib_req)); + +	attrib_req->cmd = DIGITAL_CMD_ATTRIB_REQ; +	memcpy(attrib_req->nfcid0, sensb_res->nfcid0, +	       sizeof(attrib_req->nfcid0)); +	attrib_req->param1 = DIGITAL_ATTRIB_P1_TR0_DEFAULT | +			     DIGITAL_ATTRIB_P1_TR1_DEFAULT; +	attrib_req->param2 = DIGITAL_ATTRIB_P2_LISTEN_POLL_1 | +			     DIGITAL_ATTRIB_P2_POLL_LISTEN_1 | +			     DIGITAL_ATTRIB_P2_MAX_FRAME_256; +	attrib_req->param3 = sensb_res->proto_info[1] & 0x07; +	attrib_req->param4 = DIGITAL_ATTRIB_P4_DID(0); + +	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_attrib_res, +				 target); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_in_recv_sensb_res(struct nfc_digital_dev *ddev, void *arg, +				      struct sk_buff *resp) +{ +	struct nfc_target *target = NULL; +	struct digital_sensb_res *sensb_res; +	u8 fsci; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (resp->len != sizeof(*sensb_res)) { +		PROTOCOL_ERR("5.6.2.1"); +		rc = -EIO; +		goto exit; +	} + +	sensb_res = (struct digital_sensb_res *)resp->data; + +	if (sensb_res->cmd != DIGITAL_CMD_SENSB_RES) { +		PROTOCOL_ERR("5.6.2"); +		rc = -EIO; +		goto exit; +	} + +	if (!(sensb_res->proto_info[1] & BIT(0))) { +		PROTOCOL_ERR("5.6.2.12"); +		rc = -EIO; +		goto exit; +	} + +	if (sensb_res->proto_info[1] & BIT(3)) { +		PROTOCOL_ERR("5.6.2.16"); +		rc = -EIO; +		goto exit; +	} + +	fsci = DIGITAL_SENSB_FSCI(sensb_res->proto_info[1]); +	if (fsci >= 8) +		ddev->target_fsc = DIGITAL_ATS_MAX_FSC; +	else +		ddev->target_fsc = digital_ats_fsc[fsci]; + +	target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL); +	if (!target) { +		rc = -ENOMEM; +		goto exit; +	} + +	rc = digital_in_send_attrib_req(ddev, target, sensb_res); + +exit: +	dev_kfree_skb(resp); + +	if (rc) { +		kfree(target); +		digital_poll_next_tech(ddev); +	} +} + +int digital_in_send_sensb_req(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	struct digital_sensb_req *sensb_req; +	struct sk_buff *skb; +	int rc; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, +				     NFC_DIGITAL_RF_TECH_106B); +	if (rc) +		return rc; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_NFCB); +	if (rc) +		return rc; + +	skb = digital_skb_alloc(ddev, sizeof(*sensb_req)); +	if (!skb) +		return -ENOMEM; + +	sensb_req = (struct digital_sensb_req *)skb_put(skb, +							sizeof(*sensb_req)); + +	sensb_req->cmd = DIGITAL_CMD_SENSB_REQ; +	sensb_req->afi = 0x00; /* All families and sub-families */ +	sensb_req->param = DIGITAL_SENSB_N(0); + +	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensb_res, +				 NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg, +				   struct sk_buff *resp) +{ +	int rc; +	u8 proto; +	struct nfc_target target; +	struct digital_sensf_res *sensf_res; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (resp->len < DIGITAL_SENSF_RES_MIN_LENGTH) { +		rc = -EIO; +		goto exit; +	} + +	if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) { +		rc = digital_skb_check_crc_f(resp); +		if (rc) { +			PROTOCOL_ERR("6.4.1.8"); +			goto exit; +		} +	} + +	skb_pull(resp, 1); + +	memset(&target, 0, sizeof(struct nfc_target)); + +	sensf_res = (struct digital_sensf_res *)resp->data; + +	memcpy(target.sensf_res, sensf_res, resp->len); +	target.sensf_res_len = resp->len; + +	memcpy(target.nfcid2, sensf_res->nfcid2, NFC_NFCID2_MAXSIZE); +	target.nfcid2_len = NFC_NFCID2_MAXSIZE; + +	if (target.nfcid2[0] == DIGITAL_SENSF_NFCID2_NFC_DEP_B1 && +	    target.nfcid2[1] == DIGITAL_SENSF_NFCID2_NFC_DEP_B2) +		proto = NFC_PROTO_NFC_DEP; +	else +		proto = NFC_PROTO_FELICA; + +	rc = digital_target_found(ddev, &target, proto); + +exit: +	dev_kfree_skb(resp); + +	if (rc) +		digital_poll_next_tech(ddev); +} + +int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	struct digital_sensf_req *sensf_req; +	struct sk_buff *skb; +	int rc; +	u8 size; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); +	if (rc) +		return rc; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_NFCF); +	if (rc) +		return rc; + +	size = sizeof(struct digital_sensf_req); + +	skb = digital_skb_alloc(ddev, size); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, size); + +	sensf_req = (struct digital_sensf_req *)skb->data; +	sensf_req->cmd = DIGITAL_CMD_SENSF_REQ; +	sensf_req->sc1 = 0xFF; +	sensf_req->sc2 = 0xFF; +	sensf_req->rc = 0; +	sensf_req->tsn = 0; + +	*skb_push(skb, 1) = size + 1; + +	if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) +		digital_skb_add_crc_f(skb); + +	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensf_res, +				 NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_in_recv_iso15693_inv_res(struct nfc_digital_dev *ddev, +		void *arg, struct sk_buff *resp) +{ +	struct digital_iso15693_inv_res *res; +	struct nfc_target *target = NULL; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto out_free_skb; +	} + +	if (resp->len != sizeof(*res)) { +		rc = -EIO; +		goto out_free_skb; +	} + +	res = (struct digital_iso15693_inv_res *)resp->data; + +	if (!DIGITAL_ISO15693_RES_IS_VALID(res->flags)) { +		PROTOCOL_ERR("ISO15693 - 10.3.1"); +		rc = -EINVAL; +		goto out_free_skb; +	} + +	target = kzalloc(sizeof(*target), GFP_KERNEL); +	if (!target) { +		rc = -ENOMEM; +		goto out_free_skb; +	} + +	target->is_iso15693 = 1; +	target->iso15693_dsfid = res->dsfid; +	memcpy(target->iso15693_uid, &res->uid, sizeof(target->iso15693_uid)); + +	rc = digital_target_found(ddev, target, NFC_PROTO_ISO15693); + +	kfree(target); + +out_free_skb: +	dev_kfree_skb(resp); + +	if (rc) +		digital_poll_next_tech(ddev); +} + +int digital_in_send_iso15693_inv_req(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	struct digital_iso15693_inv_req *req; +	struct sk_buff *skb; +	int rc; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, +				     NFC_DIGITAL_RF_TECH_ISO15693); +	if (rc) +		return rc; + +	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_ISO15693_INVENTORY); +	if (rc) +		return rc; + +	skb = digital_skb_alloc(ddev, sizeof(*req)); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, sizeof(*req) - sizeof(req->mask)); /* No mask */ +	req = (struct digital_iso15693_inv_req *)skb->data; + +	/* Single sub-carrier, high data rate, no AFI, single slot +	 * Inventory command +	 */ +	req->flags = DIGITAL_ISO15693_REQ_FLAG_DATA_RATE | +		     DIGITAL_ISO15693_REQ_FLAG_INVENTORY | +		     DIGITAL_ISO15693_REQ_FLAG_NB_SLOTS; +	req->cmd = DIGITAL_CMD_ISO15693_INVENTORY_REQ; +	req->mask_len = 0; + +	rc = digital_in_send_cmd(ddev, skb, 30, +				 digital_in_recv_iso15693_inv_res, NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static int digital_tg_send_sel_res(struct nfc_digital_dev *ddev) +{ +	struct sk_buff *skb; +	int rc; + +	skb = digital_skb_alloc(ddev, 1); +	if (!skb) +		return -ENOMEM; + +	*skb_put(skb, 1) = DIGITAL_SEL_RES_NFC_DEP; + +	if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) +		digital_skb_add_crc_a(skb); + +	rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_atr_req, +				 NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_tg_recv_sel_req(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp) +{ +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) { +		rc = digital_skb_check_crc_a(resp); +		if (rc) { +			PROTOCOL_ERR("4.4.1.3"); +			goto exit; +		} +	} + +	/* Silently ignore SEL_REQ content and send a SEL_RES for NFC-DEP */ + +	rc = digital_tg_send_sel_res(ddev); + +exit: +	if (rc) +		digital_poll_next_tech(ddev); + +	dev_kfree_skb(resp); +} + +static int digital_tg_send_sdd_res(struct nfc_digital_dev *ddev) +{ +	struct sk_buff *skb; +	struct digital_sdd_res *sdd_res; +	int rc, i; + +	skb = digital_skb_alloc(ddev, sizeof(struct digital_sdd_res)); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, sizeof(struct digital_sdd_res)); +	sdd_res = (struct digital_sdd_res *)skb->data; + +	sdd_res->nfcid1[0] = 0x08; +	get_random_bytes(sdd_res->nfcid1 + 1, 3); + +	sdd_res->bcc = 0; +	for (i = 0; i < 4; i++) +		sdd_res->bcc ^= sdd_res->nfcid1[i]; + +	rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sel_req, +				 NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +static void digital_tg_recv_sdd_req(struct nfc_digital_dev *ddev, void *arg, +				    struct sk_buff *resp) +{ +	u8 *sdd_req; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	sdd_req = resp->data; + +	if (resp->len < 2 || sdd_req[0] != DIGITAL_CMD_SEL_REQ_CL1 || +	    sdd_req[1] != DIGITAL_SDD_REQ_SEL_PAR) { +		rc = -EINVAL; +		goto exit; +	} + +	rc = digital_tg_send_sdd_res(ddev); + +exit: +	if (rc) +		digital_poll_next_tech(ddev); + +	dev_kfree_skb(resp); +} + +static int digital_tg_send_sens_res(struct nfc_digital_dev *ddev) +{ +	struct sk_buff *skb; +	u8 *sens_res; +	int rc; + +	skb = digital_skb_alloc(ddev, 2); +	if (!skb) +		return -ENOMEM; + +	sens_res = skb_put(skb, 2); + +	sens_res[0] = (DIGITAL_SENS_RES_NFC_DEP >> 8) & 0xFF; +	sens_res[1] = DIGITAL_SENS_RES_NFC_DEP & 0xFF; + +	rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sdd_req, +				 NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg, +			      struct sk_buff *resp) +{ +	u8 sens_req; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	sens_req = resp->data[0]; + +	if (!resp->len || (sens_req != DIGITAL_CMD_SENS_REQ && +	    sens_req != DIGITAL_CMD_ALL_REQ)) { +		rc = -EINVAL; +		goto exit; +	} + +	rc = digital_tg_send_sens_res(ddev); + +exit: +	if (rc) +		digital_poll_next_tech(ddev); + +	dev_kfree_skb(resp); +} + +static void digital_tg_recv_atr_or_sensf_req(struct nfc_digital_dev *ddev, +		void *arg, struct sk_buff *resp) +{ +	if (!IS_ERR(resp) && (resp->len >= 2) && +			(resp->data[1] == DIGITAL_CMD_SENSF_REQ)) +		digital_tg_recv_sensf_req(ddev, arg, resp); +	else +		digital_tg_recv_atr_req(ddev, arg, resp); + +	return; +} + +static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev, +			      struct digital_sensf_req *sensf_req) +{ +	struct sk_buff *skb; +	u8 size; +	int rc; +	struct digital_sensf_res *sensf_res; + +	size = sizeof(struct digital_sensf_res); + +	if (sensf_req->rc == DIGITAL_SENSF_REQ_RC_NONE) +		size -= sizeof(sensf_res->rd); + +	skb = digital_skb_alloc(ddev, size); +	if (!skb) +		return -ENOMEM; + +	skb_put(skb, size); + +	sensf_res = (struct digital_sensf_res *)skb->data; + +	memset(sensf_res, 0, size); + +	sensf_res->cmd = DIGITAL_CMD_SENSF_RES; +	sensf_res->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1; +	sensf_res->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2; +	get_random_bytes(&sensf_res->nfcid2[2], 6); + +	switch (sensf_req->rc) { +	case DIGITAL_SENSF_REQ_RC_SC: +		sensf_res->rd[0] = sensf_req->sc1; +		sensf_res->rd[1] = sensf_req->sc2; +		break; +	case DIGITAL_SENSF_REQ_RC_AP: +		sensf_res->rd[0] = DIGITAL_SENSF_RES_RD_AP_B1; +		sensf_res->rd[1] = DIGITAL_SENSF_RES_RD_AP_B2; +		break; +	} + +	*skb_push(skb, sizeof(u8)) = size + 1; + +	if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) +		digital_skb_add_crc_f(skb); + +	rc = digital_tg_send_cmd(ddev, skb, 300, +				 digital_tg_recv_atr_or_sensf_req, NULL); +	if (rc) +		kfree_skb(skb); + +	return rc; +} + +void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg, +			       struct sk_buff *resp) +{ +	struct digital_sensf_req *sensf_req; +	int rc; + +	if (IS_ERR(resp)) { +		rc = PTR_ERR(resp); +		resp = NULL; +		goto exit; +	} + +	if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) { +		rc = digital_skb_check_crc_f(resp); +		if (rc) { +			PROTOCOL_ERR("6.4.1.8"); +			goto exit; +		} +	} + +	if (resp->len != sizeof(struct digital_sensf_req) + 1) { +		rc = -EINVAL; +		goto exit; +	} + +	skb_pull(resp, 1); +	sensf_req = (struct digital_sensf_req *)resp->data; + +	if (sensf_req->cmd != DIGITAL_CMD_SENSF_REQ) { +		rc = -EINVAL; +		goto exit; +	} + +	rc = digital_tg_send_sensf_res(ddev, sensf_req); + +exit: +	if (rc) +		digital_poll_next_tech(ddev); + +	dev_kfree_skb(resp); +} + +int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	int rc; + +	rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); +	if (rc) +		return rc; + +	rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); +	if (rc) +		return rc; + +	return digital_tg_listen(ddev, 300, digital_tg_recv_sens_req, NULL); +} + +int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) +{ +	int rc; +	u8 *nfcid2; + +	rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); +	if (rc) +		return rc; + +	rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, +				     NFC_DIGITAL_FRAMING_NFCF_NFC_DEP); +	if (rc) +		return rc; + +	nfcid2 = kzalloc(NFC_NFCID2_MAXSIZE, GFP_KERNEL); +	if (!nfcid2) +		return -ENOMEM; + +	nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1; +	nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2; +	get_random_bytes(nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2); + +	return digital_tg_listen(ddev, 300, digital_tg_recv_sensf_req, nfcid2); +} diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 64f922be928..677d24bb70f 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) "hci: %s: " fmt, __func__ @@ -28,6 +26,8 @@  #include "hci.h" +#define MAX_FWI 4949 +  static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,  			       const u8 *param, size_t param_len,  			       data_exchange_cb_t cb, void *cb_context) @@ -39,7 +39,7 @@ static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,  	 * for all commands?  	 */  	return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd, -				      param, param_len, cb, cb_context, 3000); +				      param, param_len, cb, cb_context, MAX_FWI);  }  /* @@ -84,7 +84,7 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,  						    NFC_HCI_HCP_COMMAND, cmd,  						    param, param_len,  						    nfc_hci_execute_cb, &hcp_ew, -						    3000); +						    MAX_FWI);  	if (hcp_ew.exec_result < 0)  		return hcp_ew.exec_result; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index d07ca4c5cf8..47403705197 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) "hci: %s: " fmt, __func__ @@ -227,7 +225,7 @@ int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)  			goto exit;  		} -		targets->sens_res = be16_to_cpu(*(u16 *)atqa_skb->data); +		targets->sens_res = be16_to_cpu(*(__be16 *)atqa_skb->data);  		targets->sel_res = sak_skb->data[0];  		r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE, @@ -337,11 +335,8 @@ exit:  	kfree_skb(skb);  exit_noskb: -	if (r) { -		/* TODO: There was an error dispatching the event, -		 * how to propagate up to nfc core? -		 */ -	} +	if (r) +		nfc_hci_driver_failure(hdev, r);  }  static void nfc_hci_cmd_timeout(unsigned long data) @@ -385,34 +380,31 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev)  	if (r < 0)  		goto disconnect_all; -	if (skb->len && skb->len == strlen(hdev->init_data.session_id)) -		if (memcmp(hdev->init_data.session_id, skb->data, -			   skb->len) == 0) { -			/* TODO ELa: restore gate<->pipe table from -			 * some TBD location. -			 * note: it doesn't seem possible to get the chip -			 * currently open gate/pipe table. -			 * It is only possible to obtain the supported -			 * gate list. -			 */ +	if (skb->len && skb->len == strlen(hdev->init_data.session_id) && +		(memcmp(hdev->init_data.session_id, skb->data, +			   skb->len) == 0) && hdev->ops->load_session) { +		/* Restore gate<->pipe table from some proprietary location. */ -			/* goto exit -			 * For now, always do a full initialization */ -		} +		r = hdev->ops->load_session(hdev); -	r = nfc_hci_disconnect_all_gates(hdev); -	if (r < 0) -		goto exit; +		if (r < 0) +			goto disconnect_all; +	} else { -	r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, -				  hdev->init_data.gates); -	if (r < 0) -		goto disconnect_all; +		r = nfc_hci_disconnect_all_gates(hdev); +		if (r < 0) +			goto exit; + +		r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, +					  hdev->init_data.gates); +		if (r < 0) +			goto disconnect_all; -	r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, -			      NFC_HCI_ADMIN_SESSION_IDENTITY, -			      hdev->init_data.session_id, -			      strlen(hdev->init_data.session_id)); +		r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, +				NFC_HCI_ADMIN_SESSION_IDENTITY, +				hdev->init_data.session_id, +				strlen(hdev->init_data.session_id)); +	}  	if (r == 0)  		goto exit; diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h index b274d12c18a..c3d2e2c1394 100644 --- a/net/nfc/hci/hci.h +++ b/net/nfc/hci/hci.h @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #ifndef __LOCAL_HCI_H diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c index b6b4109f234..e9de1514656 100644 --- a/net/nfc/hci/hcp.c +++ b/net/nfc/hci/hcp.c @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) "hci: %s: " fmt, __func__ diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index fe5e966e5b8..1b90c053185 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -13,23 +13,19 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <net/nfc/llc.h>  #include "llc.h" -static struct list_head llc_engines; +static LIST_HEAD(llc_engines);  int nfc_llc_init(void)  {  	int r; -	INIT_LIST_HEAD(&llc_engines); -  	r = nfc_llc_nop_register();  	if (r)  		goto exit; diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h index 7be0b7f3ceb..5dad4c57ffb 100644 --- a/net/nfc/hci/llc.h +++ b/net/nfc/hci/llc.h @@ -13,9 +13,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #ifndef __LOCAL_LLC_H_ diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c index 87b10291b40..d0435d5a197 100644 --- a/net/nfc/hci/llc_nop.c +++ b/net/nfc/hci/llc_nop.c @@ -13,9 +13,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/types.h> diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 27b313befc3..401c7e25527 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -13,9 +13,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) "shdlc: %s: " fmt, __func__ @@ -300,7 +298,7 @@ static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr)  {  	struct sk_buff *skb; -	pr_debug("remote asks retransmition from frame %d\n", y_nr); +	pr_debug("remote asks retransmission from frame %d\n", y_nr);  	if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {  		if (shdlc->t2_active) { diff --git a/net/nfc/llcp.h b/net/nfc/llcp.h index f4d48b57ea1..de1789e3cc8 100644 --- a/net/nfc/llcp.h +++ b/net/nfc/llcp.h @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  enum llcp_state { diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 1017894807c..a3ad69a4c64 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) "llcp: %s: " fmt, __func__ @@ -389,7 +387,7 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)  	__net_timestamp(skb); -	nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); +	nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX);  	return nfc_data_exchange(dev, local->target_idx, skb,  				 nfc_llcp_recv, local); @@ -677,7 +675,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,  	do {  		remote_miu = sock->remote_miu > LLCP_MAX_MIU ? -				local->remote_miu : sock->remote_miu; +				LLCP_DEFAULT_MIU : sock->remote_miu;  		frag_len = min_t(size_t, remote_miu, remaining_len); @@ -686,8 +684,10 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,  		pdu = llcp_allocate_pdu(sock, LLCP_PDU_I,  					frag_len + LLCP_SEQUENCE_SIZE); -		if (pdu == NULL) +		if (pdu == NULL) { +			kfree(msg_data);  			return -ENOMEM; +		}  		skb_put(pdu, LLCP_SEQUENCE_SIZE); diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index 81cd3416c7d..51e78879731 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) "llcp: %s: " fmt, __func__ @@ -29,7 +27,7 @@  static u8 llcp_magic[3] = {0x46, 0x66, 0x6d}; -static struct list_head llcp_devices; +static LIST_HEAD(llcp_devices);  static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb); @@ -295,9 +293,9 @@ static void nfc_llcp_sdreq_timer(unsigned long data)  struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)  { -	struct nfc_llcp_local *local, *n; +	struct nfc_llcp_local *local; -	list_for_each_entry_safe(local, n, &llcp_devices, list) +	list_for_each_entry(local, &llcp_devices, list)  		if (local->dev == dev)  			return local; @@ -611,14 +609,16 @@ u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)  int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len)  { -	struct nfc_llcp_local *local = nfc_llcp_find_local(dev); +	struct nfc_llcp_local *local; + +	if (gb_len < 3 || gb_len > NFC_MAX_GT_LEN) +		return -EINVAL; +	local = nfc_llcp_find_local(dev);  	if (local == NULL) {  		pr_err("No LLCP device\n");  		return -ENODEV;  	} -	if (gb_len < 3) -		return -EINVAL;  	memset(local->remote_gb, 0, NFC_MAX_GT_LEN);  	memcpy(local->remote_gb, gb, gb_len); @@ -680,16 +680,17 @@ void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,  			continue;  		if (skb_copy == NULL) { -			skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE, -					       GFP_ATOMIC); +			skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE, +						      GFP_ATOMIC, true);  			if (skb_copy == NULL)  				continue; -			data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE); +			data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);  			data[0] = local->dev ? local->dev->idx : 0xFF; -			data[1] = direction; +			data[1] = direction & 0x01; +			data[1] |= (RAW_PAYLOAD_LLCP << 1);  		}  		nskb = skb_clone(skb_copy, GFP_ATOMIC); @@ -747,7 +748,7 @@ static void nfc_llcp_tx_work(struct work_struct *work)  			__net_timestamp(skb);  			nfc_llcp_send_to_raw_sock(local, skb, -						  NFC_LLCP_DIRECTION_TX); +						  NFC_DIRECTION_TX);  			ret = nfc_data_exchange(local->dev, local->target_idx,  						skb, nfc_llcp_recv, local); @@ -945,7 +946,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,  	new_sock->local = nfc_llcp_local_get(local);  	new_sock->rw = sock->rw;  	new_sock->miux = sock->miux; -	new_sock->remote_miu = local->remote_miu;  	new_sock->nfc_protocol = sock->nfc_protocol;  	new_sock->dsap = ssap;  	new_sock->target_idx = local->target_idx; @@ -977,7 +977,7 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,  	new_sk->sk_state = LLCP_CONNECTED;  	/* Wake the listening processes */ -	parent->sk_data_ready(parent, 0); +	parent->sk_data_ready(parent);  	/* Send CC */  	nfc_llcp_send_cc(new_sock); @@ -1477,7 +1477,7 @@ static void nfc_llcp_rx_work(struct work_struct *work)  	__net_timestamp(skb); -	nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX); +	nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_RX);  	nfc_llcp_rx_skb(local, skb); @@ -1625,8 +1625,6 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev)  int __init nfc_llcp_init(void)  { -	INIT_LIST_HEAD(&llcp_devices); -  	return nfc_llcp_sock_init();  } diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index d308402b67d..51f077a92fa 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -12,9 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) "llcp: %s: " fmt, __func__ @@ -702,7 +700,6 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,  	llcp_sock->dev = dev;  	llcp_sock->local = nfc_llcp_local_get(local); -	llcp_sock->remote_miu = llcp_sock->local->remote_miu;  	llcp_sock->ssap = nfc_llcp_get_local_ssap(local);  	if (llcp_sock->ssap == LLCP_SAP_MAX) {  		ret = -ENOMEM; @@ -772,8 +769,8 @@ static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock,  	lock_sock(sk);  	if (sk->sk_type == SOCK_DGRAM) { -		struct sockaddr_nfc_llcp *addr = -			(struct sockaddr_nfc_llcp *)msg->msg_name; +		DECLARE_SOCKADDR(struct sockaddr_nfc_llcp *, addr, +				 msg->msg_name);  		if (msg->msg_namelen < sizeof(*addr)) {  			release_sock(sk); @@ -807,8 +804,6 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,  	pr_debug("%p %zu\n", sk, len); -	msg->msg_namelen = 0; -  	lock_sock(sk);  	if (sk->sk_state == LLCP_CLOSED && @@ -847,8 +842,8 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,  	if (sk->sk_type == SOCK_DGRAM && msg->msg_name) {  		struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb); -		struct sockaddr_nfc_llcp *sockaddr = -			(struct sockaddr_nfc_llcp *) msg->msg_name; +		DECLARE_SOCKADDR(struct sockaddr_nfc_llcp *, sockaddr, +				 msg->msg_name);  		msg->msg_namelen = sizeof(struct sockaddr_nfc_llcp); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index b943d46a164..2b400e1a869 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -20,8 +20,7 @@   *  GNU General Public License for more details.   *   *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + *  along with this program; if not, see <http://www.gnu.org/licenses/>.   *   */ @@ -75,7 +74,7 @@ static int __nci_request(struct nci_dev *ndev,  	ndev->req_status = NCI_REQ_PEND; -	init_completion(&ndev->req_completion); +	reinit_completion(&ndev->req_completion);  	req(ndev, opt);  	completion_rc =  		wait_for_completion_interruptible_timeout(&ndev->req_completion, @@ -302,6 +301,9 @@ static int nci_open_device(struct nci_dev *ndev)  	rc = __nci_request(ndev, nci_reset_req, 0,  			   msecs_to_jiffies(NCI_RESET_TIMEOUT)); +	if (ndev->ops->setup) +		ndev->ops->setup(ndev); +  	if (!rc) {  		rc = __nci_request(ndev, nci_init_req, 0,  				   msecs_to_jiffies(NCI_INIT_TIMEOUT)); @@ -362,6 +364,8 @@ static int nci_close_device(struct nci_dev *ndev)  		      msecs_to_jiffies(NCI_RESET_TIMEOUT));  	clear_bit(NCI_INIT, &ndev->flags); +	del_timer_sync(&ndev->cmd_timer); +  	/* Flush cmd wq */  	flush_workqueue(ndev->cmd_wq); @@ -409,12 +413,26 @@ static int nci_dev_down(struct nfc_dev *nfc_dev)  	return nci_close_device(ndev);  } +int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) +{ +	struct nci_set_config_param param; + +	if (!val || !len) +		return 0; + +	param.id = id; +	param.len = len; +	param.val = val; + +	return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, +			     msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); +} +EXPORT_SYMBOL(nci_set_config); +  static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)  {  	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);  	struct nci_set_config_param param; -	__u8 local_gb[NFC_MAX_GT_LEN]; -	int i;  	param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len);  	if ((param.val == NULL) || (param.len == 0)) @@ -423,11 +441,7 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)  	if (param.len > NFC_MAX_GT_LEN)  		return -EINVAL; -	for (i = 0; i < param.len; i++) -		local_gb[param.len-1-i] = param.val[i]; -  	param.id = NCI_PN_ATR_REQ_GEN_BYTES; -	param.val = local_gb;  	return nci_request(ndev, nci_set_config_req, (unsigned long)¶m,  			   msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); @@ -695,6 +709,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,  	ndev->ops = ops;  	ndev->tx_headroom = tx_headroom;  	ndev->tx_tailroom = tx_tailroom; +	init_completion(&ndev->req_completion);  	ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,  					    supported_protocols, @@ -846,6 +861,10 @@ static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)  	/* Get rid of skb owner, prior to sending to the driver. */  	skb_orphan(skb); +	/* Send copy to sniffer */ +	nfc_send_to_raw_sock(ndev->nfc_dev, skb, +			     RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); +  	return ndev->ops->send(ndev, skb);  } @@ -920,6 +939,11 @@ static void nci_rx_work(struct work_struct *work)  	struct sk_buff *skb;  	while ((skb = skb_dequeue(&ndev->rx_q))) { + +		/* Send copy to sniffer */ +		nfc_send_to_raw_sock(ndev->nfc_dev, skb, +				     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); +  		/* Process frame */  		switch (nci_mt(skb->data)) {  		case NCI_MT_RSP_PKT: diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index 2a9399dd6c6..6c3aef85287 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -16,8 +16,7 @@   *  GNU General Public License for more details.   *   *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + *  along with this program; if not, see <http://www.gnu.org/licenses/>.   *   */ diff --git a/net/nfc/nci/lib.c b/net/nfc/nci/lib.c index 6b7fd26c68d..ed774a2e989 100644 --- a/net/nfc/nci/lib.c +++ b/net/nfc/nci/lib.c @@ -20,8 +20,7 @@   *  GNU General Public License for more details.   *   *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + *  along with this program; if not, see <http://www.gnu.org/licenses/>.   *   */ diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index b2aa98ef092..f8f6af23138 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -20,8 +20,7 @@   *  GNU General Public License for more details.   *   *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + *  along with this program; if not, see <http://www.gnu.org/licenses/>.   *   */ @@ -367,7 +366,6 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,  			struct nci_rf_intf_activated_ntf *ntf, __u8 *data)  {  	struct activation_params_poll_nfc_dep *poll; -	int i;  	switch (ntf->activation_rf_tech_and_mode) {  	case NCI_NFC_A_PASSIVE_POLL_MODE: @@ -375,10 +373,8 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,  		poll = &ntf->activation_params.poll_nfc_dep;  		poll->atr_res_len = min_t(__u8, *data++, 63);  		pr_debug("atr_res_len %d\n", poll->atr_res_len); -		if (poll->atr_res_len > 0) { -			for (i = 0; i < poll->atr_res_len; i++) -				poll->atr_res[poll->atr_res_len-1-i] = data[i]; -		} +		if (poll->atr_res_len > 0) +			memcpy(poll->atr_res, data, poll->atr_res_len);  		break;  	default: diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index dd072f38ad0..041de51ccdb 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -20,8 +20,7 @@   *  GNU General Public License for more details.   *   *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + *  along with this program; if not, see <http://www.gnu.org/licenses/>.   *   */ diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c index c7cf37ba729..ec250e77763 100644 --- a/net/nfc/nci/spi.c +++ b/net/nfc/nci/spi.c @@ -21,11 +21,8 @@  #include <linux/export.h>  #include <linux/spi/spi.h>  #include <linux/crc-ccitt.h> -#include <linux/nfc.h>  #include <net/nfc/nci_core.h> -#define NCI_SPI_HDR_LEN			4 -#define NCI_SPI_CRC_LEN			2  #define NCI_SPI_ACK_SHIFT		6  #define NCI_SPI_MSB_PAYLOAD_MASK	0x3F @@ -41,54 +38,48 @@  #define CRC_INIT		0xFFFF -static int nci_spi_open(struct nci_dev *nci_dev) -{ -	struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); - -	return ndev->ops->open(ndev); -} - -static int nci_spi_close(struct nci_dev *nci_dev) -{ -	struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); - -	return ndev->ops->close(ndev); -} - -static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb) +static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb, +			  int cs_change)  {  	struct spi_message m;  	struct spi_transfer t; -	t.tx_buf = skb->data; -	t.len = skb->len; -	t.cs_change = 0; -	t.delay_usecs = ndev->xfer_udelay; +	memset(&t, 0, sizeof(struct spi_transfer)); +	/* a NULL skb means we just want the SPI chip select line to raise */ +	if (skb) { +		t.tx_buf = skb->data; +		t.len = skb->len; +	} else { +		/* still set tx_buf non NULL to make the driver happy */ +		t.tx_buf = &t; +		t.len = 0; +	} +	t.cs_change = cs_change; +	t.delay_usecs = nspi->xfer_udelay;  	spi_message_init(&m);  	spi_message_add_tail(&t, &m); -	return spi_sync(ndev->spi, &m); +	return spi_sync(nspi->spi, &m);  } -static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb) +int nci_spi_send(struct nci_spi *nspi, +		 struct completion *write_handshake_completion, +		 struct sk_buff *skb)  { -	struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);  	unsigned int payload_len = skb->len;  	unsigned char *hdr;  	int ret;  	long completion_rc; -	ndev->ops->deassert_int(ndev); -  	/* add the NCI SPI header to the start of the buffer */  	hdr = skb_push(skb, NCI_SPI_HDR_LEN);  	hdr[0] = NCI_SPI_DIRECT_WRITE; -	hdr[1] = ndev->acknowledge_mode; +	hdr[1] = nspi->acknowledge_mode;  	hdr[2] = payload_len >> 8;  	hdr[3] = payload_len & 0xFF; -	if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) { +	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {  		u16 crc;  		crc = crc_ccitt(CRC_INIT, skb->data, skb->len); @@ -96,123 +87,78 @@ static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)  		*skb_put(skb, 1) = crc & 0xFF;  	} -	ret = __nci_spi_send(ndev, skb); +	if (write_handshake_completion)	{ +		/* Trick SPI driver to raise chip select */ +		ret = __nci_spi_send(nspi, NULL, 1); +		if (ret) +			goto done; -	kfree_skb(skb); -	ndev->ops->assert_int(ndev); +		/* wait for NFC chip hardware handshake to complete */ +		if (wait_for_completion_timeout(write_handshake_completion, +						msecs_to_jiffies(1000)) == 0) { +			ret = -ETIME; +			goto done; +		} +	} -	if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED) +	ret = __nci_spi_send(nspi, skb, 0); +	if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)  		goto done; -	init_completion(&ndev->req_completion); -	completion_rc = -		wait_for_completion_interruptible_timeout(&ndev->req_completion, -							  NCI_SPI_SEND_TIMEOUT); +	reinit_completion(&nspi->req_completion); +	completion_rc =	wait_for_completion_interruptible_timeout( +							&nspi->req_completion, +							NCI_SPI_SEND_TIMEOUT); -	if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK) +	if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)  		ret = -EIO;  done: +	kfree_skb(skb); +  	return ret;  } - -static struct nci_ops nci_spi_ops = { -	.open = nci_spi_open, -	.close = nci_spi_close, -	.send = nci_spi_send, -}; +EXPORT_SYMBOL_GPL(nci_spi_send);  /* ---- Interface to NCI SPI drivers ---- */  /** - * nci_spi_allocate_device - allocate a new nci spi device + * nci_spi_allocate_spi - allocate a new nci spi   *   * @spi: SPI device - * @ops: device operations - * @supported_protocols: NFC protocols supported by the device - * @supported_se: NFC Secure Elements supported by the device - * @acknowledge_mode: Acknowledge mode used by the device + * @acknowledge_mode: Acknowledge mode used by the NFC device   * @delay: delay between transactions in us + * @ndev: nci dev to send incoming nci frames to   */ -struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi, -						struct nci_spi_ops *ops, -						u32 supported_protocols, -						u32 supported_se, -						u8 acknowledge_mode, -						unsigned int delay) +struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi, +				     u8 acknowledge_mode, unsigned int delay, +				     struct nci_dev *ndev)  { -	struct nci_spi_dev *ndev; -	int tailroom = 0; +	struct nci_spi *nspi; -	if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int) +	nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL); +	if (!nspi)  		return NULL; -	if (!supported_protocols) -		return NULL; - -	ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL); -	if (!ndev) -		return NULL; +	nspi->acknowledge_mode = acknowledge_mode; +	nspi->xfer_udelay = delay; -	ndev->ops = ops; -	ndev->acknowledge_mode = acknowledge_mode; -	ndev->xfer_udelay = delay; +	nspi->spi = spi; +	nspi->ndev = ndev; +	init_completion(&nspi->req_completion); -	if (acknowledge_mode == NCI_SPI_CRC_ENABLED) -		tailroom += NCI_SPI_CRC_LEN; - -	ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols, -					    NCI_SPI_HDR_LEN, tailroom); -	if (!ndev->nci_dev) -		return NULL; - -	nci_set_drvdata(ndev->nci_dev, ndev); - -	return ndev; +	return nspi;  } -EXPORT_SYMBOL_GPL(nci_spi_allocate_device); +EXPORT_SYMBOL_GPL(nci_spi_allocate_spi); -/** - * nci_spi_free_device - deallocate nci spi device - * - * @ndev: The nci spi device to deallocate - */ -void nci_spi_free_device(struct nci_spi_dev *ndev) -{ -	nci_free_device(ndev->nci_dev); -} -EXPORT_SYMBOL_GPL(nci_spi_free_device); - -/** - * nci_spi_register_device - register a nci spi device in the nfc subsystem - * - * @pdev: The nci spi device to register - */ -int nci_spi_register_device(struct nci_spi_dev *ndev) -{ -	return nci_register_device(ndev->nci_dev); -} -EXPORT_SYMBOL_GPL(nci_spi_register_device); - -/** - * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem - * - * @dev: The nci spi device to unregister - */ -void nci_spi_unregister_device(struct nci_spi_dev *ndev) -{ -	nci_unregister_device(ndev->nci_dev); -} -EXPORT_SYMBOL_GPL(nci_spi_unregister_device); - -static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge) +static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)  {  	struct sk_buff *skb;  	unsigned char *hdr;  	u16 crc;  	int ret; -	skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL); +	skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);  	/* add the NCI SPI header to the start of the buffer */  	hdr = skb_push(skb, NCI_SPI_HDR_LEN); @@ -225,14 +171,14 @@ static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)  	*skb_put(skb, 1) = crc >> 8;  	*skb_put(skb, 1) = crc & 0xFF; -	ret = __nci_spi_send(ndev, skb); +	ret = __nci_spi_send(nspi, skb, 0);  	kfree_skb(skb);  	return ret;  } -static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev) +static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)  {  	struct sk_buff *skb;  	struct spi_message m; @@ -242,43 +188,49 @@ static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)  	int ret;  	spi_message_init(&m); + +	memset(&tx, 0, sizeof(struct spi_transfer));  	req[0] = NCI_SPI_DIRECT_READ; -	req[1] = ndev->acknowledge_mode; +	req[1] = nspi->acknowledge_mode;  	tx.tx_buf = req;  	tx.len = 2;  	tx.cs_change = 0;  	spi_message_add_tail(&tx, &m); + +	memset(&rx, 0, sizeof(struct spi_transfer));  	rx.rx_buf = resp_hdr;  	rx.len = 2;  	rx.cs_change = 1;  	spi_message_add_tail(&rx, &m); -	ret = spi_sync(ndev->spi, &m); +	ret = spi_sync(nspi->spi, &m);  	if (ret)  		return NULL; -	if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) +	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)  		rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +  				resp_hdr[1] + NCI_SPI_CRC_LEN;  	else  		rx_len = (resp_hdr[0] << 8) | resp_hdr[1]; -	skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL); +	skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);  	if (!skb)  		return NULL;  	spi_message_init(&m); + +	memset(&rx, 0, sizeof(struct spi_transfer));  	rx.rx_buf = skb_put(skb, rx_len);  	rx.len = rx_len;  	rx.cs_change = 0; -	rx.delay_usecs = ndev->xfer_udelay; +	rx.delay_usecs = nspi->xfer_udelay;  	spi_message_add_tail(&rx, &m); -	ret = spi_sync(ndev->spi, &m); +	ret = spi_sync(nspi->spi, &m);  	if (ret)  		goto receive_error; -	if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) { +	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {  		*skb_push(skb, 1) = resp_hdr[1];  		*skb_push(skb, 1) = resp_hdr[0];  	} @@ -318,61 +270,53 @@ static u8 nci_spi_get_ack(struct sk_buff *skb)  }  /** - * nci_spi_recv_frame - receive frame from NCI SPI drivers + * nci_spi_read - read frame from NCI SPI drivers   * - * @ndev: The nci spi device + * @nspi: The nci spi   * Context: can sleep   *   * This call may only be used from a context that may sleep.  The sleep   * is non-interruptible, and has no timeout.   * - * It returns zero on success, else a negative error code. + * It returns an allocated skb containing the frame on success, or NULL.   */ -int nci_spi_recv_frame(struct nci_spi_dev *ndev) +struct sk_buff *nci_spi_read(struct nci_spi *nspi)  {  	struct sk_buff *skb; -	int ret = 0; - -	ndev->ops->deassert_int(ndev);  	/* Retrieve frame from SPI */ -	skb = __nci_spi_recv_frame(ndev); -	if (!skb) { -		ret = -EIO; +	skb = __nci_spi_read(nspi); +	if (!skb)  		goto done; -	} -	if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) { +	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {  		if (!nci_spi_check_crc(skb)) { -			send_acknowledge(ndev, ACKNOWLEDGE_NACK); +			send_acknowledge(nspi, ACKNOWLEDGE_NACK);  			goto done;  		}  		/* In case of acknowledged mode: if ACK or NACK received,  		 * unblock completion of latest frame sent.  		 */ -		ndev->req_result = nci_spi_get_ack(skb); -		if (ndev->req_result) -			complete(&ndev->req_completion); +		nspi->req_result = nci_spi_get_ack(skb); +		if (nspi->req_result) +			complete(&nspi->req_completion);  	}  	/* If there is no payload (ACK/NACK only frame),  	 * free the socket buffer  	 */ -	if (skb->len == 0) { +	if (!skb->len) {  		kfree_skb(skb); +		skb = NULL;  		goto done;  	} -	if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) -		send_acknowledge(ndev, ACKNOWLEDGE_ACK); - -	/* Forward skb to NCI core layer */ -	ret = nci_recv_frame(ndev->nci_dev, skb); +	if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) +		send_acknowledge(nspi, ACKNOWLEDGE_ACK);  done: -	ndev->ops->assert_int(ndev); -	return ret; +	return skb;  } -EXPORT_SYMBOL_GPL(nci_spi_recv_frame); +EXPORT_SYMBOL_GPL(nci_spi_read); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 68063b2025d..43cb1c17e26 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -16,9 +16,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ @@ -30,8 +28,8 @@  #include "nfc.h"  #include "llcp.h" -static struct genl_multicast_group nfc_genl_event_mcgrp = { -	.name = NFC_GENL_MCAST_EVENT_NAME, +static const struct genl_multicast_group nfc_genl_mcgrps[] = { +	{ .name = NFC_GENL_MCAST_EVENT_NAME, },  };  static struct genl_family nfc_genl_family = { @@ -58,6 +56,7 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {  	[NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },  	[NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,  				     .len = NFC_FIRMWARE_NAME_MAXSIZE }, +	[NFC_ATTR_SE_APDU] = { .type = NLA_BINARY },  };  static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { @@ -95,6 +94,14 @@ static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,  		    target->sensf_res))  		goto nla_put_failure; +	if (target->is_iso15693) { +		if (nla_put_u8(msg, NFC_ATTR_TARGET_ISO15693_DSFID, +			       target->iso15693_dsfid) || +		    nla_put(msg, NFC_ATTR_TARGET_ISO15693_UID, +			    sizeof(target->iso15693_uid), target->iso15693_uid)) +			goto nla_put_failure; +	} +  	return genlmsg_end(msg, hdr);  nla_put_failure: @@ -193,7 +200,7 @@ int nfc_genl_targets_found(struct nfc_dev *dev)  	genlmsg_end(msg, hdr); -	return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC); +	return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);  nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -222,7 +229,7 @@ int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -254,7 +261,7 @@ int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -284,7 +291,7 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -317,7 +324,7 @@ int nfc_genl_device_added(struct nfc_dev *dev)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -347,7 +354,7 @@ int nfc_genl_device_removed(struct nfc_dev *dev)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -413,7 +420,7 @@ int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list)  	genlmsg_end(msg, hdr); -	return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC); +	return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);  nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -447,7 +454,7 @@ int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -478,7 +485,7 @@ int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -599,7 +606,7 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx,  	dev->dep_link_up = true; -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);  	return 0; @@ -631,7 +638,7 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev)  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);  	return 0; @@ -1136,7 +1143,7 @@ int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,  	genlmsg_end(msg, hdr); -	genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);  	return 0; @@ -1278,7 +1285,92 @@ static int nfc_genl_dump_ses_done(struct netlink_callback *cb)  	return 0;  } -static struct genl_ops nfc_genl_ops[] = { +struct se_io_ctx { +	u32 dev_idx; +	u32 se_idx; +}; + +static void se_io_cb(void *context, u8 *apdu, size_t apdu_len, int err) +{ +	struct se_io_ctx *ctx = context; +	struct sk_buff *msg; +	void *hdr; + +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	if (!msg) { +		kfree(ctx); +		return; +	} + +	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, +			  NFC_CMD_SE_IO); +	if (!hdr) +		goto free_msg; + +	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, ctx->dev_idx) || +	    nla_put_u32(msg, NFC_ATTR_SE_INDEX, ctx->se_idx) || +	    nla_put(msg, NFC_ATTR_SE_APDU, apdu_len, apdu)) +		goto nla_put_failure; + +	genlmsg_end(msg, hdr); + +	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); + +	kfree(ctx); + +	return; + +nla_put_failure: +	genlmsg_cancel(msg, hdr); +free_msg: +	nlmsg_free(msg); +	kfree(ctx); + +	return; +} + +static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info) +{ +	struct nfc_dev *dev; +	struct se_io_ctx *ctx; +	u32 dev_idx, se_idx; +	u8 *apdu; +	size_t apdu_len; + +	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || +	    !info->attrs[NFC_ATTR_SE_INDEX] || +	    !info->attrs[NFC_ATTR_SE_APDU]) +		return -EINVAL; + +	dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); +	se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]); + +	dev = nfc_get_device(dev_idx); +	if (!dev) +		return -ENODEV; + +	if (!dev->ops || !dev->ops->se_io) +		return -ENOTSUPP; + +	apdu_len = nla_len(info->attrs[NFC_ATTR_SE_APDU]); +	if (apdu_len == 0) +		return -EINVAL; + +	apdu = nla_data(info->attrs[NFC_ATTR_SE_APDU]); +	if (!apdu) +		return -EINVAL; + +	ctx = kzalloc(sizeof(struct se_io_ctx), GFP_KERNEL); +	if (!ctx) +		return -ENOMEM; + +	ctx->dev_idx = dev_idx; +	ctx->se_idx = se_idx; + +	return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx); +} + +static const struct genl_ops nfc_genl_ops[] = {  	{  		.cmd = NFC_CMD_GET_DEVICE,  		.doit = nfc_genl_get_device, @@ -1358,6 +1450,11 @@ static struct genl_ops nfc_genl_ops[] = {  		.done = nfc_genl_dump_ses_done,  		.policy = nfc_genl_policy,  	}, +	{ +		.cmd = NFC_CMD_SE_IO, +		.doit = nfc_genl_se_io, +		.policy = nfc_genl_policy, +	},  }; @@ -1445,16 +1542,15 @@ int __init nfc_genl_init(void)  {  	int rc; -	rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops, -					   ARRAY_SIZE(nfc_genl_ops)); +	rc = genl_register_family_with_ops_groups(&nfc_genl_family, +						  nfc_genl_ops, +						  nfc_genl_mcgrps);  	if (rc)  		return rc; -	rc = genl_register_mc_group(&nfc_genl_family, &nfc_genl_event_mcgrp); -  	netlink_register_notifier(&nl_notifier); -	return rc; +	return 0;  }  /** diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index aaf606fc1fa..88d60064890 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -16,9 +16,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #ifndef __LOCAL_NFC_H @@ -42,6 +40,12 @@ struct nfc_rawsock {  	struct work_struct tx_work;  	bool tx_work_scheduled;  }; + +struct nfc_sock_list { +	struct hlist_head head; +	rwlock_t          lock; +}; +  #define nfc_rawsock(sk) ((struct nfc_rawsock *) sk)  #define to_rawsock_sk(_tx_work) \  	((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work)) diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 313bf1bc848..11c3544ea54 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -16,9 +16,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ @@ -29,6 +27,24 @@  #include "nfc.h" +static struct nfc_sock_list raw_sk_list = { +	.lock = __RW_LOCK_UNLOCKED(raw_sk_list.lock) +}; + +static void nfc_sock_link(struct nfc_sock_list *l, struct sock *sk) +{ +	write_lock(&l->lock); +	sk_add_node(sk, &l->head); +	write_unlock(&l->lock); +} + +static void nfc_sock_unlink(struct nfc_sock_list *l, struct sock *sk) +{ +	write_lock(&l->lock); +	sk_del_node_init(sk); +	write_unlock(&l->lock); +} +  static void rawsock_write_queue_purge(struct sock *sk)  {  	pr_debug("sk=%p\n", sk); @@ -59,6 +75,9 @@ static int rawsock_release(struct socket *sock)  	if (!sk)  		return 0; +	if (sock->type == SOCK_RAW) +		nfc_sock_unlink(&raw_sk_list, sk); +  	sock_orphan(sk);  	sock_put(sk); @@ -142,11 +161,11 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,  	err = rawsock_add_header(skb);  	if (err) -		goto error; +		goto error_skb;  	err = sock_queue_rcv_skb(sk, skb);  	if (err) -		goto error; +		goto error_skb;  	spin_lock_bh(&sk->sk_write_queue.lock);  	if (!skb_queue_empty(&sk->sk_write_queue)) @@ -158,6 +177,9 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,  	sock_put(sk);  	return; +error_skb: +	kfree_skb(skb); +  error:  	rawsock_report_error(sk, err);  	sock_put(sk); @@ -241,8 +263,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,  	if (!skb)  		return rc; -	msg->msg_namelen = 0; -  	copied = skb->len;  	if (len < copied) {  		msg->msg_flags |= MSG_TRUNC; @@ -276,6 +296,26 @@ static const struct proto_ops rawsock_ops = {  	.mmap           = sock_no_mmap,  }; +static const struct proto_ops rawsock_raw_ops = { +	.family         = PF_NFC, +	.owner          = THIS_MODULE, +	.release        = rawsock_release, +	.bind           = sock_no_bind, +	.connect        = sock_no_connect, +	.socketpair     = sock_no_socketpair, +	.accept         = sock_no_accept, +	.getname        = sock_no_getname, +	.poll           = datagram_poll, +	.ioctl          = sock_no_ioctl, +	.listen         = sock_no_listen, +	.shutdown       = sock_no_shutdown, +	.setsockopt     = sock_no_setsockopt, +	.getsockopt     = sock_no_getsockopt, +	.sendmsg        = sock_no_sendmsg, +	.recvmsg        = rawsock_recvmsg, +	.mmap           = sock_no_mmap, +}; +  static void rawsock_destruct(struct sock *sk)  {  	pr_debug("sk=%p\n", sk); @@ -301,10 +341,13 @@ static int rawsock_create(struct net *net, struct socket *sock,  	pr_debug("sock=%p\n", sock); -	if (sock->type != SOCK_SEQPACKET) +	if ((sock->type != SOCK_SEQPACKET) && (sock->type != SOCK_RAW))  		return -ESOCKTNOSUPPORT; -	sock->ops = &rawsock_ops; +	if (sock->type == SOCK_RAW) +		sock->ops = &rawsock_raw_ops; +	else +		sock->ops = &rawsock_ops;  	sk = sk_alloc(net, PF_NFC, GFP_ATOMIC, nfc_proto->proto);  	if (!sk) @@ -314,13 +357,53 @@ static int rawsock_create(struct net *net, struct socket *sock,  	sk->sk_protocol = nfc_proto->id;  	sk->sk_destruct = rawsock_destruct;  	sock->state = SS_UNCONNECTED; - -	INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); -	nfc_rawsock(sk)->tx_work_scheduled = false; +	if (sock->type == SOCK_RAW) +		nfc_sock_link(&raw_sk_list, sk); +	else { +		INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); +		nfc_rawsock(sk)->tx_work_scheduled = false; +	}  	return 0;  } +void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb, +			  u8 payload_type, u8 direction) +{ +	struct sk_buff *skb_copy = NULL, *nskb; +	struct sock *sk; +	u8 *data; + +	read_lock(&raw_sk_list.lock); + +	sk_for_each(sk, &raw_sk_list.head) { +		if (!skb_copy) { +			skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE, +						      GFP_ATOMIC, true); +			if (!skb_copy) +				continue; + +			data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE); + +			data[0] = dev ? dev->idx : 0xFF; +			data[1] = direction & 0x01; +			data[1] |= (payload_type << 1); +		} + +		nskb = skb_clone(skb_copy, GFP_ATOMIC); +		if (!nskb) +			continue; + +		if (sock_queue_rcv_skb(sk, nskb)) +			kfree_skb(nskb); +	} + +	read_unlock(&raw_sk_list.lock); + +	kfree_skb(skb_copy); +} +EXPORT_SYMBOL(nfc_send_to_raw_sock); +  static struct proto rawsock_proto = {  	.name     = "NFC_RAW",  	.owner    = THIS_MODULE,  | 
