xref: /openbmc/linux/net/nfc/core.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23e256b8fSLauro Ramos Venancio /*
33e256b8fSLauro Ramos Venancio  * Copyright (C) 2011 Instituto Nokia de Tecnologia
43e256b8fSLauro Ramos Venancio  *
53e256b8fSLauro Ramos Venancio  * Authors:
63e256b8fSLauro Ramos Venancio  *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
73e256b8fSLauro Ramos Venancio  *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
83e256b8fSLauro Ramos Venancio  */
93e256b8fSLauro Ramos Venancio 
1052858b51SSamuel Ortiz #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
11ed1e0ad8SJoe Perches 
123e256b8fSLauro Ramos Venancio #include <linux/init.h>
133e256b8fSLauro Ramos Venancio #include <linux/kernel.h>
143e256b8fSLauro Ramos Venancio #include <linux/module.h>
153e256b8fSLauro Ramos Venancio #include <linux/slab.h>
16be055b2fSSamuel Ortiz #include <linux/rfkill.h>
177c7cd3bfSSamuel Ortiz #include <linux/nfc.h>
183e256b8fSLauro Ramos Venancio 
195df16cadSSamuel Ortiz #include <net/genetlink.h>
205df16cadSSamuel Ortiz 
213e256b8fSLauro Ramos Venancio #include "nfc.h"
223e256b8fSLauro Ramos Venancio 
233e256b8fSLauro Ramos Venancio #define VERSION "0.1"
243e256b8fSLauro Ramos Venancio 
25c8d56ae7SEric Lapuyade #define NFC_CHECK_PRES_FREQ_MS	2000
26c8d56ae7SEric Lapuyade 
273e256b8fSLauro Ramos Venancio int nfc_devlist_generation;
283e256b8fSLauro Ramos Venancio DEFINE_MUTEX(nfc_devlist_mutex);
293e256b8fSLauro Ramos Venancio 
307eda8b8eSSamuel Ortiz /* NFC device ID bitmap */
317eda8b8eSSamuel Ortiz static DEFINE_IDA(nfc_index_ida);
327eda8b8eSSamuel Ortiz 
nfc_fw_download(struct nfc_dev * dev,const char * firmware_name)339ea7187cSSamuel Ortiz int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name)
349674da87SEric Lapuyade {
359674da87SEric Lapuyade 	int rc = 0;
369674da87SEric Lapuyade 
379674da87SEric Lapuyade 	pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name);
389674da87SEric Lapuyade 
399674da87SEric Lapuyade 	device_lock(&dev->dev);
409674da87SEric Lapuyade 
41da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
429674da87SEric Lapuyade 		rc = -ENODEV;
439674da87SEric Lapuyade 		goto error;
449674da87SEric Lapuyade 	}
459674da87SEric Lapuyade 
469674da87SEric Lapuyade 	if (dev->dev_up) {
479674da87SEric Lapuyade 		rc = -EBUSY;
489674da87SEric Lapuyade 		goto error;
499674da87SEric Lapuyade 	}
509674da87SEric Lapuyade 
519ea7187cSSamuel Ortiz 	if (!dev->ops->fw_download) {
529674da87SEric Lapuyade 		rc = -EOPNOTSUPP;
539674da87SEric Lapuyade 		goto error;
549674da87SEric Lapuyade 	}
559674da87SEric Lapuyade 
569ea7187cSSamuel Ortiz 	dev->fw_download_in_progress = true;
579ea7187cSSamuel Ortiz 	rc = dev->ops->fw_download(dev, firmware_name);
589674da87SEric Lapuyade 	if (rc)
599ea7187cSSamuel Ortiz 		dev->fw_download_in_progress = false;
609674da87SEric Lapuyade 
619674da87SEric Lapuyade error:
629674da87SEric Lapuyade 	device_unlock(&dev->dev);
639674da87SEric Lapuyade 	return rc;
649674da87SEric Lapuyade }
659674da87SEric Lapuyade 
66352a5f5fSEric Lapuyade /**
67352a5f5fSEric Lapuyade  * nfc_fw_download_done - inform that a firmware download was completed
68352a5f5fSEric Lapuyade  *
69352a5f5fSEric Lapuyade  * @dev: The nfc device to which firmware was downloaded
70352a5f5fSEric Lapuyade  * @firmware_name: The firmware filename
71352a5f5fSEric Lapuyade  * @result: The positive value of a standard errno value
72352a5f5fSEric Lapuyade  */
nfc_fw_download_done(struct nfc_dev * dev,const char * firmware_name,u32 result)73352a5f5fSEric Lapuyade int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
74352a5f5fSEric Lapuyade 			 u32 result)
759674da87SEric Lapuyade {
769ea7187cSSamuel Ortiz 	dev->fw_download_in_progress = false;
779674da87SEric Lapuyade 
78352a5f5fSEric Lapuyade 	return nfc_genl_fw_download_done(dev, firmware_name, result);
799674da87SEric Lapuyade }
809ea7187cSSamuel Ortiz EXPORT_SYMBOL(nfc_fw_download_done);
819674da87SEric Lapuyade 
823e256b8fSLauro Ramos Venancio /**
838b3fe7b5SIlan Elias  * nfc_dev_up - turn on the NFC device
848b3fe7b5SIlan Elias  *
858b3fe7b5SIlan Elias  * @dev: The nfc device to be turned on
868b3fe7b5SIlan Elias  *
878b3fe7b5SIlan Elias  * The device remains up until the nfc_dev_down function is called.
888b3fe7b5SIlan Elias  */
nfc_dev_up(struct nfc_dev * dev)898b3fe7b5SIlan Elias int nfc_dev_up(struct nfc_dev *dev)
908b3fe7b5SIlan Elias {
918b3fe7b5SIlan Elias 	int rc = 0;
928b3fe7b5SIlan Elias 
9320c239c1SJoe Perches 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
948b3fe7b5SIlan Elias 
958b3fe7b5SIlan Elias 	device_lock(&dev->dev);
968b3fe7b5SIlan Elias 
97da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
983e3b5dfcSLin Ma 		rc = -ENODEV;
99be055b2fSSamuel Ortiz 		goto error;
100be055b2fSSamuel Ortiz 	}
101be055b2fSSamuel Ortiz 
1023e3b5dfcSLin Ma 	if (dev->rfkill && rfkill_blocked(dev->rfkill)) {
1033e3b5dfcSLin Ma 		rc = -ERFKILL;
1048b3fe7b5SIlan Elias 		goto error;
1058b3fe7b5SIlan Elias 	}
1068b3fe7b5SIlan Elias 
1079ea7187cSSamuel Ortiz 	if (dev->fw_download_in_progress) {
1089674da87SEric Lapuyade 		rc = -EBUSY;
1099674da87SEric Lapuyade 		goto error;
1109674da87SEric Lapuyade 	}
1119674da87SEric Lapuyade 
1128b3fe7b5SIlan Elias 	if (dev->dev_up) {
1138b3fe7b5SIlan Elias 		rc = -EALREADY;
1148b3fe7b5SIlan Elias 		goto error;
1158b3fe7b5SIlan Elias 	}
1168b3fe7b5SIlan Elias 
1178b3fe7b5SIlan Elias 	if (dev->ops->dev_up)
1188b3fe7b5SIlan Elias 		rc = dev->ops->dev_up(dev);
1198b3fe7b5SIlan Elias 
1208b3fe7b5SIlan Elias 	if (!rc)
1218b3fe7b5SIlan Elias 		dev->dev_up = true;
1228b3fe7b5SIlan Elias 
1230a946301SSamuel Ortiz 	/* We have to enable the device before discovering SEs */
124a434c240SSamuel Ortiz 	if (dev->ops->discover_se && dev->ops->discover_se(dev))
125a434c240SSamuel Ortiz 		pr_err("SE discovery failed\n");
1260a946301SSamuel Ortiz 
1278b3fe7b5SIlan Elias error:
1288b3fe7b5SIlan Elias 	device_unlock(&dev->dev);
1298b3fe7b5SIlan Elias 	return rc;
1308b3fe7b5SIlan Elias }
1318b3fe7b5SIlan Elias 
1328b3fe7b5SIlan Elias /**
1338b3fe7b5SIlan Elias  * nfc_dev_down - turn off the NFC device
1348b3fe7b5SIlan Elias  *
1358b3fe7b5SIlan Elias  * @dev: The nfc device to be turned off
1368b3fe7b5SIlan Elias  */
nfc_dev_down(struct nfc_dev * dev)1378b3fe7b5SIlan Elias int nfc_dev_down(struct nfc_dev *dev)
1388b3fe7b5SIlan Elias {
1398b3fe7b5SIlan Elias 	int rc = 0;
1408b3fe7b5SIlan Elias 
14120c239c1SJoe Perches 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
1428b3fe7b5SIlan Elias 
1438b3fe7b5SIlan Elias 	device_lock(&dev->dev);
1448b3fe7b5SIlan Elias 
145da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
1468b3fe7b5SIlan Elias 		rc = -ENODEV;
1478b3fe7b5SIlan Elias 		goto error;
1488b3fe7b5SIlan Elias 	}
1498b3fe7b5SIlan Elias 
1508b3fe7b5SIlan Elias 	if (!dev->dev_up) {
1518b3fe7b5SIlan Elias 		rc = -EALREADY;
1528b3fe7b5SIlan Elias 		goto error;
1538b3fe7b5SIlan Elias 	}
1548b3fe7b5SIlan Elias 
15590099433SEric Lapuyade 	if (dev->polling || dev->active_target) {
1568b3fe7b5SIlan Elias 		rc = -EBUSY;
1578b3fe7b5SIlan Elias 		goto error;
1588b3fe7b5SIlan Elias 	}
1598b3fe7b5SIlan Elias 
1608b3fe7b5SIlan Elias 	if (dev->ops->dev_down)
1618b3fe7b5SIlan Elias 		dev->ops->dev_down(dev);
1628b3fe7b5SIlan Elias 
1638b3fe7b5SIlan Elias 	dev->dev_up = false;
1648b3fe7b5SIlan Elias 
1658b3fe7b5SIlan Elias error:
1668b3fe7b5SIlan Elias 	device_unlock(&dev->dev);
1678b3fe7b5SIlan Elias 	return rc;
1688b3fe7b5SIlan Elias }
1698b3fe7b5SIlan Elias 
nfc_rfkill_set_block(void * data,bool blocked)170be055b2fSSamuel Ortiz static int nfc_rfkill_set_block(void *data, bool blocked)
171be055b2fSSamuel Ortiz {
172be055b2fSSamuel Ortiz 	struct nfc_dev *dev = data;
173be055b2fSSamuel Ortiz 
174be055b2fSSamuel Ortiz 	pr_debug("%s blocked %d", dev_name(&dev->dev), blocked);
175be055b2fSSamuel Ortiz 
176be055b2fSSamuel Ortiz 	if (!blocked)
177be055b2fSSamuel Ortiz 		return 0;
178be055b2fSSamuel Ortiz 
179be055b2fSSamuel Ortiz 	nfc_dev_down(dev);
180be055b2fSSamuel Ortiz 
181be055b2fSSamuel Ortiz 	return 0;
182be055b2fSSamuel Ortiz }
183be055b2fSSamuel Ortiz 
184be055b2fSSamuel Ortiz static const struct rfkill_ops nfc_rfkill_ops = {
185be055b2fSSamuel Ortiz 	.set_block = nfc_rfkill_set_block,
186be055b2fSSamuel Ortiz };
187be055b2fSSamuel Ortiz 
1888b3fe7b5SIlan Elias /**
1893e256b8fSLauro Ramos Venancio  * nfc_start_poll - start polling for nfc targets
1903e256b8fSLauro Ramos Venancio  *
1913e256b8fSLauro Ramos Venancio  * @dev: The nfc device that must start polling
1927cdda1c1SAndrew Lunn  * @im_protocols: bitset of nfc initiator protocols to be used for polling
1937cdda1c1SAndrew Lunn  * @tm_protocols: bitset of nfc transport protocols to be used for polling
1943e256b8fSLauro Ramos Venancio  *
1953e256b8fSLauro Ramos Venancio  * The device remains polling for targets until a target is found or
1963e256b8fSLauro Ramos Venancio  * the nfc_stop_poll function is called.
1973e256b8fSLauro Ramos Venancio  */
nfc_start_poll(struct nfc_dev * dev,u32 im_protocols,u32 tm_protocols)198fe7c5800SSamuel Ortiz int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols)
1993e256b8fSLauro Ramos Venancio {
2003e256b8fSLauro Ramos Venancio 	int rc;
2013e256b8fSLauro Ramos Venancio 
202fe7c5800SSamuel Ortiz 	pr_debug("dev_name %s initiator protocols 0x%x target protocols 0x%x\n",
203fe7c5800SSamuel Ortiz 		 dev_name(&dev->dev), im_protocols, tm_protocols);
2043e256b8fSLauro Ramos Venancio 
205fe7c5800SSamuel Ortiz 	if (!im_protocols && !tm_protocols)
2063e256b8fSLauro Ramos Venancio 		return -EINVAL;
2073e256b8fSLauro Ramos Venancio 
2083e256b8fSLauro Ramos Venancio 	device_lock(&dev->dev);
2093e256b8fSLauro Ramos Venancio 
210da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
2113e256b8fSLauro Ramos Venancio 		rc = -ENODEV;
2123e256b8fSLauro Ramos Venancio 		goto error;
2133e256b8fSLauro Ramos Venancio 	}
2143e256b8fSLauro Ramos Venancio 
2157757dc8aSSamuel Ortiz 	if (!dev->dev_up) {
2167757dc8aSSamuel Ortiz 		rc = -ENODEV;
2177757dc8aSSamuel Ortiz 		goto error;
2187757dc8aSSamuel Ortiz 	}
2197757dc8aSSamuel Ortiz 
2203e256b8fSLauro Ramos Venancio 	if (dev->polling) {
2213e256b8fSLauro Ramos Venancio 		rc = -EBUSY;
2223e256b8fSLauro Ramos Venancio 		goto error;
2233e256b8fSLauro Ramos Venancio 	}
2243e256b8fSLauro Ramos Venancio 
225fe7c5800SSamuel Ortiz 	rc = dev->ops->start_poll(dev, im_protocols, tm_protocols);
226f212ad5eSSamuel Ortiz 	if (!rc) {
2273e256b8fSLauro Ramos Venancio 		dev->polling = true;
228f212ad5eSSamuel Ortiz 		dev->rf_mode = NFC_RF_NONE;
229f212ad5eSSamuel Ortiz 	}
2303e256b8fSLauro Ramos Venancio 
2313e256b8fSLauro Ramos Venancio error:
2323e256b8fSLauro Ramos Venancio 	device_unlock(&dev->dev);
2333e256b8fSLauro Ramos Venancio 	return rc;
2343e256b8fSLauro Ramos Venancio }
2353e256b8fSLauro Ramos Venancio 
2363e256b8fSLauro Ramos Venancio /**
2373e256b8fSLauro Ramos Venancio  * nfc_stop_poll - stop polling for nfc targets
2383e256b8fSLauro Ramos Venancio  *
2393e256b8fSLauro Ramos Venancio  * @dev: The nfc device that must stop polling
2403e256b8fSLauro Ramos Venancio  */
nfc_stop_poll(struct nfc_dev * dev)2413e256b8fSLauro Ramos Venancio int nfc_stop_poll(struct nfc_dev *dev)
2423e256b8fSLauro Ramos Venancio {
2433e256b8fSLauro Ramos Venancio 	int rc = 0;
2443e256b8fSLauro Ramos Venancio 
24520c239c1SJoe Perches 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
2463e256b8fSLauro Ramos Venancio 
2473e256b8fSLauro Ramos Venancio 	device_lock(&dev->dev);
2483e256b8fSLauro Ramos Venancio 
249da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
2503e256b8fSLauro Ramos Venancio 		rc = -ENODEV;
2513e256b8fSLauro Ramos Venancio 		goto error;
2523e256b8fSLauro Ramos Venancio 	}
2533e256b8fSLauro Ramos Venancio 
2543e256b8fSLauro Ramos Venancio 	if (!dev->polling) {
2553e256b8fSLauro Ramos Venancio 		rc = -EINVAL;
2563e256b8fSLauro Ramos Venancio 		goto error;
2573e256b8fSLauro Ramos Venancio 	}
2583e256b8fSLauro Ramos Venancio 
2593e256b8fSLauro Ramos Venancio 	dev->ops->stop_poll(dev);
2603e256b8fSLauro Ramos Venancio 	dev->polling = false;
2615bcf099cSThierry Escande 	dev->rf_mode = NFC_RF_NONE;
2623e256b8fSLauro Ramos Venancio 
2633e256b8fSLauro Ramos Venancio error:
2643e256b8fSLauro Ramos Venancio 	device_unlock(&dev->dev);
2653e256b8fSLauro Ramos Venancio 	return rc;
2663e256b8fSLauro Ramos Venancio }
2673e256b8fSLauro Ramos Venancio 
nfc_find_target(struct nfc_dev * dev,u32 target_idx)26890099433SEric Lapuyade static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)
26990099433SEric Lapuyade {
27090099433SEric Lapuyade 	int i;
27190099433SEric Lapuyade 
27290099433SEric Lapuyade 	for (i = 0; i < dev->n_targets; i++) {
27390099433SEric Lapuyade 		if (dev->targets[i].idx == target_idx)
27490099433SEric Lapuyade 			return &dev->targets[i];
27590099433SEric Lapuyade 	}
27690099433SEric Lapuyade 
27790099433SEric Lapuyade 	return NULL;
27890099433SEric Lapuyade }
27990099433SEric Lapuyade 
nfc_dep_link_up(struct nfc_dev * dev,int target_index,u8 comm_mode)28047807d3dSSamuel Ortiz int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
2811ed28f61SSamuel Ortiz {
2821ed28f61SSamuel Ortiz 	int rc = 0;
28347807d3dSSamuel Ortiz 	u8 *gb;
28447807d3dSSamuel Ortiz 	size_t gb_len;
28590099433SEric Lapuyade 	struct nfc_target *target;
2861ed28f61SSamuel Ortiz 
28747807d3dSSamuel Ortiz 	pr_debug("dev_name=%s comm %d\n", dev_name(&dev->dev), comm_mode);
2881ed28f61SSamuel Ortiz 
2891ed28f61SSamuel Ortiz 	if (!dev->ops->dep_link_up)
2901ed28f61SSamuel Ortiz 		return -EOPNOTSUPP;
2911ed28f61SSamuel Ortiz 
2921ed28f61SSamuel Ortiz 	device_lock(&dev->dev);
2931ed28f61SSamuel Ortiz 
294da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
2951ed28f61SSamuel Ortiz 		rc = -ENODEV;
2961ed28f61SSamuel Ortiz 		goto error;
2971ed28f61SSamuel Ortiz 	}
2981ed28f61SSamuel Ortiz 
2991ed28f61SSamuel Ortiz 	if (dev->dep_link_up == true) {
3001ed28f61SSamuel Ortiz 		rc = -EALREADY;
3011ed28f61SSamuel Ortiz 		goto error;
3021ed28f61SSamuel Ortiz 	}
3031ed28f61SSamuel Ortiz 
30447807d3dSSamuel Ortiz 	gb = nfc_llcp_general_bytes(dev, &gb_len);
30547807d3dSSamuel Ortiz 	if (gb_len > NFC_MAX_GT_LEN) {
30647807d3dSSamuel Ortiz 		rc = -EINVAL;
30747807d3dSSamuel Ortiz 		goto error;
30847807d3dSSamuel Ortiz 	}
30947807d3dSSamuel Ortiz 
31090099433SEric Lapuyade 	target = nfc_find_target(dev, target_index);
31190099433SEric Lapuyade 	if (target == NULL) {
31290099433SEric Lapuyade 		rc = -ENOTCONN;
31390099433SEric Lapuyade 		goto error;
31490099433SEric Lapuyade 	}
31590099433SEric Lapuyade 
31690099433SEric Lapuyade 	rc = dev->ops->dep_link_up(dev, target, comm_mode, gb, gb_len);
317f212ad5eSSamuel Ortiz 	if (!rc) {
31890099433SEric Lapuyade 		dev->active_target = target;
319f212ad5eSSamuel Ortiz 		dev->rf_mode = NFC_RF_INITIATOR;
320f212ad5eSSamuel Ortiz 	}
3211ed28f61SSamuel Ortiz 
3221ed28f61SSamuel Ortiz error:
3231ed28f61SSamuel Ortiz 	device_unlock(&dev->dev);
3241ed28f61SSamuel Ortiz 	return rc;
3251ed28f61SSamuel Ortiz }
3261ed28f61SSamuel Ortiz 
nfc_dep_link_down(struct nfc_dev * dev)3271ed28f61SSamuel Ortiz int nfc_dep_link_down(struct nfc_dev *dev)
3281ed28f61SSamuel Ortiz {
3291ed28f61SSamuel Ortiz 	int rc = 0;
3301ed28f61SSamuel Ortiz 
3311ed28f61SSamuel Ortiz 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
3321ed28f61SSamuel Ortiz 
3331ed28f61SSamuel Ortiz 	if (!dev->ops->dep_link_down)
3341ed28f61SSamuel Ortiz 		return -EOPNOTSUPP;
3351ed28f61SSamuel Ortiz 
3361ed28f61SSamuel Ortiz 	device_lock(&dev->dev);
3371ed28f61SSamuel Ortiz 
338da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
3391ed28f61SSamuel Ortiz 		rc = -ENODEV;
3401ed28f61SSamuel Ortiz 		goto error;
3411ed28f61SSamuel Ortiz 	}
3421ed28f61SSamuel Ortiz 
3431ed28f61SSamuel Ortiz 	if (dev->dep_link_up == false) {
3441ed28f61SSamuel Ortiz 		rc = -EALREADY;
3451ed28f61SSamuel Ortiz 		goto error;
3461ed28f61SSamuel Ortiz 	}
3471ed28f61SSamuel Ortiz 
3481ed28f61SSamuel Ortiz 	rc = dev->ops->dep_link_down(dev);
3491ed28f61SSamuel Ortiz 	if (!rc) {
3501ed28f61SSamuel Ortiz 		dev->dep_link_up = false;
35190099433SEric Lapuyade 		dev->active_target = NULL;
3525bcf099cSThierry Escande 		dev->rf_mode = NFC_RF_NONE;
353d646960fSSamuel Ortiz 		nfc_llcp_mac_is_down(dev);
3541ed28f61SSamuel Ortiz 		nfc_genl_dep_link_down_event(dev);
3551ed28f61SSamuel Ortiz 	}
3561ed28f61SSamuel Ortiz 
3571ed28f61SSamuel Ortiz error:
3581ed28f61SSamuel Ortiz 	device_unlock(&dev->dev);
3595bcf099cSThierry Escande 
3601ed28f61SSamuel Ortiz 	return rc;
3611ed28f61SSamuel Ortiz }
3621ed28f61SSamuel Ortiz 
nfc_dep_link_is_up(struct nfc_dev * dev,u32 target_idx,u8 comm_mode,u8 rf_mode)3631ed28f61SSamuel Ortiz int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,
3641ed28f61SSamuel Ortiz 		       u8 comm_mode, u8 rf_mode)
3651ed28f61SSamuel Ortiz {
3661ed28f61SSamuel Ortiz 	dev->dep_link_up = true;
3671ed28f61SSamuel Ortiz 
368d31652a2SArron Wang 	if (!dev->active_target && rf_mode == NFC_RF_INITIATOR) {
369e29a9e2aSSamuel Ortiz 		struct nfc_target *target;
370e29a9e2aSSamuel Ortiz 
371e29a9e2aSSamuel Ortiz 		target = nfc_find_target(dev, target_idx);
372e29a9e2aSSamuel Ortiz 		if (target == NULL)
373e29a9e2aSSamuel Ortiz 			return -ENOTCONN;
374e29a9e2aSSamuel Ortiz 
375e29a9e2aSSamuel Ortiz 		dev->active_target = target;
376e29a9e2aSSamuel Ortiz 	}
377e29a9e2aSSamuel Ortiz 
378e29a9e2aSSamuel Ortiz 	dev->polling = false;
379e29a9e2aSSamuel Ortiz 	dev->rf_mode = rf_mode;
380e29a9e2aSSamuel Ortiz 
381d646960fSSamuel Ortiz 	nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode);
382d646960fSSamuel Ortiz 
3831ed28f61SSamuel Ortiz 	return nfc_genl_dep_link_up_event(dev, target_idx, comm_mode, rf_mode);
3841ed28f61SSamuel Ortiz }
3851ed28f61SSamuel Ortiz EXPORT_SYMBOL(nfc_dep_link_is_up);
3861ed28f61SSamuel Ortiz 
3873e256b8fSLauro Ramos Venancio /**
3883e256b8fSLauro Ramos Venancio  * nfc_activate_target - prepare the target for data exchange
3893e256b8fSLauro Ramos Venancio  *
3903e256b8fSLauro Ramos Venancio  * @dev: The nfc device that found the target
3913e256b8fSLauro Ramos Venancio  * @target_idx: index of the target that must be activated
3923e256b8fSLauro Ramos Venancio  * @protocol: nfc protocol that will be used for data exchange
3933e256b8fSLauro Ramos Venancio  */
nfc_activate_target(struct nfc_dev * dev,u32 target_idx,u32 protocol)3943e256b8fSLauro Ramos Venancio int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
3953e256b8fSLauro Ramos Venancio {
3963e256b8fSLauro Ramos Venancio 	int rc;
39790099433SEric Lapuyade 	struct nfc_target *target;
3983e256b8fSLauro Ramos Venancio 
39920c239c1SJoe Perches 	pr_debug("dev_name=%s target_idx=%u protocol=%u\n",
40020c239c1SJoe Perches 		 dev_name(&dev->dev), target_idx, protocol);
4013e256b8fSLauro Ramos Venancio 
4023e256b8fSLauro Ramos Venancio 	device_lock(&dev->dev);
4033e256b8fSLauro Ramos Venancio 
404da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
4053e256b8fSLauro Ramos Venancio 		rc = -ENODEV;
4063e256b8fSLauro Ramos Venancio 		goto error;
4073e256b8fSLauro Ramos Venancio 	}
4083e256b8fSLauro Ramos Venancio 
40990099433SEric Lapuyade 	if (dev->active_target) {
41090099433SEric Lapuyade 		rc = -EBUSY;
41190099433SEric Lapuyade 		goto error;
41290099433SEric Lapuyade 	}
41390099433SEric Lapuyade 
41490099433SEric Lapuyade 	target = nfc_find_target(dev, target_idx);
41590099433SEric Lapuyade 	if (target == NULL) {
41690099433SEric Lapuyade 		rc = -ENOTCONN;
41790099433SEric Lapuyade 		goto error;
41890099433SEric Lapuyade 	}
41990099433SEric Lapuyade 
42090099433SEric Lapuyade 	rc = dev->ops->activate_target(dev, target, protocol);
421c8d56ae7SEric Lapuyade 	if (!rc) {
42290099433SEric Lapuyade 		dev->active_target = target;
423f212ad5eSSamuel Ortiz 		dev->rf_mode = NFC_RF_INITIATOR;
4243e256b8fSLauro Ramos Venancio 
425f0c91038SEric Lapuyade 		if (dev->ops->check_presence && !dev->shutting_down)
426c8d56ae7SEric Lapuyade 			mod_timer(&dev->check_pres_timer, jiffies +
427c8d56ae7SEric Lapuyade 				  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
428c8d56ae7SEric Lapuyade 	}
4293e256b8fSLauro Ramos Venancio 
4303e256b8fSLauro Ramos Venancio error:
4313e256b8fSLauro Ramos Venancio 	device_unlock(&dev->dev);
4323e256b8fSLauro Ramos Venancio 	return rc;
4333e256b8fSLauro Ramos Venancio }
4343e256b8fSLauro Ramos Venancio 
4353e256b8fSLauro Ramos Venancio /**
4363e256b8fSLauro Ramos Venancio  * nfc_deactivate_target - deactivate a nfc target
4373e256b8fSLauro Ramos Venancio  *
4383e256b8fSLauro Ramos Venancio  * @dev: The nfc device that found the target
4393e256b8fSLauro Ramos Venancio  * @target_idx: index of the target that must be deactivated
4407cdda1c1SAndrew Lunn  * @mode: idle or sleep?
4413e256b8fSLauro Ramos Venancio  */
nfc_deactivate_target(struct nfc_dev * dev,u32 target_idx,u8 mode)44296d4581fSChristophe Ricard int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx, u8 mode)
4433e256b8fSLauro Ramos Venancio {
4443e256b8fSLauro Ramos Venancio 	int rc = 0;
4453e256b8fSLauro Ramos Venancio 
44620c239c1SJoe Perches 	pr_debug("dev_name=%s target_idx=%u\n",
44720c239c1SJoe Perches 		 dev_name(&dev->dev), target_idx);
4483e256b8fSLauro Ramos Venancio 
4493e256b8fSLauro Ramos Venancio 	device_lock(&dev->dev);
4503e256b8fSLauro Ramos Venancio 
451da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
4523e256b8fSLauro Ramos Venancio 		rc = -ENODEV;
4533e256b8fSLauro Ramos Venancio 		goto error;
4543e256b8fSLauro Ramos Venancio 	}
4553e256b8fSLauro Ramos Venancio 
45690099433SEric Lapuyade 	if (dev->active_target == NULL) {
45790099433SEric Lapuyade 		rc = -ENOTCONN;
45890099433SEric Lapuyade 		goto error;
45990099433SEric Lapuyade 	}
46090099433SEric Lapuyade 
46190099433SEric Lapuyade 	if (dev->active_target->idx != target_idx) {
46290099433SEric Lapuyade 		rc = -ENOTCONN;
46390099433SEric Lapuyade 		goto error;
46490099433SEric Lapuyade 	}
46590099433SEric Lapuyade 
466c8d56ae7SEric Lapuyade 	if (dev->ops->check_presence)
467c8d56ae7SEric Lapuyade 		del_timer_sync(&dev->check_pres_timer);
468c8d56ae7SEric Lapuyade 
46996d4581fSChristophe Ricard 	dev->ops->deactivate_target(dev, dev->active_target, mode);
47090099433SEric Lapuyade 	dev->active_target = NULL;
4713e256b8fSLauro Ramos Venancio 
4723e256b8fSLauro Ramos Venancio error:
4733e256b8fSLauro Ramos Venancio 	device_unlock(&dev->dev);
4743e256b8fSLauro Ramos Venancio 	return rc;
4753e256b8fSLauro Ramos Venancio }
4763e256b8fSLauro Ramos Venancio 
4773e256b8fSLauro Ramos Venancio /**
4783e256b8fSLauro Ramos Venancio  * nfc_data_exchange - transceive data
4793e256b8fSLauro Ramos Venancio  *
4803e256b8fSLauro Ramos Venancio  * @dev: The nfc device that found the target
4813e256b8fSLauro Ramos Venancio  * @target_idx: index of the target
4823e256b8fSLauro Ramos Venancio  * @skb: data to be sent
4833e256b8fSLauro Ramos Venancio  * @cb: callback called when the response is received
4843e256b8fSLauro Ramos Venancio  * @cb_context: parameter for the callback function
4853e256b8fSLauro Ramos Venancio  *
4863e256b8fSLauro Ramos Venancio  * The user must wait for the callback before calling this function again.
4873e256b8fSLauro Ramos Venancio  */
nfc_data_exchange(struct nfc_dev * dev,u32 target_idx,struct sk_buff * skb,data_exchange_cb_t cb,void * cb_context)4880a40acb2SSamuel Ortiz int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
4890a40acb2SSamuel Ortiz 		      data_exchange_cb_t cb, void *cb_context)
4903e256b8fSLauro Ramos Venancio {
4913e256b8fSLauro Ramos Venancio 	int rc;
4923e256b8fSLauro Ramos Venancio 
49320c239c1SJoe Perches 	pr_debug("dev_name=%s target_idx=%u skb->len=%u\n",
49420c239c1SJoe Perches 		 dev_name(&dev->dev), target_idx, skb->len);
4953e256b8fSLauro Ramos Venancio 
4963e256b8fSLauro Ramos Venancio 	device_lock(&dev->dev);
4973e256b8fSLauro Ramos Venancio 
498da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
4993e256b8fSLauro Ramos Venancio 		rc = -ENODEV;
5003e256b8fSLauro Ramos Venancio 		kfree_skb(skb);
5013e256b8fSLauro Ramos Venancio 		goto error;
5023e256b8fSLauro Ramos Venancio 	}
5033e256b8fSLauro Ramos Venancio 
504be9ae4ceSSamuel Ortiz 	if (dev->rf_mode == NFC_RF_INITIATOR && dev->active_target != NULL) {
50590099433SEric Lapuyade 		if (dev->active_target->idx != target_idx) {
506144612caSEric Lapuyade 			rc = -EADDRNOTAVAIL;
507144612caSEric Lapuyade 			kfree_skb(skb);
508144612caSEric Lapuyade 			goto error;
509144612caSEric Lapuyade 		}
510144612caSEric Lapuyade 
511c8d56ae7SEric Lapuyade 		if (dev->ops->check_presence)
512c8d56ae7SEric Lapuyade 			del_timer_sync(&dev->check_pres_timer);
513c8d56ae7SEric Lapuyade 
514be9ae4ceSSamuel Ortiz 		rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb,
51590099433SEric Lapuyade 					     cb_context);
5163e256b8fSLauro Ramos Venancio 
517f0c91038SEric Lapuyade 		if (!rc && dev->ops->check_presence && !dev->shutting_down)
518c8d56ae7SEric Lapuyade 			mod_timer(&dev->check_pres_timer, jiffies +
519c8d56ae7SEric Lapuyade 				  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
520be9ae4ceSSamuel Ortiz 	} else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) {
521be9ae4ceSSamuel Ortiz 		rc = dev->ops->tm_send(dev, skb);
522be9ae4ceSSamuel Ortiz 	} else {
523be9ae4ceSSamuel Ortiz 		rc = -ENOTCONN;
524be9ae4ceSSamuel Ortiz 		kfree_skb(skb);
525be9ae4ceSSamuel Ortiz 		goto error;
526be9ae4ceSSamuel Ortiz 	}
527be9ae4ceSSamuel Ortiz 
528c8d56ae7SEric Lapuyade 
5293e256b8fSLauro Ramos Venancio error:
5303e256b8fSLauro Ramos Venancio 	device_unlock(&dev->dev);
5313e256b8fSLauro Ramos Venancio 	return rc;
5323e256b8fSLauro Ramos Venancio }
5333e256b8fSLauro Ramos Venancio 
nfc_find_se(struct nfc_dev * dev,u32 se_idx)534d8eb18eeSArron Wang struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx)
535c531c9ecSSamuel Ortiz {
536156cef80SAxel Lin 	struct nfc_se *se;
537c531c9ecSSamuel Ortiz 
538156cef80SAxel Lin 	list_for_each_entry(se, &dev->secure_elements, list)
539c531c9ecSSamuel Ortiz 		if (se->idx == se_idx)
540c531c9ecSSamuel Ortiz 			return se;
541c531c9ecSSamuel Ortiz 
542c531c9ecSSamuel Ortiz 	return NULL;
543c531c9ecSSamuel Ortiz }
544d8eb18eeSArron Wang EXPORT_SYMBOL(nfc_find_se);
545c531c9ecSSamuel Ortiz 
nfc_enable_se(struct nfc_dev * dev,u32 se_idx)546c531c9ecSSamuel Ortiz int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
547c531c9ecSSamuel Ortiz {
548c531c9ecSSamuel Ortiz 	struct nfc_se *se;
549c531c9ecSSamuel Ortiz 	int rc;
550c531c9ecSSamuel Ortiz 
551c531c9ecSSamuel Ortiz 	pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
552c531c9ecSSamuel Ortiz 
553c531c9ecSSamuel Ortiz 	device_lock(&dev->dev);
554c531c9ecSSamuel Ortiz 
555da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
556c531c9ecSSamuel Ortiz 		rc = -ENODEV;
557c531c9ecSSamuel Ortiz 		goto error;
558c531c9ecSSamuel Ortiz 	}
559c531c9ecSSamuel Ortiz 
560c531c9ecSSamuel Ortiz 	if (!dev->dev_up) {
561c531c9ecSSamuel Ortiz 		rc = -ENODEV;
562c531c9ecSSamuel Ortiz 		goto error;
563c531c9ecSSamuel Ortiz 	}
564c531c9ecSSamuel Ortiz 
565c531c9ecSSamuel Ortiz 	if (dev->polling) {
566c531c9ecSSamuel Ortiz 		rc = -EBUSY;
567c531c9ecSSamuel Ortiz 		goto error;
568c531c9ecSSamuel Ortiz 	}
569c531c9ecSSamuel Ortiz 
570c531c9ecSSamuel Ortiz 	if (!dev->ops->enable_se || !dev->ops->disable_se) {
571c531c9ecSSamuel Ortiz 		rc = -EOPNOTSUPP;
572c531c9ecSSamuel Ortiz 		goto error;
573c531c9ecSSamuel Ortiz 	}
574c531c9ecSSamuel Ortiz 
575d8eb18eeSArron Wang 	se = nfc_find_se(dev, se_idx);
576c531c9ecSSamuel Ortiz 	if (!se) {
577c531c9ecSSamuel Ortiz 		rc = -EINVAL;
578c531c9ecSSamuel Ortiz 		goto error;
579c531c9ecSSamuel Ortiz 	}
580c531c9ecSSamuel Ortiz 
5812c383283SArron Wang 	if (se->state == NFC_SE_ENABLED) {
582c531c9ecSSamuel Ortiz 		rc = -EALREADY;
583c531c9ecSSamuel Ortiz 		goto error;
584c531c9ecSSamuel Ortiz 	}
585c531c9ecSSamuel Ortiz 
586c531c9ecSSamuel Ortiz 	rc = dev->ops->enable_se(dev, se_idx);
58739525ee1SArron Wang 	if (rc >= 0)
58839525ee1SArron Wang 		se->state = NFC_SE_ENABLED;
589c531c9ecSSamuel Ortiz 
590c531c9ecSSamuel Ortiz error:
591c531c9ecSSamuel Ortiz 	device_unlock(&dev->dev);
592c531c9ecSSamuel Ortiz 	return rc;
593c531c9ecSSamuel Ortiz }
594c531c9ecSSamuel Ortiz 
nfc_disable_se(struct nfc_dev * dev,u32 se_idx)595c531c9ecSSamuel Ortiz int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
596c531c9ecSSamuel Ortiz {
597c531c9ecSSamuel Ortiz 	struct nfc_se *se;
598c531c9ecSSamuel Ortiz 	int rc;
599c531c9ecSSamuel Ortiz 
600c531c9ecSSamuel Ortiz 	pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
601c531c9ecSSamuel Ortiz 
602c531c9ecSSamuel Ortiz 	device_lock(&dev->dev);
603c531c9ecSSamuel Ortiz 
604da5c0f11SDuoming Zhou 	if (dev->shutting_down) {
605c531c9ecSSamuel Ortiz 		rc = -ENODEV;
606c531c9ecSSamuel Ortiz 		goto error;
607c531c9ecSSamuel Ortiz 	}
608c531c9ecSSamuel Ortiz 
609c531c9ecSSamuel Ortiz 	if (!dev->dev_up) {
610c531c9ecSSamuel Ortiz 		rc = -ENODEV;
611c531c9ecSSamuel Ortiz 		goto error;
612c531c9ecSSamuel Ortiz 	}
613c531c9ecSSamuel Ortiz 
614c531c9ecSSamuel Ortiz 	if (!dev->ops->enable_se || !dev->ops->disable_se) {
615c531c9ecSSamuel Ortiz 		rc = -EOPNOTSUPP;
616c531c9ecSSamuel Ortiz 		goto error;
617c531c9ecSSamuel Ortiz 	}
618c531c9ecSSamuel Ortiz 
619d8eb18eeSArron Wang 	se = nfc_find_se(dev, se_idx);
620c531c9ecSSamuel Ortiz 	if (!se) {
621c531c9ecSSamuel Ortiz 		rc = -EINVAL;
622c531c9ecSSamuel Ortiz 		goto error;
623c531c9ecSSamuel Ortiz 	}
624c531c9ecSSamuel Ortiz 
6252c383283SArron Wang 	if (se->state == NFC_SE_DISABLED) {
626c531c9ecSSamuel Ortiz 		rc = -EALREADY;
627c531c9ecSSamuel Ortiz 		goto error;
628c531c9ecSSamuel Ortiz 	}
629c531c9ecSSamuel Ortiz 
630c531c9ecSSamuel Ortiz 	rc = dev->ops->disable_se(dev, se_idx);
63139525ee1SArron Wang 	if (rc >= 0)
63239525ee1SArron Wang 		se->state = NFC_SE_DISABLED;
633c531c9ecSSamuel Ortiz 
634c531c9ecSSamuel Ortiz error:
635c531c9ecSSamuel Ortiz 	device_unlock(&dev->dev);
636c531c9ecSSamuel Ortiz 	return rc;
637c531c9ecSSamuel Ortiz }
638c531c9ecSSamuel Ortiz 
nfc_set_remote_general_bytes(struct nfc_dev * dev,const u8 * gb,u8 gb_len)6393df40eb3SKrzysztof Kozlowski int nfc_set_remote_general_bytes(struct nfc_dev *dev, const u8 *gb, u8 gb_len)
640541d920bSSamuel Ortiz {
6410a40acb2SSamuel Ortiz 	pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len);
642541d920bSSamuel Ortiz 
643d646960fSSamuel Ortiz 	return nfc_llcp_set_remote_gb(dev, gb, gb_len);
644541d920bSSamuel Ortiz }
645541d920bSSamuel Ortiz EXPORT_SYMBOL(nfc_set_remote_general_bytes);
646541d920bSSamuel Ortiz 
nfc_get_local_general_bytes(struct nfc_dev * dev,size_t * gb_len)647ab73b751SSamuel Ortiz u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len)
648ab73b751SSamuel Ortiz {
649ab73b751SSamuel Ortiz 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
650ab73b751SSamuel Ortiz 
651ab73b751SSamuel Ortiz 	return nfc_llcp_general_bytes(dev, gb_len);
652ab73b751SSamuel Ortiz }
653ab73b751SSamuel Ortiz EXPORT_SYMBOL(nfc_get_local_general_bytes);
654ab73b751SSamuel Ortiz 
nfc_tm_data_received(struct nfc_dev * dev,struct sk_buff * skb)65573167cedSSamuel Ortiz int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb)
65673167cedSSamuel Ortiz {
65773167cedSSamuel Ortiz 	/* Only LLCP target mode for now */
65873167cedSSamuel Ortiz 	if (dev->dep_link_up == false) {
65973167cedSSamuel Ortiz 		kfree_skb(skb);
66073167cedSSamuel Ortiz 		return -ENOLINK;
66173167cedSSamuel Ortiz 	}
66273167cedSSamuel Ortiz 
66373167cedSSamuel Ortiz 	return nfc_llcp_data_received(dev, skb);
66473167cedSSamuel Ortiz }
66573167cedSSamuel Ortiz EXPORT_SYMBOL(nfc_tm_data_received);
66673167cedSSamuel Ortiz 
nfc_tm_activated(struct nfc_dev * dev,u32 protocol,u8 comm_mode,const u8 * gb,size_t gb_len)667fc40a8c1SSamuel Ortiz int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode,
6683df40eb3SKrzysztof Kozlowski 		     const u8 *gb, size_t gb_len)
669fc40a8c1SSamuel Ortiz {
670fc40a8c1SSamuel Ortiz 	int rc;
671fc40a8c1SSamuel Ortiz 
672fc40a8c1SSamuel Ortiz 	device_lock(&dev->dev);
673fc40a8c1SSamuel Ortiz 
674fc40a8c1SSamuel Ortiz 	dev->polling = false;
675fc40a8c1SSamuel Ortiz 
676fc40a8c1SSamuel Ortiz 	if (gb != NULL) {
677fc40a8c1SSamuel Ortiz 		rc = nfc_set_remote_general_bytes(dev, gb, gb_len);
678fc40a8c1SSamuel Ortiz 		if (rc < 0)
679fc40a8c1SSamuel Ortiz 			goto out;
680fc40a8c1SSamuel Ortiz 	}
681fc40a8c1SSamuel Ortiz 
682f212ad5eSSamuel Ortiz 	dev->rf_mode = NFC_RF_TARGET;
683f212ad5eSSamuel Ortiz 
684fc40a8c1SSamuel Ortiz 	if (protocol == NFC_PROTO_NFC_DEP_MASK)
685fc40a8c1SSamuel Ortiz 		nfc_dep_link_is_up(dev, 0, comm_mode, NFC_RF_TARGET);
686fc40a8c1SSamuel Ortiz 
687fc40a8c1SSamuel Ortiz 	rc = nfc_genl_tm_activated(dev, protocol);
688fc40a8c1SSamuel Ortiz 
689fc40a8c1SSamuel Ortiz out:
690fc40a8c1SSamuel Ortiz 	device_unlock(&dev->dev);
691fc40a8c1SSamuel Ortiz 
692fc40a8c1SSamuel Ortiz 	return rc;
693fc40a8c1SSamuel Ortiz }
694fc40a8c1SSamuel Ortiz EXPORT_SYMBOL(nfc_tm_activated);
695fc40a8c1SSamuel Ortiz 
nfc_tm_deactivated(struct nfc_dev * dev)696fc40a8c1SSamuel Ortiz int nfc_tm_deactivated(struct nfc_dev *dev)
697fc40a8c1SSamuel Ortiz {
698fc40a8c1SSamuel Ortiz 	dev->dep_link_up = false;
6995bcf099cSThierry Escande 	dev->rf_mode = NFC_RF_NONE;
700fc40a8c1SSamuel Ortiz 
701fc40a8c1SSamuel Ortiz 	return nfc_genl_tm_deactivated(dev);
702fc40a8c1SSamuel Ortiz }
703fc40a8c1SSamuel Ortiz EXPORT_SYMBOL(nfc_tm_deactivated);
704fc40a8c1SSamuel Ortiz 
7053e256b8fSLauro Ramos Venancio /**
7067c7cd3bfSSamuel Ortiz  * nfc_alloc_send_skb - allocate a skb for data exchange responses
7073e256b8fSLauro Ramos Venancio  *
7087cdda1c1SAndrew Lunn  * @dev: device sending the response
7097cdda1c1SAndrew Lunn  * @sk: socket sending the response
7107cdda1c1SAndrew Lunn  * @flags: MSG_DONTWAIT flag
7113e256b8fSLauro Ramos Venancio  * @size: size to allocate
7127cdda1c1SAndrew Lunn  * @err: pointer to memory to store the error code
7133e256b8fSLauro Ramos Venancio  */
nfc_alloc_send_skb(struct nfc_dev * dev,struct sock * sk,unsigned int flags,unsigned int size,unsigned int * err)7147c7cd3bfSSamuel Ortiz struct sk_buff *nfc_alloc_send_skb(struct nfc_dev *dev, struct sock *sk,
7157c7cd3bfSSamuel Ortiz 				   unsigned int flags, unsigned int size,
7167c7cd3bfSSamuel Ortiz 				   unsigned int *err)
7177c7cd3bfSSamuel Ortiz {
7187c7cd3bfSSamuel Ortiz 	struct sk_buff *skb;
7197c7cd3bfSSamuel Ortiz 	unsigned int total_size;
7207c7cd3bfSSamuel Ortiz 
7217c7cd3bfSSamuel Ortiz 	total_size = size +
7227c7cd3bfSSamuel Ortiz 		dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
7237c7cd3bfSSamuel Ortiz 
7247c7cd3bfSSamuel Ortiz 	skb = sock_alloc_send_skb(sk, total_size, flags & MSG_DONTWAIT, err);
7257c7cd3bfSSamuel Ortiz 	if (skb)
7267c7cd3bfSSamuel Ortiz 		skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
7277c7cd3bfSSamuel Ortiz 
7287c7cd3bfSSamuel Ortiz 	return skb;
7297c7cd3bfSSamuel Ortiz }
7307c7cd3bfSSamuel Ortiz 
7317c7cd3bfSSamuel Ortiz /**
7327c7cd3bfSSamuel Ortiz  * nfc_alloc_recv_skb - allocate a skb for data exchange responses
7337c7cd3bfSSamuel Ortiz  *
7347c7cd3bfSSamuel Ortiz  * @size: size to allocate
7357c7cd3bfSSamuel Ortiz  * @gfp: gfp flags
7367c7cd3bfSSamuel Ortiz  */
nfc_alloc_recv_skb(unsigned int size,gfp_t gfp)7377c7cd3bfSSamuel Ortiz struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp)
7383e256b8fSLauro Ramos Venancio {
7393e256b8fSLauro Ramos Venancio 	struct sk_buff *skb;
7403e256b8fSLauro Ramos Venancio 	unsigned int total_size;
7413e256b8fSLauro Ramos Venancio 
7423e256b8fSLauro Ramos Venancio 	total_size = size + 1;
7433e256b8fSLauro Ramos Venancio 	skb = alloc_skb(total_size, gfp);
7443e256b8fSLauro Ramos Venancio 
7453e256b8fSLauro Ramos Venancio 	if (skb)
7463e256b8fSLauro Ramos Venancio 		skb_reserve(skb, 1);
7473e256b8fSLauro Ramos Venancio 
7483e256b8fSLauro Ramos Venancio 	return skb;
7493e256b8fSLauro Ramos Venancio }
7507c7cd3bfSSamuel Ortiz EXPORT_SYMBOL(nfc_alloc_recv_skb);
7513e256b8fSLauro Ramos Venancio 
7524d12b8b1SLauro Ramos Venancio /**
7534d12b8b1SLauro Ramos Venancio  * nfc_targets_found - inform that targets were found
7544d12b8b1SLauro Ramos Venancio  *
7554d12b8b1SLauro Ramos Venancio  * @dev: The nfc device that found the targets
7564d12b8b1SLauro Ramos Venancio  * @targets: array of nfc targets found
757ffbab1c9SAndrew Lunn  * @n_targets: targets array size
7584d12b8b1SLauro Ramos Venancio  *
7594d12b8b1SLauro Ramos Venancio  * The device driver must call this function when one or many nfc targets
7604d12b8b1SLauro Ramos Venancio  * are found. After calling this function, the device driver must stop
7614d12b8b1SLauro Ramos Venancio  * polling for targets.
762d94f9c55SEric Lapuyade  * NOTE: This function can be called with targets=NULL and n_targets=0 to
763d94f9c55SEric Lapuyade  * notify a driver error, meaning that the polling operation cannot complete.
764d4ccb132SEric Lapuyade  * IMPORTANT: this function must not be called from an atomic context.
765d4ccb132SEric Lapuyade  * In addition, it must also not be called from a context that would prevent
766d4ccb132SEric Lapuyade  * the NFC Core to call other nfc ops entry point concurrently.
7674d12b8b1SLauro Ramos Venancio  */
nfc_targets_found(struct nfc_dev * dev,struct nfc_target * targets,int n_targets)7680a40acb2SSamuel Ortiz int nfc_targets_found(struct nfc_dev *dev,
7690a40acb2SSamuel Ortiz 		      struct nfc_target *targets, int n_targets)
7704d12b8b1SLauro Ramos Venancio {
771c4fbb651SSamuel Ortiz 	int i;
772c4fbb651SSamuel Ortiz 
77320c239c1SJoe Perches 	pr_debug("dev_name=%s n_targets=%d\n", dev_name(&dev->dev), n_targets);
7744d12b8b1SLauro Ramos Venancio 
775c4fbb651SSamuel Ortiz 	for (i = 0; i < n_targets; i++)
77601ae0eeaSEric Lapuyade 		targets[i].idx = dev->target_next_idx++;
777c4fbb651SSamuel Ortiz 
778d4ccb132SEric Lapuyade 	device_lock(&dev->dev);
7794d12b8b1SLauro Ramos Venancio 
7808668fdd6SEric Lapuyade 	if (dev->polling == false) {
7818668fdd6SEric Lapuyade 		device_unlock(&dev->dev);
7828668fdd6SEric Lapuyade 		return 0;
7838668fdd6SEric Lapuyade 	}
7848668fdd6SEric Lapuyade 
7858668fdd6SEric Lapuyade 	dev->polling = false;
7868668fdd6SEric Lapuyade 
7874d12b8b1SLauro Ramos Venancio 	dev->targets_generation++;
7884d12b8b1SLauro Ramos Venancio 
7894d12b8b1SLauro Ramos Venancio 	kfree(dev->targets);
790d94f9c55SEric Lapuyade 	dev->targets = NULL;
791d94f9c55SEric Lapuyade 
792d94f9c55SEric Lapuyade 	if (targets) {
793d94f9c55SEric Lapuyade 		dev->targets = kmemdup(targets,
794d94f9c55SEric Lapuyade 				       n_targets * sizeof(struct nfc_target),
7954d12b8b1SLauro Ramos Venancio 				       GFP_ATOMIC);
7964d12b8b1SLauro Ramos Venancio 
7974d12b8b1SLauro Ramos Venancio 		if (!dev->targets) {
7984d12b8b1SLauro Ramos Venancio 			dev->n_targets = 0;
799d4ccb132SEric Lapuyade 			device_unlock(&dev->dev);
8004d12b8b1SLauro Ramos Venancio 			return -ENOMEM;
8014d12b8b1SLauro Ramos Venancio 		}
802d94f9c55SEric Lapuyade 	}
8034d12b8b1SLauro Ramos Venancio 
8044d12b8b1SLauro Ramos Venancio 	dev->n_targets = n_targets;
805d4ccb132SEric Lapuyade 	device_unlock(&dev->dev);
8064d12b8b1SLauro Ramos Venancio 
8074d12b8b1SLauro Ramos Venancio 	nfc_genl_targets_found(dev);
8084d12b8b1SLauro Ramos Venancio 
8094d12b8b1SLauro Ramos Venancio 	return 0;
8104d12b8b1SLauro Ramos Venancio }
8114d12b8b1SLauro Ramos Venancio EXPORT_SYMBOL(nfc_targets_found);
8124d12b8b1SLauro Ramos Venancio 
813d4ccb132SEric Lapuyade /**
814d4ccb132SEric Lapuyade  * nfc_target_lost - inform that an activated target went out of field
815d4ccb132SEric Lapuyade  *
816d4ccb132SEric Lapuyade  * @dev: The nfc device that had the activated target in field
817d4ccb132SEric Lapuyade  * @target_idx: the nfc index of the target
818d4ccb132SEric Lapuyade  *
819d4ccb132SEric Lapuyade  * The device driver must call this function when the activated target
820d4ccb132SEric Lapuyade  * goes out of the field.
821d4ccb132SEric Lapuyade  * IMPORTANT: this function must not be called from an atomic context.
822d4ccb132SEric Lapuyade  * In addition, it must also not be called from a context that would prevent
823d4ccb132SEric Lapuyade  * the NFC Core to call other nfc ops entry point concurrently.
824d4ccb132SEric Lapuyade  */
nfc_target_lost(struct nfc_dev * dev,u32 target_idx)825e1da0efaSEric Lapuyade int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
826e1da0efaSEric Lapuyade {
827f2479c0aSKrzysztof Kozlowski 	const struct nfc_target *tg;
828e1da0efaSEric Lapuyade 	int i;
829e1da0efaSEric Lapuyade 
830e1da0efaSEric Lapuyade 	pr_debug("dev_name %s n_target %d\n", dev_name(&dev->dev), target_idx);
831e1da0efaSEric Lapuyade 
832d4ccb132SEric Lapuyade 	device_lock(&dev->dev);
833e1da0efaSEric Lapuyade 
834e1da0efaSEric Lapuyade 	for (i = 0; i < dev->n_targets; i++) {
835e1da0efaSEric Lapuyade 		tg = &dev->targets[i];
836e1da0efaSEric Lapuyade 		if (tg->idx == target_idx)
837e1da0efaSEric Lapuyade 			break;
838e1da0efaSEric Lapuyade 	}
839e1da0efaSEric Lapuyade 
840e1da0efaSEric Lapuyade 	if (i == dev->n_targets) {
841d4ccb132SEric Lapuyade 		device_unlock(&dev->dev);
842e1da0efaSEric Lapuyade 		return -EINVAL;
843e1da0efaSEric Lapuyade 	}
844e1da0efaSEric Lapuyade 
845e1da0efaSEric Lapuyade 	dev->targets_generation++;
846e1da0efaSEric Lapuyade 	dev->n_targets--;
84790099433SEric Lapuyade 	dev->active_target = NULL;
848e1da0efaSEric Lapuyade 
849e1da0efaSEric Lapuyade 	if (dev->n_targets) {
850e1da0efaSEric Lapuyade 		memcpy(&dev->targets[i], &dev->targets[i + 1],
851e1da0efaSEric Lapuyade 		       (dev->n_targets - i) * sizeof(struct nfc_target));
852e1da0efaSEric Lapuyade 	} else {
853e1da0efaSEric Lapuyade 		kfree(dev->targets);
854e1da0efaSEric Lapuyade 		dev->targets = NULL;
855e1da0efaSEric Lapuyade 	}
856e1da0efaSEric Lapuyade 
857d4ccb132SEric Lapuyade 	device_unlock(&dev->dev);
858e1da0efaSEric Lapuyade 
859e1da0efaSEric Lapuyade 	nfc_genl_target_lost(dev, target_idx);
860e1da0efaSEric Lapuyade 
861e1da0efaSEric Lapuyade 	return 0;
862e1da0efaSEric Lapuyade }
863e1da0efaSEric Lapuyade EXPORT_SYMBOL(nfc_target_lost);
864e1da0efaSEric Lapuyade 
nfc_driver_failure(struct nfc_dev * dev,int err)8659eb334acSEric Lapuyade inline void nfc_driver_failure(struct nfc_dev *dev, int err)
866456411caSEric Lapuyade {
8679eb334acSEric Lapuyade 	nfc_targets_found(dev, NULL, 0);
868456411caSEric Lapuyade }
869456411caSEric Lapuyade EXPORT_SYMBOL(nfc_driver_failure);
870456411caSEric Lapuyade 
nfc_add_se(struct nfc_dev * dev,u32 se_idx,u16 type)871fed7c25eSSamuel Ortiz int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
872fed7c25eSSamuel Ortiz {
873c531c9ecSSamuel Ortiz 	struct nfc_se *se;
8742757c372SSamuel Ortiz 	int rc;
875fed7c25eSSamuel Ortiz 
876fed7c25eSSamuel Ortiz 	pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
877fed7c25eSSamuel Ortiz 
878d8eb18eeSArron Wang 	se = nfc_find_se(dev, se_idx);
879c531c9ecSSamuel Ortiz 	if (se)
880fed7c25eSSamuel Ortiz 		return -EALREADY;
881fed7c25eSSamuel Ortiz 
882fed7c25eSSamuel Ortiz 	se = kzalloc(sizeof(struct nfc_se), GFP_KERNEL);
883fed7c25eSSamuel Ortiz 	if (!se)
884fed7c25eSSamuel Ortiz 		return -ENOMEM;
885fed7c25eSSamuel Ortiz 
886fed7c25eSSamuel Ortiz 	se->idx = se_idx;
887fed7c25eSSamuel Ortiz 	se->type = type;
888fed7c25eSSamuel Ortiz 	se->state = NFC_SE_DISABLED;
889fed7c25eSSamuel Ortiz 	INIT_LIST_HEAD(&se->list);
890fed7c25eSSamuel Ortiz 
891fed7c25eSSamuel Ortiz 	list_add(&se->list, &dev->secure_elements);
892fed7c25eSSamuel Ortiz 
8932757c372SSamuel Ortiz 	rc = nfc_genl_se_added(dev, se_idx, type);
8942757c372SSamuel Ortiz 	if (rc < 0) {
8952757c372SSamuel Ortiz 		list_del(&se->list);
8962757c372SSamuel Ortiz 		kfree(se);
8972757c372SSamuel Ortiz 
8982757c372SSamuel Ortiz 		return rc;
8992757c372SSamuel Ortiz 	}
9002757c372SSamuel Ortiz 
901fed7c25eSSamuel Ortiz 	return 0;
902fed7c25eSSamuel Ortiz }
903fed7c25eSSamuel Ortiz EXPORT_SYMBOL(nfc_add_se);
904fed7c25eSSamuel Ortiz 
nfc_remove_se(struct nfc_dev * dev,u32 se_idx)905fed7c25eSSamuel Ortiz int nfc_remove_se(struct nfc_dev *dev, u32 se_idx)
906fed7c25eSSamuel Ortiz {
907fed7c25eSSamuel Ortiz 	struct nfc_se *se, *n;
9082757c372SSamuel Ortiz 	int rc;
909fed7c25eSSamuel Ortiz 
910fed7c25eSSamuel Ortiz 	pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
911fed7c25eSSamuel Ortiz 
912fed7c25eSSamuel Ortiz 	list_for_each_entry_safe(se, n, &dev->secure_elements, list)
913fed7c25eSSamuel Ortiz 		if (se->idx == se_idx) {
9142757c372SSamuel Ortiz 			rc = nfc_genl_se_removed(dev, se_idx);
9152757c372SSamuel Ortiz 			if (rc < 0)
9162757c372SSamuel Ortiz 				return rc;
9172757c372SSamuel Ortiz 
918fed7c25eSSamuel Ortiz 			list_del(&se->list);
919fed7c25eSSamuel Ortiz 			kfree(se);
920fed7c25eSSamuel Ortiz 
921fed7c25eSSamuel Ortiz 			return 0;
922fed7c25eSSamuel Ortiz 		}
923fed7c25eSSamuel Ortiz 
924fed7c25eSSamuel Ortiz 	return -EINVAL;
925fed7c25eSSamuel Ortiz }
926fed7c25eSSamuel Ortiz EXPORT_SYMBOL(nfc_remove_se);
927fed7c25eSSamuel Ortiz 
nfc_se_transaction(struct nfc_dev * dev,u8 se_idx,struct nfc_evt_transaction * evt_transaction)928447b27c4SChristophe Ricard int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx,
929447b27c4SChristophe Ricard 		       struct nfc_evt_transaction *evt_transaction)
930447b27c4SChristophe Ricard {
931447b27c4SChristophe Ricard 	int rc;
932447b27c4SChristophe Ricard 
933447b27c4SChristophe Ricard 	pr_debug("transaction: %x\n", se_idx);
934447b27c4SChristophe Ricard 
935447b27c4SChristophe Ricard 	device_lock(&dev->dev);
936447b27c4SChristophe Ricard 
937447b27c4SChristophe Ricard 	if (!evt_transaction) {
938447b27c4SChristophe Ricard 		rc = -EPROTO;
939447b27c4SChristophe Ricard 		goto out;
940447b27c4SChristophe Ricard 	}
941447b27c4SChristophe Ricard 
942447b27c4SChristophe Ricard 	rc = nfc_genl_se_transaction(dev, se_idx, evt_transaction);
943447b27c4SChristophe Ricard out:
944447b27c4SChristophe Ricard 	device_unlock(&dev->dev);
945447b27c4SChristophe Ricard 	return rc;
946447b27c4SChristophe Ricard }
947447b27c4SChristophe Ricard EXPORT_SYMBOL(nfc_se_transaction);
948447b27c4SChristophe Ricard 
nfc_se_connectivity(struct nfc_dev * dev,u8 se_idx)9499afec6d3SChristophe Ricard int nfc_se_connectivity(struct nfc_dev *dev, u8 se_idx)
9509afec6d3SChristophe Ricard {
9519afec6d3SChristophe Ricard 	int rc;
9529afec6d3SChristophe Ricard 
9539afec6d3SChristophe Ricard 	pr_debug("connectivity: %x\n", se_idx);
9549afec6d3SChristophe Ricard 
9559afec6d3SChristophe Ricard 	device_lock(&dev->dev);
9569afec6d3SChristophe Ricard 	rc = nfc_genl_se_connectivity(dev, se_idx);
9579afec6d3SChristophe Ricard 	device_unlock(&dev->dev);
9589afec6d3SChristophe Ricard 	return rc;
9599afec6d3SChristophe Ricard }
9609afec6d3SChristophe Ricard EXPORT_SYMBOL(nfc_se_connectivity);
9619afec6d3SChristophe Ricard 
nfc_release(struct device * d)9623e256b8fSLauro Ramos Venancio static void nfc_release(struct device *d)
9633e256b8fSLauro Ramos Venancio {
9643e256b8fSLauro Ramos Venancio 	struct nfc_dev *dev = to_nfc_dev(d);
965ee656e9dSSamuel Ortiz 	struct nfc_se *se, *n;
9663e256b8fSLauro Ramos Venancio 
96720c239c1SJoe Perches 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
9683e256b8fSLauro Ramos Venancio 
9694d12b8b1SLauro Ramos Venancio 	nfc_genl_data_exit(&dev->genl_data);
9704d12b8b1SLauro Ramos Venancio 	kfree(dev->targets);
971ee656e9dSSamuel Ortiz 
972ee656e9dSSamuel Ortiz 	list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
973ee656e9dSSamuel Ortiz 			nfc_genl_se_removed(dev, se->idx);
974ee656e9dSSamuel Ortiz 			list_del(&se->list);
975ee656e9dSSamuel Ortiz 			kfree(se);
976ee656e9dSSamuel Ortiz 	}
977ee656e9dSSamuel Ortiz 
978*91179917Skeliu 	ida_free(&nfc_index_ida, dev->idx);
97920777bc5SJohan Hovold 
9803e256b8fSLauro Ramos Venancio 	kfree(dev);
9813e256b8fSLauro Ramos Venancio }
9823e256b8fSLauro Ramos Venancio 
nfc_check_pres_work(struct work_struct * work)983c8d56ae7SEric Lapuyade static void nfc_check_pres_work(struct work_struct *work)
984c8d56ae7SEric Lapuyade {
985c8d56ae7SEric Lapuyade 	struct nfc_dev *dev = container_of(work, struct nfc_dev,
986c8d56ae7SEric Lapuyade 					   check_pres_work);
987c8d56ae7SEric Lapuyade 	int rc;
988c8d56ae7SEric Lapuyade 
989c8d56ae7SEric Lapuyade 	device_lock(&dev->dev);
990c8d56ae7SEric Lapuyade 
99190099433SEric Lapuyade 	if (dev->active_target && timer_pending(&dev->check_pres_timer) == 0) {
99290099433SEric Lapuyade 		rc = dev->ops->check_presence(dev, dev->active_target);
993632c016aSEric Lapuyade 		if (rc == -EOPNOTSUPP)
994632c016aSEric Lapuyade 			goto exit;
995f0c91038SEric Lapuyade 		if (rc) {
996d4ccb132SEric Lapuyade 			u32 active_target_idx = dev->active_target->idx;
997d4ccb132SEric Lapuyade 			device_unlock(&dev->dev);
998d4ccb132SEric Lapuyade 			nfc_target_lost(dev, active_target_idx);
999d4ccb132SEric Lapuyade 			return;
1000c8d56ae7SEric Lapuyade 		}
1001f0c91038SEric Lapuyade 
1002f0c91038SEric Lapuyade 		if (!dev->shutting_down)
1003f0c91038SEric Lapuyade 			mod_timer(&dev->check_pres_timer, jiffies +
1004f0c91038SEric Lapuyade 				  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
1005c8d56ae7SEric Lapuyade 	}
1006c8d56ae7SEric Lapuyade 
1007632c016aSEric Lapuyade exit:
1008c8d56ae7SEric Lapuyade 	device_unlock(&dev->dev);
1009c8d56ae7SEric Lapuyade }
1010c8d56ae7SEric Lapuyade 
nfc_check_pres_timeout(struct timer_list * t)10114b519bb4SAllen Pais static void nfc_check_pres_timeout(struct timer_list *t)
1012c8d56ae7SEric Lapuyade {
10134b519bb4SAllen Pais 	struct nfc_dev *dev = from_timer(dev, t, check_pres_timer);
1014c8d56ae7SEric Lapuyade 
1015916082b0SLinus Torvalds 	schedule_work(&dev->check_pres_work);
1016c8d56ae7SEric Lapuyade }
1017c8d56ae7SEric Lapuyade 
10183e256b8fSLauro Ramos Venancio struct class nfc_class = {
10193e256b8fSLauro Ramos Venancio 	.name = "nfc",
10203e256b8fSLauro Ramos Venancio 	.dev_release = nfc_release,
10213e256b8fSLauro Ramos Venancio };
10223e256b8fSLauro Ramos Venancio EXPORT_SYMBOL(nfc_class);
10233e256b8fSLauro Ramos Venancio 
match_idx(struct device * d,const void * data)10249f3b795aSMichał Mirosław static int match_idx(struct device *d, const void *data)
10253e256b8fSLauro Ramos Venancio {
10263e256b8fSLauro Ramos Venancio 	struct nfc_dev *dev = to_nfc_dev(d);
10279f3b795aSMichał Mirosław 	const unsigned int *idx = data;
10283e256b8fSLauro Ramos Venancio 
10293e256b8fSLauro Ramos Venancio 	return dev->idx == *idx;
10303e256b8fSLauro Ramos Venancio }
10313e256b8fSLauro Ramos Venancio 
nfc_get_device(unsigned int idx)103295c96174SEric Dumazet struct nfc_dev *nfc_get_device(unsigned int idx)
10333e256b8fSLauro Ramos Venancio {
10343e256b8fSLauro Ramos Venancio 	struct device *d;
10353e256b8fSLauro Ramos Venancio 
10363e256b8fSLauro Ramos Venancio 	d = class_find_device(&nfc_class, NULL, &idx, match_idx);
10373e256b8fSLauro Ramos Venancio 	if (!d)
10383e256b8fSLauro Ramos Venancio 		return NULL;
10393e256b8fSLauro Ramos Venancio 
10403e256b8fSLauro Ramos Venancio 	return to_nfc_dev(d);
10413e256b8fSLauro Ramos Venancio }
10423e256b8fSLauro Ramos Venancio 
10433e256b8fSLauro Ramos Venancio /**
10443e256b8fSLauro Ramos Venancio  * nfc_allocate_device - allocate a new nfc device
10453e256b8fSLauro Ramos Venancio  *
10463e256b8fSLauro Ramos Venancio  * @ops: device operations
10473e256b8fSLauro Ramos Venancio  * @supported_protocols: NFC protocols supported by the device
10487cdda1c1SAndrew Lunn  * @tx_headroom: reserved space at beginning of skb
10497cdda1c1SAndrew Lunn  * @tx_tailroom: reserved space at end of skb
10503e256b8fSLauro Ramos Venancio  */
nfc_allocate_device(const struct nfc_ops * ops,u32 supported_protocols,int tx_headroom,int tx_tailroom)1051f6c802a7SKrzysztof Kozlowski struct nfc_dev *nfc_allocate_device(const struct nfc_ops *ops,
1052e8753043SSamuel Ortiz 				    u32 supported_protocols,
10530a40acb2SSamuel Ortiz 				    int tx_headroom, int tx_tailroom)
10543e256b8fSLauro Ramos Venancio {
10553e256b8fSLauro Ramos Venancio 	struct nfc_dev *dev;
105620777bc5SJohan Hovold 	int rc;
10573e256b8fSLauro Ramos Venancio 
10583e256b8fSLauro Ramos Venancio 	if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
1059be9ae4ceSSamuel Ortiz 	    !ops->deactivate_target || !ops->im_transceive)
10603e256b8fSLauro Ramos Venancio 		return NULL;
10613e256b8fSLauro Ramos Venancio 
10623e256b8fSLauro Ramos Venancio 	if (!supported_protocols)
10633e256b8fSLauro Ramos Venancio 		return NULL;
10643e256b8fSLauro Ramos Venancio 
10653e256b8fSLauro Ramos Venancio 	dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
10663e256b8fSLauro Ramos Venancio 	if (!dev)
10673e256b8fSLauro Ramos Venancio 		return NULL;
10683e256b8fSLauro Ramos Venancio 
1069*91179917Skeliu 	rc = ida_alloc(&nfc_index_ida, GFP_KERNEL);
107020777bc5SJohan Hovold 	if (rc < 0)
107120777bc5SJohan Hovold 		goto err_free_dev;
107220777bc5SJohan Hovold 	dev->idx = rc;
107320777bc5SJohan Hovold 
107420777bc5SJohan Hovold 	dev->dev.class = &nfc_class;
107520777bc5SJohan Hovold 	dev_set_name(&dev->dev, "nfc%d", dev->idx);
107620777bc5SJohan Hovold 	device_initialize(&dev->dev);
107720777bc5SJohan Hovold 
10783e256b8fSLauro Ramos Venancio 	dev->ops = ops;
10793e256b8fSLauro Ramos Venancio 	dev->supported_protocols = supported_protocols;
1080e8753043SSamuel Ortiz 	dev->tx_headroom = tx_headroom;
1081e8753043SSamuel Ortiz 	dev->tx_tailroom = tx_tailroom;
1082fed7c25eSSamuel Ortiz 	INIT_LIST_HEAD(&dev->secure_elements);
10833e256b8fSLauro Ramos Venancio 
10844d12b8b1SLauro Ramos Venancio 	nfc_genl_data_init(&dev->genl_data);
10854d12b8b1SLauro Ramos Venancio 
10865bcf099cSThierry Escande 	dev->rf_mode = NFC_RF_NONE;
1087d4ccb132SEric Lapuyade 
10884d12b8b1SLauro Ramos Venancio 	/* first generation must not be 0 */
10894d12b8b1SLauro Ramos Venancio 	dev->targets_generation = 1;
10904d12b8b1SLauro Ramos Venancio 
1091c8d56ae7SEric Lapuyade 	if (ops->check_presence) {
10924b519bb4SAllen Pais 		timer_setup(&dev->check_pres_timer, nfc_check_pres_timeout, 0);
1093c8d56ae7SEric Lapuyade 		INIT_WORK(&dev->check_pres_work, nfc_check_pres_work);
1094c8d56ae7SEric Lapuyade 	}
1095c8d56ae7SEric Lapuyade 
10963e256b8fSLauro Ramos Venancio 	return dev;
109720777bc5SJohan Hovold 
109820777bc5SJohan Hovold err_free_dev:
109920777bc5SJohan Hovold 	kfree(dev);
110020777bc5SJohan Hovold 
1101c45e3e4cSJohan Hovold 	return NULL;
11023e256b8fSLauro Ramos Venancio }
11033e256b8fSLauro Ramos Venancio EXPORT_SYMBOL(nfc_allocate_device);
11043e256b8fSLauro Ramos Venancio 
11053e256b8fSLauro Ramos Venancio /**
11063e256b8fSLauro Ramos Venancio  * nfc_register_device - register a nfc device in the nfc subsystem
11073e256b8fSLauro Ramos Venancio  *
11083e256b8fSLauro Ramos Venancio  * @dev: The nfc device to register
11093e256b8fSLauro Ramos Venancio  */
nfc_register_device(struct nfc_dev * dev)11103e256b8fSLauro Ramos Venancio int nfc_register_device(struct nfc_dev *dev)
11113e256b8fSLauro Ramos Venancio {
11123e256b8fSLauro Ramos Venancio 	int rc;
11133e256b8fSLauro Ramos Venancio 
111420c239c1SJoe Perches 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
11153e256b8fSLauro Ramos Venancio 
11163e256b8fSLauro Ramos Venancio 	mutex_lock(&nfc_devlist_mutex);
11173e256b8fSLauro Ramos Venancio 	nfc_devlist_generation++;
11183e256b8fSLauro Ramos Venancio 	rc = device_add(&dev->dev);
11193e256b8fSLauro Ramos Venancio 	mutex_unlock(&nfc_devlist_mutex);
11203e256b8fSLauro Ramos Venancio 
11214d12b8b1SLauro Ramos Venancio 	if (rc < 0)
11223e256b8fSLauro Ramos Venancio 		return rc;
11234d12b8b1SLauro Ramos Venancio 
1124d646960fSSamuel Ortiz 	rc = nfc_llcp_register_device(dev);
1125d646960fSSamuel Ortiz 	if (rc)
1126d646960fSSamuel Ortiz 		pr_err("Could not register llcp device\n");
1127d646960fSSamuel Ortiz 
11283e3b5dfcSLin Ma 	device_lock(&dev->dev);
1129be055b2fSSamuel Ortiz 	dev->rfkill = rfkill_alloc(dev_name(&dev->dev), &dev->dev,
1130be055b2fSSamuel Ortiz 				   RFKILL_TYPE_NFC, &nfc_rfkill_ops, dev);
1131be055b2fSSamuel Ortiz 	if (dev->rfkill) {
1132be055b2fSSamuel Ortiz 		if (rfkill_register(dev->rfkill) < 0) {
1133be055b2fSSamuel Ortiz 			rfkill_destroy(dev->rfkill);
1134be055b2fSSamuel Ortiz 			dev->rfkill = NULL;
1135be055b2fSSamuel Ortiz 		}
1136be055b2fSSamuel Ortiz 	}
1137da5c0f11SDuoming Zhou 	dev->shutting_down = false;
11383e3b5dfcSLin Ma 	device_unlock(&dev->dev);
11393e3b5dfcSLin Ma 
11403e3b5dfcSLin Ma 	rc = nfc_genl_device_added(dev);
11413e3b5dfcSLin Ma 	if (rc)
11423e3b5dfcSLin Ma 		pr_debug("The userspace won't be notified that the device %s was added\n",
11433e3b5dfcSLin Ma 			 dev_name(&dev->dev));
1144be055b2fSSamuel Ortiz 
11454d12b8b1SLauro Ramos Venancio 	return 0;
11463e256b8fSLauro Ramos Venancio }
11473e256b8fSLauro Ramos Venancio EXPORT_SYMBOL(nfc_register_device);
11483e256b8fSLauro Ramos Venancio 
11493e256b8fSLauro Ramos Venancio /**
11503e256b8fSLauro Ramos Venancio  * nfc_unregister_device - unregister a nfc device in the nfc subsystem
11513e256b8fSLauro Ramos Venancio  *
11523e256b8fSLauro Ramos Venancio  * @dev: The nfc device to unregister
11533e256b8fSLauro Ramos Venancio  */
nfc_unregister_device(struct nfc_dev * dev)11543e256b8fSLauro Ramos Venancio void nfc_unregister_device(struct nfc_dev *dev)
11553e256b8fSLauro Ramos Venancio {
115620777bc5SJohan Hovold 	int rc;
11574d12b8b1SLauro Ramos Venancio 
115820c239c1SJoe Perches 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
11593e256b8fSLauro Ramos Venancio 
11603e3b5dfcSLin Ma 	rc = nfc_genl_device_removed(dev);
11613e3b5dfcSLin Ma 	if (rc)
11623e3b5dfcSLin Ma 		pr_debug("The userspace won't be notified that the device %s "
11633e3b5dfcSLin Ma 			 "was removed\n", dev_name(&dev->dev));
11643e3b5dfcSLin Ma 
11653e3b5dfcSLin Ma 	device_lock(&dev->dev);
1166be055b2fSSamuel Ortiz 	if (dev->rfkill) {
1167be055b2fSSamuel Ortiz 		rfkill_unregister(dev->rfkill);
1168be055b2fSSamuel Ortiz 		rfkill_destroy(dev->rfkill);
11691b0e8141SLin Ma 		dev->rfkill = NULL;
1170be055b2fSSamuel Ortiz 	}
1171da5c0f11SDuoming Zhou 	dev->shutting_down = true;
11723e3b5dfcSLin Ma 	device_unlock(&dev->dev);
1173be055b2fSSamuel Ortiz 
1174f0c91038SEric Lapuyade 	if (dev->ops->check_presence) {
1175f0c91038SEric Lapuyade 		del_timer_sync(&dev->check_pres_timer);
1176f0c91038SEric Lapuyade 		cancel_work_sync(&dev->check_pres_work);
1177f0c91038SEric Lapuyade 	}
1178d646960fSSamuel Ortiz 
1179f0c91038SEric Lapuyade 	nfc_llcp_unregister_device(dev);
1180f0c91038SEric Lapuyade 
1181f0c91038SEric Lapuyade 	mutex_lock(&nfc_devlist_mutex);
1182f0c91038SEric Lapuyade 	nfc_devlist_generation++;
1183f0c91038SEric Lapuyade 	device_del(&dev->dev);
1184f0c91038SEric Lapuyade 	mutex_unlock(&nfc_devlist_mutex);
11853e256b8fSLauro Ramos Venancio }
11863e256b8fSLauro Ramos Venancio EXPORT_SYMBOL(nfc_unregister_device);
11873e256b8fSLauro Ramos Venancio 
nfc_init(void)11883e256b8fSLauro Ramos Venancio static int __init nfc_init(void)
11893e256b8fSLauro Ramos Venancio {
11904d12b8b1SLauro Ramos Venancio 	int rc;
11914d12b8b1SLauro Ramos Venancio 
1192ed1e0ad8SJoe Perches 	pr_info("NFC Core ver %s\n", VERSION);
11933e256b8fSLauro Ramos Venancio 
11944d12b8b1SLauro Ramos Venancio 	rc = class_register(&nfc_class);
11954d12b8b1SLauro Ramos Venancio 	if (rc)
11964d12b8b1SLauro Ramos Venancio 		return rc;
11974d12b8b1SLauro Ramos Venancio 
11984d12b8b1SLauro Ramos Venancio 	rc = nfc_genl_init();
11994d12b8b1SLauro Ramos Venancio 	if (rc)
12004d12b8b1SLauro Ramos Venancio 		goto err_genl;
12014d12b8b1SLauro Ramos Venancio 
12024d12b8b1SLauro Ramos Venancio 	/* the first generation must not be 0 */
12034d12b8b1SLauro Ramos Venancio 	nfc_devlist_generation = 1;
12044d12b8b1SLauro Ramos Venancio 
120523b7869cSLauro Ramos Venancio 	rc = rawsock_init();
120623b7869cSLauro Ramos Venancio 	if (rc)
120723b7869cSLauro Ramos Venancio 		goto err_rawsock;
120823b7869cSLauro Ramos Venancio 
1209d646960fSSamuel Ortiz 	rc = nfc_llcp_init();
1210d646960fSSamuel Ortiz 	if (rc)
1211d646960fSSamuel Ortiz 		goto err_llcp_sock;
1212d646960fSSamuel Ortiz 
1213c7fe3b52SAloisio Almeida Jr 	rc = af_nfc_init();
1214c7fe3b52SAloisio Almeida Jr 	if (rc)
1215c7fe3b52SAloisio Almeida Jr 		goto err_af_nfc;
1216c7fe3b52SAloisio Almeida Jr 
12174d12b8b1SLauro Ramos Venancio 	return 0;
12184d12b8b1SLauro Ramos Venancio 
1219c7fe3b52SAloisio Almeida Jr err_af_nfc:
1220d646960fSSamuel Ortiz 	nfc_llcp_exit();
1221d646960fSSamuel Ortiz err_llcp_sock:
122223b7869cSLauro Ramos Venancio 	rawsock_exit();
122323b7869cSLauro Ramos Venancio err_rawsock:
1224c7fe3b52SAloisio Almeida Jr 	nfc_genl_exit();
12254d12b8b1SLauro Ramos Venancio err_genl:
12264d12b8b1SLauro Ramos Venancio 	class_unregister(&nfc_class);
12274d12b8b1SLauro Ramos Venancio 	return rc;
12283e256b8fSLauro Ramos Venancio }
12293e256b8fSLauro Ramos Venancio 
nfc_exit(void)12303e256b8fSLauro Ramos Venancio static void __exit nfc_exit(void)
12313e256b8fSLauro Ramos Venancio {
1232c7fe3b52SAloisio Almeida Jr 	af_nfc_exit();
1233d646960fSSamuel Ortiz 	nfc_llcp_exit();
123423b7869cSLauro Ramos Venancio 	rawsock_exit();
12354d12b8b1SLauro Ramos Venancio 	nfc_genl_exit();
12363e256b8fSLauro Ramos Venancio 	class_unregister(&nfc_class);
12373e256b8fSLauro Ramos Venancio }
12383e256b8fSLauro Ramos Venancio 
12393e256b8fSLauro Ramos Venancio subsys_initcall(nfc_init);
12403e256b8fSLauro Ramos Venancio module_exit(nfc_exit);
12413e256b8fSLauro Ramos Venancio 
12423e256b8fSLauro Ramos Venancio MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>");
12433e256b8fSLauro Ramos Venancio MODULE_DESCRIPTION("NFC Core ver " VERSION);
12443e256b8fSLauro Ramos Venancio MODULE_VERSION(VERSION);
12453e256b8fSLauro Ramos Venancio MODULE_LICENSE("GPL");
12461155bb61SSamuel Ortiz MODULE_ALIAS_NETPROTO(PF_NFC);
12475df16cadSSamuel Ortiz MODULE_ALIAS_GENL_FAMILY(NFC_GENL_NAME);
1248