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