xref: /openbmc/linux/net/nfc/netlink.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
24d12b8b1SLauro Ramos Venancio /*
34d12b8b1SLauro Ramos Venancio  * Copyright (C) 2011 Instituto Nokia de Tecnologia
44d12b8b1SLauro Ramos Venancio  *
54d12b8b1SLauro Ramos Venancio  * Authors:
64d12b8b1SLauro Ramos Venancio  *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
74d12b8b1SLauro Ramos Venancio  *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
84d12b8b1SLauro Ramos Venancio  *
99e58095fSSamuel Ortiz  * Vendor commands implementation based on net/wireless/nl80211.c
109e58095fSSamuel Ortiz  * which is:
119e58095fSSamuel Ortiz  *
129e58095fSSamuel Ortiz  * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net>
139e58095fSSamuel Ortiz  * Copyright 2013-2014  Intel Mobile Communications GmbH
144d12b8b1SLauro Ramos Venancio  */
154d12b8b1SLauro Ramos Venancio 
1652858b51SSamuel Ortiz #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
1720c239c1SJoe Perches 
184d12b8b1SLauro Ramos Venancio #include <net/genetlink.h>
194d12b8b1SLauro Ramos Venancio #include <linux/nfc.h>
204d12b8b1SLauro Ramos Venancio #include <linux/slab.h>
214d12b8b1SLauro Ramos Venancio 
224d12b8b1SLauro Ramos Venancio #include "nfc.h"
2330cc4587SSamuel Ortiz #include "llcp.h"
2452feb444SThierry Escande 
252a94fe48SJohannes Berg static const struct genl_multicast_group nfc_genl_mcgrps[] = {
262a94fe48SJohannes Berg 	{ .name = NFC_GENL_MCAST_EVENT_NAME, },
274d12b8b1SLauro Ramos Venancio };
284d12b8b1SLauro Ramos Venancio 
29489111e5SJohannes Berg static struct genl_family nfc_genl_family;
304d12b8b1SLauro Ramos Venancio static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
314d12b8b1SLauro Ramos Venancio 	[NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 },
324d12b8b1SLauro Ramos Venancio 	[NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING,
334d12b8b1SLauro Ramos Venancio 				.len = NFC_DEVICE_NAME_MAXSIZE },
344d12b8b1SLauro Ramos Venancio 	[NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 },
3588e706d5SJakub Kicinski 	[NFC_ATTR_TARGET_INDEX] = { .type = NLA_U32 },
361ed28f61SSamuel Ortiz 	[NFC_ATTR_COMM_MODE] = { .type = NLA_U8 },
371ed28f61SSamuel Ortiz 	[NFC_ATTR_RF_MODE] = { .type = NLA_U8 },
38c970a1acSSamuel Ortiz 	[NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 },
39fe7c5800SSamuel Ortiz 	[NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 },
40fe7c5800SSamuel Ortiz 	[NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 },
418af362d1SThierry Escande 	[NFC_ATTR_LLC_PARAM_LTO] = { .type = NLA_U8 },
428af362d1SThierry Escande 	[NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 },
438af362d1SThierry Escande 	[NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 },
44d9b8d8e1SThierry Escande 	[NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
459674da87SEric Lapuyade 	[NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
469674da87SEric Lapuyade 				     .len = NFC_FIRMWARE_NAME_MAXSIZE },
47361d23e4SJakub Kicinski 	[NFC_ATTR_SE_INDEX] = { .type = NLA_U32 },
485ce3f32bSSamuel Ortiz 	[NFC_ATTR_SE_APDU] = { .type = NLA_BINARY },
496ba3da44SJakub Kicinski 	[NFC_ATTR_VENDOR_ID] = { .type = NLA_U32 },
506ba3da44SJakub Kicinski 	[NFC_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
5129e76924SChristophe Ricard 	[NFC_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
5229e76924SChristophe Ricard 
53d9b8d8e1SThierry Escande };
54d9b8d8e1SThierry Escande 
55d9b8d8e1SThierry Escande static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
56fe9c8426SKees Cook 	[NFC_SDP_ATTR_URI] = { .type = NLA_STRING,
57fe9c8426SKees Cook 			       .len = U8_MAX - 4 },
58d9b8d8e1SThierry Escande 	[NFC_SDP_ATTR_SAP] = { .type = NLA_U8 },
594d12b8b1SLauro Ramos Venancio };
604d12b8b1SLauro Ramos Venancio 
nfc_genl_send_target(struct sk_buff * msg,struct nfc_target * target,struct netlink_callback * cb,int flags)614d12b8b1SLauro Ramos Venancio static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
624d12b8b1SLauro Ramos Venancio 				struct netlink_callback *cb, int flags)
634d12b8b1SLauro Ramos Venancio {
644d12b8b1SLauro Ramos Venancio 	void *hdr;
654d12b8b1SLauro Ramos Venancio 
6615e47304SEric W. Biederman 	hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
674d12b8b1SLauro Ramos Venancio 			  &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
684d12b8b1SLauro Ramos Venancio 	if (!hdr)
694d12b8b1SLauro Ramos Venancio 		return -EMSGSIZE;
704d12b8b1SLauro Ramos Venancio 
710a833c29SMichal Kubecek 	genl_dump_check_consistent(cb, hdr);
724d12b8b1SLauro Ramos Venancio 
731e6428d8SDavid S. Miller 	if (nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target->idx) ||
741e6428d8SDavid S. Miller 	    nla_put_u32(msg, NFC_ATTR_PROTOCOLS, target->supported_protocols) ||
751e6428d8SDavid S. Miller 	    nla_put_u16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res) ||
761e6428d8SDavid S. Miller 	    nla_put_u8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res))
771e6428d8SDavid S. Miller 		goto nla_put_failure;
781e6428d8SDavid S. Miller 	if (target->nfcid1_len > 0 &&
791e6428d8SDavid S. Miller 	    nla_put(msg, NFC_ATTR_TARGET_NFCID1, target->nfcid1_len,
801e6428d8SDavid S. Miller 		    target->nfcid1))
811e6428d8SDavid S. Miller 		goto nla_put_failure;
821e6428d8SDavid S. Miller 	if (target->sensb_res_len > 0 &&
831e6428d8SDavid S. Miller 	    nla_put(msg, NFC_ATTR_TARGET_SENSB_RES, target->sensb_res_len,
841e6428d8SDavid S. Miller 		    target->sensb_res))
851e6428d8SDavid S. Miller 		goto nla_put_failure;
861e6428d8SDavid S. Miller 	if (target->sensf_res_len > 0 &&
871e6428d8SDavid S. Miller 	    nla_put(msg, NFC_ATTR_TARGET_SENSF_RES, target->sensf_res_len,
881e6428d8SDavid S. Miller 		    target->sensf_res))
891e6428d8SDavid S. Miller 		goto nla_put_failure;
904d12b8b1SLauro Ramos Venancio 
91f5f6872eSMark A. Greer 	if (target->is_iso15693) {
92f5f6872eSMark A. Greer 		if (nla_put_u8(msg, NFC_ATTR_TARGET_ISO15693_DSFID,
93f5f6872eSMark A. Greer 			       target->iso15693_dsfid) ||
94f5f6872eSMark A. Greer 		    nla_put(msg, NFC_ATTR_TARGET_ISO15693_UID,
95f5f6872eSMark A. Greer 			    sizeof(target->iso15693_uid), target->iso15693_uid))
96f5f6872eSMark A. Greer 			goto nla_put_failure;
97f5f6872eSMark A. Greer 	}
98f5f6872eSMark A. Greer 
99053c095aSJohannes Berg 	genlmsg_end(msg, hdr);
100053c095aSJohannes Berg 	return 0;
1014d12b8b1SLauro Ramos Venancio 
1024d12b8b1SLauro Ramos Venancio nla_put_failure:
1034d12b8b1SLauro Ramos Venancio 	genlmsg_cancel(msg, hdr);
1044d12b8b1SLauro Ramos Venancio 	return -EMSGSIZE;
1054d12b8b1SLauro Ramos Venancio }
1064d12b8b1SLauro Ramos Venancio 
__get_device_from_cb(struct netlink_callback * cb)1074d12b8b1SLauro Ramos Venancio static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
1084d12b8b1SLauro Ramos Venancio {
1094495af31SJiri Pirko 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
1104d12b8b1SLauro Ramos Venancio 	struct nfc_dev *dev;
1114d12b8b1SLauro Ramos Venancio 	u32 idx;
1124d12b8b1SLauro Ramos Venancio 
113*7288dd2fSJakub Kicinski 	if (!info->info.attrs[NFC_ATTR_DEVICE_INDEX])
1144d12b8b1SLauro Ramos Venancio 		return ERR_PTR(-EINVAL);
1154d12b8b1SLauro Ramos Venancio 
116*7288dd2fSJakub Kicinski 	idx = nla_get_u32(info->info.attrs[NFC_ATTR_DEVICE_INDEX]);
1174d12b8b1SLauro Ramos Venancio 
1184d12b8b1SLauro Ramos Venancio 	dev = nfc_get_device(idx);
1194d12b8b1SLauro Ramos Venancio 	if (!dev)
1204d12b8b1SLauro Ramos Venancio 		return ERR_PTR(-ENODEV);
1214d12b8b1SLauro Ramos Venancio 
1224d12b8b1SLauro Ramos Venancio 	return dev;
1234d12b8b1SLauro Ramos Venancio }
1244d12b8b1SLauro Ramos Venancio 
nfc_genl_dump_targets(struct sk_buff * skb,struct netlink_callback * cb)1254d12b8b1SLauro Ramos Venancio static int nfc_genl_dump_targets(struct sk_buff *skb,
1264d12b8b1SLauro Ramos Venancio 				 struct netlink_callback *cb)
1274d12b8b1SLauro Ramos Venancio {
1284d12b8b1SLauro Ramos Venancio 	int i = cb->args[0];
1294d12b8b1SLauro Ramos Venancio 	struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
1304d12b8b1SLauro Ramos Venancio 	int rc;
1314d12b8b1SLauro Ramos Venancio 
1324d12b8b1SLauro Ramos Venancio 	if (!dev) {
1334d12b8b1SLauro Ramos Venancio 		dev = __get_device_from_cb(cb);
1344d12b8b1SLauro Ramos Venancio 		if (IS_ERR(dev))
1354d12b8b1SLauro Ramos Venancio 			return PTR_ERR(dev);
1364d12b8b1SLauro Ramos Venancio 
1374d12b8b1SLauro Ramos Venancio 		cb->args[1] = (long) dev;
1384d12b8b1SLauro Ramos Venancio 	}
1394d12b8b1SLauro Ramos Venancio 
140d4ccb132SEric Lapuyade 	device_lock(&dev->dev);
1414d12b8b1SLauro Ramos Venancio 
1424d12b8b1SLauro Ramos Venancio 	cb->seq = dev->targets_generation;
1434d12b8b1SLauro Ramos Venancio 
1444d12b8b1SLauro Ramos Venancio 	while (i < dev->n_targets) {
1454d12b8b1SLauro Ramos Venancio 		rc = nfc_genl_send_target(skb, &dev->targets[i], cb,
1464d12b8b1SLauro Ramos Venancio 					  NLM_F_MULTI);
1474d12b8b1SLauro Ramos Venancio 		if (rc < 0)
1484d12b8b1SLauro Ramos Venancio 			break;
1494d12b8b1SLauro Ramos Venancio 
1504d12b8b1SLauro Ramos Venancio 		i++;
1514d12b8b1SLauro Ramos Venancio 	}
1524d12b8b1SLauro Ramos Venancio 
153d4ccb132SEric Lapuyade 	device_unlock(&dev->dev);
1544d12b8b1SLauro Ramos Venancio 
1554d12b8b1SLauro Ramos Venancio 	cb->args[0] = i;
1564d12b8b1SLauro Ramos Venancio 
1574d12b8b1SLauro Ramos Venancio 	return skb->len;
1584d12b8b1SLauro Ramos Venancio }
1594d12b8b1SLauro Ramos Venancio 
nfc_genl_dump_targets_done(struct netlink_callback * cb)1604d12b8b1SLauro Ramos Venancio static int nfc_genl_dump_targets_done(struct netlink_callback *cb)
1614d12b8b1SLauro Ramos Venancio {
1624d12b8b1SLauro Ramos Venancio 	struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
1634d12b8b1SLauro Ramos Venancio 
1644d12b8b1SLauro Ramos Venancio 	if (dev)
1654d12b8b1SLauro Ramos Venancio 		nfc_put_device(dev);
1664d12b8b1SLauro Ramos Venancio 
1674d12b8b1SLauro Ramos Venancio 	return 0;
1684d12b8b1SLauro Ramos Venancio }
1694d12b8b1SLauro Ramos Venancio 
nfc_genl_targets_found(struct nfc_dev * dev)1704d12b8b1SLauro Ramos Venancio int nfc_genl_targets_found(struct nfc_dev *dev)
1714d12b8b1SLauro Ramos Venancio {
1724d12b8b1SLauro Ramos Venancio 	struct sk_buff *msg;
1734d12b8b1SLauro Ramos Venancio 	void *hdr;
1744d12b8b1SLauro Ramos Venancio 
17515e47304SEric W. Biederman 	dev->genl_data.poll_req_portid = 0;
1764d12b8b1SLauro Ramos Venancio 
17758050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
1784d12b8b1SLauro Ramos Venancio 	if (!msg)
1794d12b8b1SLauro Ramos Venancio 		return -ENOMEM;
1804d12b8b1SLauro Ramos Venancio 
1814d12b8b1SLauro Ramos Venancio 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
1824d12b8b1SLauro Ramos Venancio 			  NFC_EVENT_TARGETS_FOUND);
1834d12b8b1SLauro Ramos Venancio 	if (!hdr)
1844d12b8b1SLauro Ramos Venancio 		goto free_msg;
1854d12b8b1SLauro Ramos Venancio 
1861e6428d8SDavid S. Miller 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
1871e6428d8SDavid S. Miller 		goto nla_put_failure;
1884d12b8b1SLauro Ramos Venancio 
1894d12b8b1SLauro Ramos Venancio 	genlmsg_end(msg, hdr);
1904d12b8b1SLauro Ramos Venancio 
1912a94fe48SJohannes Berg 	return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
1924d12b8b1SLauro Ramos Venancio 
1934d12b8b1SLauro Ramos Venancio nla_put_failure:
1944d12b8b1SLauro Ramos Venancio free_msg:
1954d12b8b1SLauro Ramos Venancio 	nlmsg_free(msg);
1964d12b8b1SLauro Ramos Venancio 	return -EMSGSIZE;
1974d12b8b1SLauro Ramos Venancio }
1984d12b8b1SLauro Ramos Venancio 
nfc_genl_target_lost(struct nfc_dev * dev,u32 target_idx)1998112a5c9SSamuel Ortiz int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx)
2008112a5c9SSamuel Ortiz {
2018112a5c9SSamuel Ortiz 	struct sk_buff *msg;
2028112a5c9SSamuel Ortiz 	void *hdr;
2038112a5c9SSamuel Ortiz 
20458050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2058112a5c9SSamuel Ortiz 	if (!msg)
2068112a5c9SSamuel Ortiz 		return -ENOMEM;
2078112a5c9SSamuel Ortiz 
2088112a5c9SSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
2098112a5c9SSamuel Ortiz 			  NFC_EVENT_TARGET_LOST);
2108112a5c9SSamuel Ortiz 	if (!hdr)
2118112a5c9SSamuel Ortiz 		goto free_msg;
2128112a5c9SSamuel Ortiz 
21359ef43e6SJohn W. Linville 	if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
21459ef43e6SJohn W. Linville 	    nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target_idx))
21559ef43e6SJohn W. Linville 		goto nla_put_failure;
2168112a5c9SSamuel Ortiz 
2178112a5c9SSamuel Ortiz 	genlmsg_end(msg, hdr);
2188112a5c9SSamuel Ortiz 
2192a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
2208112a5c9SSamuel Ortiz 
2218112a5c9SSamuel Ortiz 	return 0;
2228112a5c9SSamuel Ortiz 
2238112a5c9SSamuel Ortiz nla_put_failure:
2248112a5c9SSamuel Ortiz free_msg:
2258112a5c9SSamuel Ortiz 	nlmsg_free(msg);
2268112a5c9SSamuel Ortiz 	return -EMSGSIZE;
2278112a5c9SSamuel Ortiz }
2288112a5c9SSamuel Ortiz 
nfc_genl_tm_activated(struct nfc_dev * dev,u32 protocol)229fc40a8c1SSamuel Ortiz int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol)
230fc40a8c1SSamuel Ortiz {
231fc40a8c1SSamuel Ortiz 	struct sk_buff *msg;
232fc40a8c1SSamuel Ortiz 	void *hdr;
233fc40a8c1SSamuel Ortiz 
23458050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
235fc40a8c1SSamuel Ortiz 	if (!msg)
236fc40a8c1SSamuel Ortiz 		return -ENOMEM;
237fc40a8c1SSamuel Ortiz 
238fc40a8c1SSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
239fc40a8c1SSamuel Ortiz 			  NFC_EVENT_TM_ACTIVATED);
240fc40a8c1SSamuel Ortiz 	if (!hdr)
241fc40a8c1SSamuel Ortiz 		goto free_msg;
242fc40a8c1SSamuel Ortiz 
243fc40a8c1SSamuel Ortiz 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
244fc40a8c1SSamuel Ortiz 		goto nla_put_failure;
245fc40a8c1SSamuel Ortiz 	if (nla_put_u32(msg, NFC_ATTR_TM_PROTOCOLS, protocol))
246fc40a8c1SSamuel Ortiz 		goto nla_put_failure;
247fc40a8c1SSamuel Ortiz 
248fc40a8c1SSamuel Ortiz 	genlmsg_end(msg, hdr);
249fc40a8c1SSamuel Ortiz 
2502a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
251fc40a8c1SSamuel Ortiz 
252fc40a8c1SSamuel Ortiz 	return 0;
253fc40a8c1SSamuel Ortiz 
254fc40a8c1SSamuel Ortiz nla_put_failure:
255fc40a8c1SSamuel Ortiz free_msg:
256fc40a8c1SSamuel Ortiz 	nlmsg_free(msg);
257fc40a8c1SSamuel Ortiz 	return -EMSGSIZE;
258fc40a8c1SSamuel Ortiz }
259fc40a8c1SSamuel Ortiz 
nfc_genl_tm_deactivated(struct nfc_dev * dev)260fc40a8c1SSamuel Ortiz int nfc_genl_tm_deactivated(struct nfc_dev *dev)
261fc40a8c1SSamuel Ortiz {
262fc40a8c1SSamuel Ortiz 	struct sk_buff *msg;
263fc40a8c1SSamuel Ortiz 	void *hdr;
264fc40a8c1SSamuel Ortiz 
26558050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
266fc40a8c1SSamuel Ortiz 	if (!msg)
267fc40a8c1SSamuel Ortiz 		return -ENOMEM;
268fc40a8c1SSamuel Ortiz 
269fc40a8c1SSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
270fc40a8c1SSamuel Ortiz 			  NFC_EVENT_TM_DEACTIVATED);
271fc40a8c1SSamuel Ortiz 	if (!hdr)
272fc40a8c1SSamuel Ortiz 		goto free_msg;
273fc40a8c1SSamuel Ortiz 
274fc40a8c1SSamuel Ortiz 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
275fc40a8c1SSamuel Ortiz 		goto nla_put_failure;
276fc40a8c1SSamuel Ortiz 
277fc40a8c1SSamuel Ortiz 	genlmsg_end(msg, hdr);
278fc40a8c1SSamuel Ortiz 
2792a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
280fc40a8c1SSamuel Ortiz 
281fc40a8c1SSamuel Ortiz 	return 0;
282fc40a8c1SSamuel Ortiz 
283fc40a8c1SSamuel Ortiz nla_put_failure:
284fc40a8c1SSamuel Ortiz free_msg:
285fc40a8c1SSamuel Ortiz 	nlmsg_free(msg);
286fc40a8c1SSamuel Ortiz 	return -EMSGSIZE;
287fc40a8c1SSamuel Ortiz }
288fc40a8c1SSamuel Ortiz 
nfc_genl_setup_device_added(struct nfc_dev * dev,struct sk_buff * msg)28985a2566dSOGAWA Hirofumi static int nfc_genl_setup_device_added(struct nfc_dev *dev, struct sk_buff *msg)
29085a2566dSOGAWA Hirofumi {
29185a2566dSOGAWA Hirofumi 	if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
29285a2566dSOGAWA Hirofumi 	    nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
29385a2566dSOGAWA Hirofumi 	    nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
29485a2566dSOGAWA Hirofumi 	    nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
29585a2566dSOGAWA Hirofumi 	    nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
29685a2566dSOGAWA Hirofumi 		return -1;
29785a2566dSOGAWA Hirofumi 	return 0;
29885a2566dSOGAWA Hirofumi }
29985a2566dSOGAWA Hirofumi 
nfc_genl_device_added(struct nfc_dev * dev)3004d12b8b1SLauro Ramos Venancio int nfc_genl_device_added(struct nfc_dev *dev)
3014d12b8b1SLauro Ramos Venancio {
3024d12b8b1SLauro Ramos Venancio 	struct sk_buff *msg;
3034d12b8b1SLauro Ramos Venancio 	void *hdr;
3044d12b8b1SLauro Ramos Venancio 
30558050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
3064d12b8b1SLauro Ramos Venancio 	if (!msg)
3074d12b8b1SLauro Ramos Venancio 		return -ENOMEM;
3084d12b8b1SLauro Ramos Venancio 
3094d12b8b1SLauro Ramos Venancio 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
3104d12b8b1SLauro Ramos Venancio 			  NFC_EVENT_DEVICE_ADDED);
3114d12b8b1SLauro Ramos Venancio 	if (!hdr)
3124d12b8b1SLauro Ramos Venancio 		goto free_msg;
3134d12b8b1SLauro Ramos Venancio 
31485a2566dSOGAWA Hirofumi 	if (nfc_genl_setup_device_added(dev, msg))
3151e6428d8SDavid S. Miller 		goto nla_put_failure;
3164d12b8b1SLauro Ramos Venancio 
3174d12b8b1SLauro Ramos Venancio 	genlmsg_end(msg, hdr);
3184d12b8b1SLauro Ramos Venancio 
3192a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
3204d12b8b1SLauro Ramos Venancio 
3214d12b8b1SLauro Ramos Venancio 	return 0;
3224d12b8b1SLauro Ramos Venancio 
3234d12b8b1SLauro Ramos Venancio nla_put_failure:
3244d12b8b1SLauro Ramos Venancio free_msg:
3254d12b8b1SLauro Ramos Venancio 	nlmsg_free(msg);
3264d12b8b1SLauro Ramos Venancio 	return -EMSGSIZE;
3274d12b8b1SLauro Ramos Venancio }
3284d12b8b1SLauro Ramos Venancio 
nfc_genl_device_removed(struct nfc_dev * dev)3294d12b8b1SLauro Ramos Venancio int nfc_genl_device_removed(struct nfc_dev *dev)
3304d12b8b1SLauro Ramos Venancio {
3314d12b8b1SLauro Ramos Venancio 	struct sk_buff *msg;
3324d12b8b1SLauro Ramos Venancio 	void *hdr;
3334d12b8b1SLauro Ramos Venancio 
33458050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
3354d12b8b1SLauro Ramos Venancio 	if (!msg)
3364d12b8b1SLauro Ramos Venancio 		return -ENOMEM;
3374d12b8b1SLauro Ramos Venancio 
3384d12b8b1SLauro Ramos Venancio 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
3394d12b8b1SLauro Ramos Venancio 			  NFC_EVENT_DEVICE_REMOVED);
3404d12b8b1SLauro Ramos Venancio 	if (!hdr)
3414d12b8b1SLauro Ramos Venancio 		goto free_msg;
3424d12b8b1SLauro Ramos Venancio 
3431e6428d8SDavid S. Miller 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
3441e6428d8SDavid S. Miller 		goto nla_put_failure;
3454d12b8b1SLauro Ramos Venancio 
3464d12b8b1SLauro Ramos Venancio 	genlmsg_end(msg, hdr);
3474d12b8b1SLauro Ramos Venancio 
3482a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
3494d12b8b1SLauro Ramos Venancio 
3504d12b8b1SLauro Ramos Venancio 	return 0;
3514d12b8b1SLauro Ramos Venancio 
3524d12b8b1SLauro Ramos Venancio nla_put_failure:
3534d12b8b1SLauro Ramos Venancio free_msg:
3544d12b8b1SLauro Ramos Venancio 	nlmsg_free(msg);
3554d12b8b1SLauro Ramos Venancio 	return -EMSGSIZE;
3564d12b8b1SLauro Ramos Venancio }
3574d12b8b1SLauro Ramos Venancio 
nfc_genl_llc_send_sdres(struct nfc_dev * dev,struct hlist_head * sdres_list)358d9b8d8e1SThierry Escande int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list)
359d9b8d8e1SThierry Escande {
360d9b8d8e1SThierry Escande 	struct sk_buff *msg;
361d9b8d8e1SThierry Escande 	struct nlattr *sdp_attr, *uri_attr;
362d9b8d8e1SThierry Escande 	struct nfc_llcp_sdp_tlv *sdres;
363d9b8d8e1SThierry Escande 	struct hlist_node *n;
364d9b8d8e1SThierry Escande 	void *hdr;
365d9b8d8e1SThierry Escande 	int rc = -EMSGSIZE;
366d9b8d8e1SThierry Escande 	int i;
367d9b8d8e1SThierry Escande 
368d9b8d8e1SThierry Escande 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
369d9b8d8e1SThierry Escande 	if (!msg)
370d9b8d8e1SThierry Escande 		return -ENOMEM;
371d9b8d8e1SThierry Escande 
372d9b8d8e1SThierry Escande 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
373d9b8d8e1SThierry Escande 			  NFC_EVENT_LLC_SDRES);
374d9b8d8e1SThierry Escande 	if (!hdr)
375d9b8d8e1SThierry Escande 		goto free_msg;
376d9b8d8e1SThierry Escande 
377d9b8d8e1SThierry Escande 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
378d9b8d8e1SThierry Escande 		goto nla_put_failure;
379d9b8d8e1SThierry Escande 
380ae0be8deSMichal Kubecek 	sdp_attr = nla_nest_start_noflag(msg, NFC_ATTR_LLC_SDP);
381d9b8d8e1SThierry Escande 	if (sdp_attr == NULL) {
382d9b8d8e1SThierry Escande 		rc = -ENOMEM;
383d9b8d8e1SThierry Escande 		goto nla_put_failure;
384d9b8d8e1SThierry Escande 	}
385d9b8d8e1SThierry Escande 
386d9b8d8e1SThierry Escande 	i = 1;
387d9b8d8e1SThierry Escande 	hlist_for_each_entry_safe(sdres, n, sdres_list, node) {
388d9b8d8e1SThierry Escande 		pr_debug("uri: %s, sap: %d\n", sdres->uri, sdres->sap);
389d9b8d8e1SThierry Escande 
390ae0be8deSMichal Kubecek 		uri_attr = nla_nest_start_noflag(msg, i++);
391d9b8d8e1SThierry Escande 		if (uri_attr == NULL) {
392d9b8d8e1SThierry Escande 			rc = -ENOMEM;
393d9b8d8e1SThierry Escande 			goto nla_put_failure;
394d9b8d8e1SThierry Escande 		}
395d9b8d8e1SThierry Escande 
396d9b8d8e1SThierry Escande 		if (nla_put_u8(msg, NFC_SDP_ATTR_SAP, sdres->sap))
397d9b8d8e1SThierry Escande 			goto nla_put_failure;
398d9b8d8e1SThierry Escande 
399d9b8d8e1SThierry Escande 		if (nla_put_string(msg, NFC_SDP_ATTR_URI, sdres->uri))
400d9b8d8e1SThierry Escande 			goto nla_put_failure;
401d9b8d8e1SThierry Escande 
402d9b8d8e1SThierry Escande 		nla_nest_end(msg, uri_attr);
403d9b8d8e1SThierry Escande 
404d9b8d8e1SThierry Escande 		hlist_del(&sdres->node);
405d9b8d8e1SThierry Escande 
406d9b8d8e1SThierry Escande 		nfc_llcp_free_sdp_tlv(sdres);
407d9b8d8e1SThierry Escande 	}
408d9b8d8e1SThierry Escande 
409d9b8d8e1SThierry Escande 	nla_nest_end(msg, sdp_attr);
410d9b8d8e1SThierry Escande 
411d9b8d8e1SThierry Escande 	genlmsg_end(msg, hdr);
412d9b8d8e1SThierry Escande 
4132a94fe48SJohannes Berg 	return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
414d9b8d8e1SThierry Escande 
415d9b8d8e1SThierry Escande nla_put_failure:
416d9b8d8e1SThierry Escande free_msg:
417d9b8d8e1SThierry Escande 	nlmsg_free(msg);
418d9b8d8e1SThierry Escande 
419d9b8d8e1SThierry Escande 	nfc_llcp_free_sdp_tlv_list(sdres_list);
420d9b8d8e1SThierry Escande 
421d9b8d8e1SThierry Escande 	return rc;
422d9b8d8e1SThierry Escande }
423d9b8d8e1SThierry Escande 
nfc_genl_se_added(struct nfc_dev * dev,u32 se_idx,u16 type)4242757c372SSamuel Ortiz int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type)
4252757c372SSamuel Ortiz {
4262757c372SSamuel Ortiz 	struct sk_buff *msg;
4272757c372SSamuel Ortiz 	void *hdr;
4282757c372SSamuel Ortiz 
4292757c372SSamuel Ortiz 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4302757c372SSamuel Ortiz 	if (!msg)
4312757c372SSamuel Ortiz 		return -ENOMEM;
4322757c372SSamuel Ortiz 
4332757c372SSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
4342757c372SSamuel Ortiz 			  NFC_EVENT_SE_ADDED);
4352757c372SSamuel Ortiz 	if (!hdr)
4362757c372SSamuel Ortiz 		goto free_msg;
4372757c372SSamuel Ortiz 
4382757c372SSamuel Ortiz 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
4392757c372SSamuel Ortiz 	    nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
4402757c372SSamuel Ortiz 	    nla_put_u8(msg, NFC_ATTR_SE_TYPE, type))
4412757c372SSamuel Ortiz 		goto nla_put_failure;
4422757c372SSamuel Ortiz 
4432757c372SSamuel Ortiz 	genlmsg_end(msg, hdr);
4442757c372SSamuel Ortiz 
4452a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
4462757c372SSamuel Ortiz 
4472757c372SSamuel Ortiz 	return 0;
4482757c372SSamuel Ortiz 
4492757c372SSamuel Ortiz nla_put_failure:
4502757c372SSamuel Ortiz free_msg:
4512757c372SSamuel Ortiz 	nlmsg_free(msg);
4522757c372SSamuel Ortiz 	return -EMSGSIZE;
4532757c372SSamuel Ortiz }
4542757c372SSamuel Ortiz 
nfc_genl_se_removed(struct nfc_dev * dev,u32 se_idx)4552757c372SSamuel Ortiz int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx)
4562757c372SSamuel Ortiz {
4572757c372SSamuel Ortiz 	struct sk_buff *msg;
4582757c372SSamuel Ortiz 	void *hdr;
4592757c372SSamuel Ortiz 
4602757c372SSamuel Ortiz 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4612757c372SSamuel Ortiz 	if (!msg)
4622757c372SSamuel Ortiz 		return -ENOMEM;
4632757c372SSamuel Ortiz 
4642757c372SSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
4652757c372SSamuel Ortiz 			  NFC_EVENT_SE_REMOVED);
4662757c372SSamuel Ortiz 	if (!hdr)
4672757c372SSamuel Ortiz 		goto free_msg;
4682757c372SSamuel Ortiz 
4692757c372SSamuel Ortiz 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
4702757c372SSamuel Ortiz 	    nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx))
4712757c372SSamuel Ortiz 		goto nla_put_failure;
4722757c372SSamuel Ortiz 
4732757c372SSamuel Ortiz 	genlmsg_end(msg, hdr);
4742757c372SSamuel Ortiz 
4752a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
4762757c372SSamuel Ortiz 
4772757c372SSamuel Ortiz 	return 0;
4782757c372SSamuel Ortiz 
4792757c372SSamuel Ortiz nla_put_failure:
4802757c372SSamuel Ortiz free_msg:
4812757c372SSamuel Ortiz 	nlmsg_free(msg);
4822757c372SSamuel Ortiz 	return -EMSGSIZE;
4832757c372SSamuel Ortiz }
4842757c372SSamuel Ortiz 
nfc_genl_se_transaction(struct nfc_dev * dev,u8 se_idx,struct nfc_evt_transaction * evt_transaction)485447b27c4SChristophe Ricard int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx,
486447b27c4SChristophe Ricard 			    struct nfc_evt_transaction *evt_transaction)
487447b27c4SChristophe Ricard {
488447b27c4SChristophe Ricard 	struct nfc_se *se;
489447b27c4SChristophe Ricard 	struct sk_buff *msg;
490447b27c4SChristophe Ricard 	void *hdr;
491447b27c4SChristophe Ricard 
492447b27c4SChristophe Ricard 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
493447b27c4SChristophe Ricard 	if (!msg)
494447b27c4SChristophe Ricard 		return -ENOMEM;
495447b27c4SChristophe Ricard 
496447b27c4SChristophe Ricard 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
497447b27c4SChristophe Ricard 			  NFC_EVENT_SE_TRANSACTION);
498447b27c4SChristophe Ricard 	if (!hdr)
499447b27c4SChristophe Ricard 		goto free_msg;
500447b27c4SChristophe Ricard 
501447b27c4SChristophe Ricard 	se = nfc_find_se(dev, se_idx);
502447b27c4SChristophe Ricard 	if (!se)
503447b27c4SChristophe Ricard 		goto free_msg;
504447b27c4SChristophe Ricard 
505447b27c4SChristophe Ricard 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
506447b27c4SChristophe Ricard 	    nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
507447b27c4SChristophe Ricard 	    nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type) ||
508447b27c4SChristophe Ricard 	    nla_put(msg, NFC_ATTR_SE_AID, evt_transaction->aid_len,
509447b27c4SChristophe Ricard 		    evt_transaction->aid) ||
510447b27c4SChristophe Ricard 	    nla_put(msg, NFC_ATTR_SE_PARAMS, evt_transaction->params_len,
511447b27c4SChristophe Ricard 		    evt_transaction->params))
512447b27c4SChristophe Ricard 		goto nla_put_failure;
513447b27c4SChristophe Ricard 
514447b27c4SChristophe Ricard 	/* evt_transaction is no more used */
515447b27c4SChristophe Ricard 	devm_kfree(&dev->dev, evt_transaction);
516447b27c4SChristophe Ricard 
517447b27c4SChristophe Ricard 	genlmsg_end(msg, hdr);
518447b27c4SChristophe Ricard 
519447b27c4SChristophe Ricard 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
520447b27c4SChristophe Ricard 
521447b27c4SChristophe Ricard 	return 0;
522447b27c4SChristophe Ricard 
523447b27c4SChristophe Ricard nla_put_failure:
524447b27c4SChristophe Ricard free_msg:
525447b27c4SChristophe Ricard 	/* evt_transaction is no more used */
526447b27c4SChristophe Ricard 	devm_kfree(&dev->dev, evt_transaction);
527447b27c4SChristophe Ricard 	nlmsg_free(msg);
528447b27c4SChristophe Ricard 	return -EMSGSIZE;
529447b27c4SChristophe Ricard }
530447b27c4SChristophe Ricard 
nfc_genl_se_connectivity(struct nfc_dev * dev,u8 se_idx)5319afec6d3SChristophe Ricard int nfc_genl_se_connectivity(struct nfc_dev *dev, u8 se_idx)
5329afec6d3SChristophe Ricard {
533f2479c0aSKrzysztof Kozlowski 	const struct nfc_se *se;
5349afec6d3SChristophe Ricard 	struct sk_buff *msg;
5359afec6d3SChristophe Ricard 	void *hdr;
5369afec6d3SChristophe Ricard 
5379afec6d3SChristophe Ricard 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5389afec6d3SChristophe Ricard 	if (!msg)
5399afec6d3SChristophe Ricard 		return -ENOMEM;
5409afec6d3SChristophe Ricard 
5419afec6d3SChristophe Ricard 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
5429afec6d3SChristophe Ricard 			  NFC_EVENT_SE_CONNECTIVITY);
5439afec6d3SChristophe Ricard 	if (!hdr)
5449afec6d3SChristophe Ricard 		goto free_msg;
5459afec6d3SChristophe Ricard 
5469afec6d3SChristophe Ricard 	se = nfc_find_se(dev, se_idx);
5479afec6d3SChristophe Ricard 	if (!se)
5489afec6d3SChristophe Ricard 		goto free_msg;
5499afec6d3SChristophe Ricard 
5509afec6d3SChristophe Ricard 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
5519afec6d3SChristophe Ricard 	    nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
5529afec6d3SChristophe Ricard 	    nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type))
5539afec6d3SChristophe Ricard 		goto nla_put_failure;
5549afec6d3SChristophe Ricard 
5559afec6d3SChristophe Ricard 	genlmsg_end(msg, hdr);
5569afec6d3SChristophe Ricard 
5579afec6d3SChristophe Ricard 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
5589afec6d3SChristophe Ricard 
5599afec6d3SChristophe Ricard 	return 0;
5609afec6d3SChristophe Ricard 
5619afec6d3SChristophe Ricard nla_put_failure:
5629afec6d3SChristophe Ricard free_msg:
5639afec6d3SChristophe Ricard 	nlmsg_free(msg);
5649afec6d3SChristophe Ricard 	return -EMSGSIZE;
5659afec6d3SChristophe Ricard }
5669afec6d3SChristophe Ricard 
nfc_genl_send_device(struct sk_buff * msg,struct nfc_dev * dev,u32 portid,u32 seq,struct netlink_callback * cb,int flags)5674d12b8b1SLauro Ramos Venancio static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
56815e47304SEric W. Biederman 				u32 portid, u32 seq,
5694d12b8b1SLauro Ramos Venancio 				struct netlink_callback *cb,
5704d12b8b1SLauro Ramos Venancio 				int flags)
5714d12b8b1SLauro Ramos Venancio {
5724d12b8b1SLauro Ramos Venancio 	void *hdr;
5734d12b8b1SLauro Ramos Venancio 
57415e47304SEric W. Biederman 	hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags,
5754d12b8b1SLauro Ramos Venancio 			  NFC_CMD_GET_DEVICE);
5764d12b8b1SLauro Ramos Venancio 	if (!hdr)
5774d12b8b1SLauro Ramos Venancio 		return -EMSGSIZE;
5784d12b8b1SLauro Ramos Venancio 
5794d12b8b1SLauro Ramos Venancio 	if (cb)
5800a833c29SMichal Kubecek 		genl_dump_check_consistent(cb, hdr);
5814d12b8b1SLauro Ramos Venancio 
58285a2566dSOGAWA Hirofumi 	if (nfc_genl_setup_device_added(dev, msg))
5831e6428d8SDavid S. Miller 		goto nla_put_failure;
5844d12b8b1SLauro Ramos Venancio 
585053c095aSJohannes Berg 	genlmsg_end(msg, hdr);
586053c095aSJohannes Berg 	return 0;
5874d12b8b1SLauro Ramos Venancio 
5884d12b8b1SLauro Ramos Venancio nla_put_failure:
5894d12b8b1SLauro Ramos Venancio 	genlmsg_cancel(msg, hdr);
5904d12b8b1SLauro Ramos Venancio 	return -EMSGSIZE;
5914d12b8b1SLauro Ramos Venancio }
5924d12b8b1SLauro Ramos Venancio 
nfc_genl_dump_devices(struct sk_buff * skb,struct netlink_callback * cb)5934d12b8b1SLauro Ramos Venancio static int nfc_genl_dump_devices(struct sk_buff *skb,
5944d12b8b1SLauro Ramos Venancio 				 struct netlink_callback *cb)
5954d12b8b1SLauro Ramos Venancio {
5964d12b8b1SLauro Ramos Venancio 	struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
5974d12b8b1SLauro Ramos Venancio 	struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
5984d12b8b1SLauro Ramos Venancio 	bool first_call = false;
5994d12b8b1SLauro Ramos Venancio 
6004d12b8b1SLauro Ramos Venancio 	if (!iter) {
6014d12b8b1SLauro Ramos Venancio 		first_call = true;
6024d12b8b1SLauro Ramos Venancio 		iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
6034d12b8b1SLauro Ramos Venancio 		if (!iter)
6044d12b8b1SLauro Ramos Venancio 			return -ENOMEM;
6054d12b8b1SLauro Ramos Venancio 		cb->args[0] = (long) iter;
6064d12b8b1SLauro Ramos Venancio 	}
6074d12b8b1SLauro Ramos Venancio 
6084d12b8b1SLauro Ramos Venancio 	mutex_lock(&nfc_devlist_mutex);
6094d12b8b1SLauro Ramos Venancio 
6104d12b8b1SLauro Ramos Venancio 	cb->seq = nfc_devlist_generation;
6114d12b8b1SLauro Ramos Venancio 
6124d12b8b1SLauro Ramos Venancio 	if (first_call) {
6134d12b8b1SLauro Ramos Venancio 		nfc_device_iter_init(iter);
6144d12b8b1SLauro Ramos Venancio 		dev = nfc_device_iter_next(iter);
6154d12b8b1SLauro Ramos Venancio 	}
6164d12b8b1SLauro Ramos Venancio 
6174d12b8b1SLauro Ramos Venancio 	while (dev) {
6184d12b8b1SLauro Ramos Venancio 		int rc;
6194d12b8b1SLauro Ramos Venancio 
62015e47304SEric W. Biederman 		rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).portid,
6210a40acb2SSamuel Ortiz 					  cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
6224d12b8b1SLauro Ramos Venancio 		if (rc < 0)
6234d12b8b1SLauro Ramos Venancio 			break;
6244d12b8b1SLauro Ramos Venancio 
6254d12b8b1SLauro Ramos Venancio 		dev = nfc_device_iter_next(iter);
6264d12b8b1SLauro Ramos Venancio 	}
6274d12b8b1SLauro Ramos Venancio 
6284d12b8b1SLauro Ramos Venancio 	mutex_unlock(&nfc_devlist_mutex);
6294d12b8b1SLauro Ramos Venancio 
6304d12b8b1SLauro Ramos Venancio 	cb->args[1] = (long) dev;
6314d12b8b1SLauro Ramos Venancio 
6324d12b8b1SLauro Ramos Venancio 	return skb->len;
6334d12b8b1SLauro Ramos Venancio }
6344d12b8b1SLauro Ramos Venancio 
nfc_genl_dump_devices_done(struct netlink_callback * cb)6354d12b8b1SLauro Ramos Venancio static int nfc_genl_dump_devices_done(struct netlink_callback *cb)
6364d12b8b1SLauro Ramos Venancio {
6374d12b8b1SLauro Ramos Venancio 	struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
6384d12b8b1SLauro Ramos Venancio 
639fd79a0cbSTadeusz Struk 	if (iter) {
6404d12b8b1SLauro Ramos Venancio 		nfc_device_iter_exit(iter);
6414d12b8b1SLauro Ramos Venancio 		kfree(iter);
642fd79a0cbSTadeusz Struk 	}
6434d12b8b1SLauro Ramos Venancio 
6444d12b8b1SLauro Ramos Venancio 	return 0;
6454d12b8b1SLauro Ramos Venancio }
6464d12b8b1SLauro Ramos Venancio 
nfc_genl_dep_link_up_event(struct nfc_dev * dev,u32 target_idx,u8 comm_mode,u8 rf_mode)6471ed28f61SSamuel Ortiz int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx,
6481ed28f61SSamuel Ortiz 			       u8 comm_mode, u8 rf_mode)
6491ed28f61SSamuel Ortiz {
6501ed28f61SSamuel Ortiz 	struct sk_buff *msg;
6511ed28f61SSamuel Ortiz 	void *hdr;
6521ed28f61SSamuel Ortiz 
6531ed28f61SSamuel Ortiz 	pr_debug("DEP link is up\n");
6541ed28f61SSamuel Ortiz 
65558050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
6561ed28f61SSamuel Ortiz 	if (!msg)
6571ed28f61SSamuel Ortiz 		return -ENOMEM;
6581ed28f61SSamuel Ortiz 
6590a40acb2SSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, NFC_CMD_DEP_LINK_UP);
6601ed28f61SSamuel Ortiz 	if (!hdr)
6611ed28f61SSamuel Ortiz 		goto free_msg;
6621ed28f61SSamuel Ortiz 
6631e6428d8SDavid S. Miller 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
6641e6428d8SDavid S. Miller 		goto nla_put_failure;
6651e6428d8SDavid S. Miller 	if (rf_mode == NFC_RF_INITIATOR &&
6661e6428d8SDavid S. Miller 	    nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target_idx))
6671e6428d8SDavid S. Miller 		goto nla_put_failure;
6681e6428d8SDavid S. Miller 	if (nla_put_u8(msg, NFC_ATTR_COMM_MODE, comm_mode) ||
6691e6428d8SDavid S. Miller 	    nla_put_u8(msg, NFC_ATTR_RF_MODE, rf_mode))
6701e6428d8SDavid S. Miller 		goto nla_put_failure;
6711ed28f61SSamuel Ortiz 
6721ed28f61SSamuel Ortiz 	genlmsg_end(msg, hdr);
6731ed28f61SSamuel Ortiz 
6741ed28f61SSamuel Ortiz 	dev->dep_link_up = true;
6751ed28f61SSamuel Ortiz 
6762a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
6771ed28f61SSamuel Ortiz 
6781ed28f61SSamuel Ortiz 	return 0;
6791ed28f61SSamuel Ortiz 
6801ed28f61SSamuel Ortiz nla_put_failure:
6811ed28f61SSamuel Ortiz free_msg:
6821ed28f61SSamuel Ortiz 	nlmsg_free(msg);
6831ed28f61SSamuel Ortiz 	return -EMSGSIZE;
6841ed28f61SSamuel Ortiz }
6851ed28f61SSamuel Ortiz 
nfc_genl_dep_link_down_event(struct nfc_dev * dev)6861ed28f61SSamuel Ortiz int nfc_genl_dep_link_down_event(struct nfc_dev *dev)
6871ed28f61SSamuel Ortiz {
6881ed28f61SSamuel Ortiz 	struct sk_buff *msg;
6891ed28f61SSamuel Ortiz 	void *hdr;
6901ed28f61SSamuel Ortiz 
6911ed28f61SSamuel Ortiz 	pr_debug("DEP link is down\n");
6921ed28f61SSamuel Ortiz 
69358050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
6941ed28f61SSamuel Ortiz 	if (!msg)
6951ed28f61SSamuel Ortiz 		return -ENOMEM;
6961ed28f61SSamuel Ortiz 
6971ed28f61SSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
6981ed28f61SSamuel Ortiz 			  NFC_CMD_DEP_LINK_DOWN);
6991ed28f61SSamuel Ortiz 	if (!hdr)
7001ed28f61SSamuel Ortiz 		goto free_msg;
7011ed28f61SSamuel Ortiz 
7021e6428d8SDavid S. Miller 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
7031e6428d8SDavid S. Miller 		goto nla_put_failure;
7041ed28f61SSamuel Ortiz 
7051ed28f61SSamuel Ortiz 	genlmsg_end(msg, hdr);
7061ed28f61SSamuel Ortiz 
7072a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
7081ed28f61SSamuel Ortiz 
7091ed28f61SSamuel Ortiz 	return 0;
7101ed28f61SSamuel Ortiz 
7111ed28f61SSamuel Ortiz nla_put_failure:
7121ed28f61SSamuel Ortiz free_msg:
7131ed28f61SSamuel Ortiz 	nlmsg_free(msg);
7141ed28f61SSamuel Ortiz 	return -EMSGSIZE;
7151ed28f61SSamuel Ortiz }
7161ed28f61SSamuel Ortiz 
nfc_genl_get_device(struct sk_buff * skb,struct genl_info * info)7174d12b8b1SLauro Ramos Venancio static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info)
7184d12b8b1SLauro Ramos Venancio {
7194d12b8b1SLauro Ramos Venancio 	struct sk_buff *msg;
7204d12b8b1SLauro Ramos Venancio 	struct nfc_dev *dev;
7214d12b8b1SLauro Ramos Venancio 	u32 idx;
7224d12b8b1SLauro Ramos Venancio 	int rc = -ENOBUFS;
7234d12b8b1SLauro Ramos Venancio 
7244d12b8b1SLauro Ramos Venancio 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
7254d12b8b1SLauro Ramos Venancio 		return -EINVAL;
7264d12b8b1SLauro Ramos Venancio 
7274d12b8b1SLauro Ramos Venancio 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
7284d12b8b1SLauro Ramos Venancio 
7294d12b8b1SLauro Ramos Venancio 	dev = nfc_get_device(idx);
7304d12b8b1SLauro Ramos Venancio 	if (!dev)
7314d12b8b1SLauro Ramos Venancio 		return -ENODEV;
7324d12b8b1SLauro Ramos Venancio 
73358050fceSThomas Graf 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7344d12b8b1SLauro Ramos Venancio 	if (!msg) {
7354d12b8b1SLauro Ramos Venancio 		rc = -ENOMEM;
7364d12b8b1SLauro Ramos Venancio 		goto out_putdev;
7374d12b8b1SLauro Ramos Venancio 	}
7384d12b8b1SLauro Ramos Venancio 
73915e47304SEric W. Biederman 	rc = nfc_genl_send_device(msg, dev, info->snd_portid, info->snd_seq,
7404d12b8b1SLauro Ramos Venancio 				  NULL, 0);
7414d12b8b1SLauro Ramos Venancio 	if (rc < 0)
7424d12b8b1SLauro Ramos Venancio 		goto out_free;
7434d12b8b1SLauro Ramos Venancio 
7444d12b8b1SLauro Ramos Venancio 	nfc_put_device(dev);
7454d12b8b1SLauro Ramos Venancio 
7464d12b8b1SLauro Ramos Venancio 	return genlmsg_reply(msg, info);
7474d12b8b1SLauro Ramos Venancio 
7484d12b8b1SLauro Ramos Venancio out_free:
7494d12b8b1SLauro Ramos Venancio 	nlmsg_free(msg);
7504d12b8b1SLauro Ramos Venancio out_putdev:
7514d12b8b1SLauro Ramos Venancio 	nfc_put_device(dev);
7524d12b8b1SLauro Ramos Venancio 	return rc;
7534d12b8b1SLauro Ramos Venancio }
7544d12b8b1SLauro Ramos Venancio 
nfc_genl_dev_up(struct sk_buff * skb,struct genl_info * info)7558b3fe7b5SIlan Elias static int nfc_genl_dev_up(struct sk_buff *skb, struct genl_info *info)
7568b3fe7b5SIlan Elias {
7578b3fe7b5SIlan Elias 	struct nfc_dev *dev;
7588b3fe7b5SIlan Elias 	int rc;
7598b3fe7b5SIlan Elias 	u32 idx;
7608b3fe7b5SIlan Elias 
7618b3fe7b5SIlan Elias 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
7628b3fe7b5SIlan Elias 		return -EINVAL;
7638b3fe7b5SIlan Elias 
7648b3fe7b5SIlan Elias 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
7658b3fe7b5SIlan Elias 
7668b3fe7b5SIlan Elias 	dev = nfc_get_device(idx);
7678b3fe7b5SIlan Elias 	if (!dev)
7688b3fe7b5SIlan Elias 		return -ENODEV;
7698b3fe7b5SIlan Elias 
7708b3fe7b5SIlan Elias 	rc = nfc_dev_up(dev);
7718b3fe7b5SIlan Elias 
7728b3fe7b5SIlan Elias 	nfc_put_device(dev);
7738b3fe7b5SIlan Elias 	return rc;
7748b3fe7b5SIlan Elias }
7758b3fe7b5SIlan Elias 
nfc_genl_dev_down(struct sk_buff * skb,struct genl_info * info)7768b3fe7b5SIlan Elias static int nfc_genl_dev_down(struct sk_buff *skb, struct genl_info *info)
7778b3fe7b5SIlan Elias {
7788b3fe7b5SIlan Elias 	struct nfc_dev *dev;
7798b3fe7b5SIlan Elias 	int rc;
7808b3fe7b5SIlan Elias 	u32 idx;
7818b3fe7b5SIlan Elias 
7828b3fe7b5SIlan Elias 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
7838b3fe7b5SIlan Elias 		return -EINVAL;
7848b3fe7b5SIlan Elias 
7858b3fe7b5SIlan Elias 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
7868b3fe7b5SIlan Elias 
7878b3fe7b5SIlan Elias 	dev = nfc_get_device(idx);
7888b3fe7b5SIlan Elias 	if (!dev)
7898b3fe7b5SIlan Elias 		return -ENODEV;
7908b3fe7b5SIlan Elias 
7918b3fe7b5SIlan Elias 	rc = nfc_dev_down(dev);
7928b3fe7b5SIlan Elias 
7938b3fe7b5SIlan Elias 	nfc_put_device(dev);
7948b3fe7b5SIlan Elias 	return rc;
7958b3fe7b5SIlan Elias }
7968b3fe7b5SIlan Elias 
nfc_genl_start_poll(struct sk_buff * skb,struct genl_info * info)7974d12b8b1SLauro Ramos Venancio static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
7984d12b8b1SLauro Ramos Venancio {
7994d12b8b1SLauro Ramos Venancio 	struct nfc_dev *dev;
8004d12b8b1SLauro Ramos Venancio 	int rc;
8014d12b8b1SLauro Ramos Venancio 	u32 idx;
802fe7c5800SSamuel Ortiz 	u32 im_protocols = 0, tm_protocols = 0;
8034d12b8b1SLauro Ramos Venancio 
8041ed28f61SSamuel Ortiz 	pr_debug("Poll start\n");
8051ed28f61SSamuel Ortiz 
8064d12b8b1SLauro Ramos Venancio 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
807fe7c5800SSamuel Ortiz 	    ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] &&
808fe7c5800SSamuel Ortiz 	      !info->attrs[NFC_ATTR_PROTOCOLS]) &&
809fe7c5800SSamuel Ortiz 	      !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
8104d12b8b1SLauro Ramos Venancio 		return -EINVAL;
8114d12b8b1SLauro Ramos Venancio 
8124d12b8b1SLauro Ramos Venancio 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
813fe7c5800SSamuel Ortiz 
814fe7c5800SSamuel Ortiz 	if (info->attrs[NFC_ATTR_TM_PROTOCOLS])
815fe7c5800SSamuel Ortiz 		tm_protocols = nla_get_u32(info->attrs[NFC_ATTR_TM_PROTOCOLS]);
816fe7c5800SSamuel Ortiz 
817fe7c5800SSamuel Ortiz 	if (info->attrs[NFC_ATTR_IM_PROTOCOLS])
818fe7c5800SSamuel Ortiz 		im_protocols = nla_get_u32(info->attrs[NFC_ATTR_IM_PROTOCOLS]);
8195e50ee3aSSamuel Ortiz 	else if (info->attrs[NFC_ATTR_PROTOCOLS])
8205e50ee3aSSamuel Ortiz 		im_protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
8214d12b8b1SLauro Ramos Venancio 
8224d12b8b1SLauro Ramos Venancio 	dev = nfc_get_device(idx);
8234d12b8b1SLauro Ramos Venancio 	if (!dev)
8244d12b8b1SLauro Ramos Venancio 		return -ENODEV;
8254d12b8b1SLauro Ramos Venancio 
8264d12b8b1SLauro Ramos Venancio 	mutex_lock(&dev->genl_data.genl_data_mutex);
8274d12b8b1SLauro Ramos Venancio 
828fe7c5800SSamuel Ortiz 	rc = nfc_start_poll(dev, im_protocols, tm_protocols);
8294d12b8b1SLauro Ramos Venancio 	if (!rc)
83015e47304SEric W. Biederman 		dev->genl_data.poll_req_portid = info->snd_portid;
8314d12b8b1SLauro Ramos Venancio 
8324d12b8b1SLauro Ramos Venancio 	mutex_unlock(&dev->genl_data.genl_data_mutex);
8334d12b8b1SLauro Ramos Venancio 
8344d12b8b1SLauro Ramos Venancio 	nfc_put_device(dev);
8354d12b8b1SLauro Ramos Venancio 	return rc;
8364d12b8b1SLauro Ramos Venancio }
8374d12b8b1SLauro Ramos Venancio 
nfc_genl_stop_poll(struct sk_buff * skb,struct genl_info * info)8384d12b8b1SLauro Ramos Venancio static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info)
8394d12b8b1SLauro Ramos Venancio {
8404d12b8b1SLauro Ramos Venancio 	struct nfc_dev *dev;
8414d12b8b1SLauro Ramos Venancio 	int rc;
8424d12b8b1SLauro Ramos Venancio 	u32 idx;
8434d12b8b1SLauro Ramos Venancio 
8444d12b8b1SLauro Ramos Venancio 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
8454d12b8b1SLauro Ramos Venancio 		return -EINVAL;
8464d12b8b1SLauro Ramos Venancio 
8474d12b8b1SLauro Ramos Venancio 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
8484d12b8b1SLauro Ramos Venancio 
8494d12b8b1SLauro Ramos Venancio 	dev = nfc_get_device(idx);
8504d12b8b1SLauro Ramos Venancio 	if (!dev)
8514d12b8b1SLauro Ramos Venancio 		return -ENODEV;
8524d12b8b1SLauro Ramos Venancio 
853a831b913SSamuel Ortiz 	device_lock(&dev->dev);
854a831b913SSamuel Ortiz 
855a831b913SSamuel Ortiz 	if (!dev->polling) {
856a831b913SSamuel Ortiz 		device_unlock(&dev->dev);
857d8f923c3SPan Bian 		nfc_put_device(dev);
858a831b913SSamuel Ortiz 		return -EINVAL;
859a831b913SSamuel Ortiz 	}
860a831b913SSamuel Ortiz 
861a831b913SSamuel Ortiz 	device_unlock(&dev->dev);
862a831b913SSamuel Ortiz 
8634d12b8b1SLauro Ramos Venancio 	mutex_lock(&dev->genl_data.genl_data_mutex);
8644d12b8b1SLauro Ramos Venancio 
86515e47304SEric W. Biederman 	if (dev->genl_data.poll_req_portid != info->snd_portid) {
8664d12b8b1SLauro Ramos Venancio 		rc = -EBUSY;
8674d12b8b1SLauro Ramos Venancio 		goto out;
8684d12b8b1SLauro Ramos Venancio 	}
8694d12b8b1SLauro Ramos Venancio 
8704d12b8b1SLauro Ramos Venancio 	rc = nfc_stop_poll(dev);
87115e47304SEric W. Biederman 	dev->genl_data.poll_req_portid = 0;
8724d12b8b1SLauro Ramos Venancio 
8734d12b8b1SLauro Ramos Venancio out:
8744d12b8b1SLauro Ramos Venancio 	mutex_unlock(&dev->genl_data.genl_data_mutex);
8754d12b8b1SLauro Ramos Venancio 	nfc_put_device(dev);
8764d12b8b1SLauro Ramos Venancio 	return rc;
8774d12b8b1SLauro Ramos Venancio }
8784d12b8b1SLauro Ramos Venancio 
nfc_genl_activate_target(struct sk_buff * skb,struct genl_info * info)8793682f49fSChristophe Ricard static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
8803682f49fSChristophe Ricard {
8813682f49fSChristophe Ricard 	struct nfc_dev *dev;
8823682f49fSChristophe Ricard 	u32 device_idx, target_idx, protocol;
8833682f49fSChristophe Ricard 	int rc;
8843682f49fSChristophe Ricard 
885a0323b97SMateusz Jurczyk 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
886a0323b97SMateusz Jurczyk 	    !info->attrs[NFC_ATTR_TARGET_INDEX] ||
887a0323b97SMateusz Jurczyk 	    !info->attrs[NFC_ATTR_PROTOCOLS])
8883682f49fSChristophe Ricard 		return -EINVAL;
8893682f49fSChristophe Ricard 
8903682f49fSChristophe Ricard 	device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
8913682f49fSChristophe Ricard 
8923682f49fSChristophe Ricard 	dev = nfc_get_device(device_idx);
8933682f49fSChristophe Ricard 	if (!dev)
8943682f49fSChristophe Ricard 		return -ENODEV;
8953682f49fSChristophe Ricard 
8963682f49fSChristophe Ricard 	target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
8973682f49fSChristophe Ricard 	protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
8983682f49fSChristophe Ricard 
89996d4581fSChristophe Ricard 	nfc_deactivate_target(dev, target_idx, NFC_TARGET_MODE_SLEEP);
9003682f49fSChristophe Ricard 	rc = nfc_activate_target(dev, target_idx, protocol);
9013682f49fSChristophe Ricard 
9023682f49fSChristophe Ricard 	nfc_put_device(dev);
9033267183cSAndy Shevchenko 	return rc;
9043682f49fSChristophe Ricard }
9053682f49fSChristophe Ricard 
nfc_genl_deactivate_target(struct sk_buff * skb,struct genl_info * info)9064d63adfeSMark Greer static int nfc_genl_deactivate_target(struct sk_buff *skb,
9074d63adfeSMark Greer 				      struct genl_info *info)
9084d63adfeSMark Greer {
9094d63adfeSMark Greer 	struct nfc_dev *dev;
9104d63adfeSMark Greer 	u32 device_idx, target_idx;
9114d63adfeSMark Greer 	int rc;
9124d63adfeSMark Greer 
913385097a3SYoung Xiao 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
914385097a3SYoung Xiao 	    !info->attrs[NFC_ATTR_TARGET_INDEX])
9154d63adfeSMark Greer 		return -EINVAL;
9164d63adfeSMark Greer 
9174d63adfeSMark Greer 	device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
9184d63adfeSMark Greer 
9194d63adfeSMark Greer 	dev = nfc_get_device(device_idx);
9204d63adfeSMark Greer 	if (!dev)
9214d63adfeSMark Greer 		return -ENODEV;
9224d63adfeSMark Greer 
9234d63adfeSMark Greer 	target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
9244d63adfeSMark Greer 
9254d63adfeSMark Greer 	rc = nfc_deactivate_target(dev, target_idx, NFC_TARGET_MODE_SLEEP);
9264d63adfeSMark Greer 
9274d63adfeSMark Greer 	nfc_put_device(dev);
9284d63adfeSMark Greer 	return rc;
9294d63adfeSMark Greer }
9304d63adfeSMark Greer 
nfc_genl_dep_link_up(struct sk_buff * skb,struct genl_info * info)9311ed28f61SSamuel Ortiz static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info)
9321ed28f61SSamuel Ortiz {
9331ed28f61SSamuel Ortiz 	struct nfc_dev *dev;
9341ed28f61SSamuel Ortiz 	int rc, tgt_idx;
9351ed28f61SSamuel Ortiz 	u32 idx;
93647807d3dSSamuel Ortiz 	u8 comm;
9371ed28f61SSamuel Ortiz 
9381ed28f61SSamuel Ortiz 	pr_debug("DEP link up\n");
9391ed28f61SSamuel Ortiz 
9401ed28f61SSamuel Ortiz 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
94147807d3dSSamuel Ortiz 	    !info->attrs[NFC_ATTR_COMM_MODE])
9421ed28f61SSamuel Ortiz 		return -EINVAL;
9431ed28f61SSamuel Ortiz 
9441ed28f61SSamuel Ortiz 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
9451ed28f61SSamuel Ortiz 	if (!info->attrs[NFC_ATTR_TARGET_INDEX])
9461ed28f61SSamuel Ortiz 		tgt_idx = NFC_TARGET_IDX_ANY;
9471ed28f61SSamuel Ortiz 	else
9481ed28f61SSamuel Ortiz 		tgt_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
9491ed28f61SSamuel Ortiz 
9501ed28f61SSamuel Ortiz 	comm = nla_get_u8(info->attrs[NFC_ATTR_COMM_MODE]);
9511ed28f61SSamuel Ortiz 
9521ed28f61SSamuel Ortiz 	if (comm != NFC_COMM_ACTIVE && comm != NFC_COMM_PASSIVE)
9531ed28f61SSamuel Ortiz 		return -EINVAL;
9541ed28f61SSamuel Ortiz 
9551ed28f61SSamuel Ortiz 	dev = nfc_get_device(idx);
9561ed28f61SSamuel Ortiz 	if (!dev)
9571ed28f61SSamuel Ortiz 		return -ENODEV;
9581ed28f61SSamuel Ortiz 
95947807d3dSSamuel Ortiz 	rc = nfc_dep_link_up(dev, tgt_idx, comm);
9601ed28f61SSamuel Ortiz 
9611ed28f61SSamuel Ortiz 	nfc_put_device(dev);
9621ed28f61SSamuel Ortiz 
9631ed28f61SSamuel Ortiz 	return rc;
9641ed28f61SSamuel Ortiz }
9651ed28f61SSamuel Ortiz 
nfc_genl_dep_link_down(struct sk_buff * skb,struct genl_info * info)9661ed28f61SSamuel Ortiz static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info)
9671ed28f61SSamuel Ortiz {
9681ed28f61SSamuel Ortiz 	struct nfc_dev *dev;
9691ed28f61SSamuel Ortiz 	int rc;
9701ed28f61SSamuel Ortiz 	u32 idx;
9711ed28f61SSamuel Ortiz 
97218917d51SAndrey Konovalov 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
97318917d51SAndrey Konovalov 	    !info->attrs[NFC_ATTR_TARGET_INDEX])
9741ed28f61SSamuel Ortiz 		return -EINVAL;
9751ed28f61SSamuel Ortiz 
9761ed28f61SSamuel Ortiz 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
9771ed28f61SSamuel Ortiz 
9781ed28f61SSamuel Ortiz 	dev = nfc_get_device(idx);
9791ed28f61SSamuel Ortiz 	if (!dev)
9801ed28f61SSamuel Ortiz 		return -ENODEV;
9811ed28f61SSamuel Ortiz 
9821ed28f61SSamuel Ortiz 	rc = nfc_dep_link_down(dev);
9831ed28f61SSamuel Ortiz 
9841ed28f61SSamuel Ortiz 	nfc_put_device(dev);
9851ed28f61SSamuel Ortiz 	return rc;
9861ed28f61SSamuel Ortiz }
9871ed28f61SSamuel Ortiz 
nfc_genl_send_params(struct sk_buff * msg,struct nfc_llcp_local * local,u32 portid,u32 seq)98852feb444SThierry Escande static int nfc_genl_send_params(struct sk_buff *msg,
98952feb444SThierry Escande 				struct nfc_llcp_local *local,
99052feb444SThierry Escande 				u32 portid, u32 seq)
99152feb444SThierry Escande {
99252feb444SThierry Escande 	void *hdr;
99352feb444SThierry Escande 
99452feb444SThierry Escande 	hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0,
99552feb444SThierry Escande 			  NFC_CMD_LLC_GET_PARAMS);
99652feb444SThierry Escande 	if (!hdr)
99752feb444SThierry Escande 		return -EMSGSIZE;
99852feb444SThierry Escande 
99952feb444SThierry Escande 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) ||
100052feb444SThierry Escande 	    nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) ||
100152feb444SThierry Escande 	    nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) ||
100252feb444SThierry Escande 	    nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux)))
100352feb444SThierry Escande 		goto nla_put_failure;
100452feb444SThierry Escande 
1005053c095aSJohannes Berg 	genlmsg_end(msg, hdr);
1006053c095aSJohannes Berg 	return 0;
100752feb444SThierry Escande 
100852feb444SThierry Escande nla_put_failure:
100952feb444SThierry Escande 	genlmsg_cancel(msg, hdr);
101052feb444SThierry Escande 	return -EMSGSIZE;
101152feb444SThierry Escande }
101252feb444SThierry Escande 
nfc_genl_llc_get_params(struct sk_buff * skb,struct genl_info * info)101352feb444SThierry Escande static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info)
101452feb444SThierry Escande {
101552feb444SThierry Escande 	struct nfc_dev *dev;
101652feb444SThierry Escande 	struct nfc_llcp_local *local;
101752feb444SThierry Escande 	int rc = 0;
101852feb444SThierry Escande 	struct sk_buff *msg = NULL;
101952feb444SThierry Escande 	u32 idx;
102052feb444SThierry Escande 
102118917d51SAndrey Konovalov 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
102218917d51SAndrey Konovalov 	    !info->attrs[NFC_ATTR_FIRMWARE_NAME])
102352feb444SThierry Escande 		return -EINVAL;
102452feb444SThierry Escande 
102552feb444SThierry Escande 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
102652feb444SThierry Escande 
102752feb444SThierry Escande 	dev = nfc_get_device(idx);
102852feb444SThierry Escande 	if (!dev)
102952feb444SThierry Escande 		return -ENODEV;
103052feb444SThierry Escande 
103152feb444SThierry Escande 	device_lock(&dev->dev);
103252feb444SThierry Escande 
103352feb444SThierry Escande 	local = nfc_llcp_find_local(dev);
103452feb444SThierry Escande 	if (!local) {
103552feb444SThierry Escande 		rc = -ENODEV;
103652feb444SThierry Escande 		goto exit;
103752feb444SThierry Escande 	}
103852feb444SThierry Escande 
103952feb444SThierry Escande 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
104052feb444SThierry Escande 	if (!msg) {
104152feb444SThierry Escande 		rc = -ENOMEM;
10426709d4b7SLin Ma 		goto put_local;
104352feb444SThierry Escande 	}
104452feb444SThierry Escande 
104552feb444SThierry Escande 	rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq);
104652feb444SThierry Escande 
10476709d4b7SLin Ma put_local:
10486709d4b7SLin Ma 	nfc_llcp_local_put(local);
10496709d4b7SLin Ma 
105052feb444SThierry Escande exit:
105152feb444SThierry Escande 	device_unlock(&dev->dev);
105252feb444SThierry Escande 
105352feb444SThierry Escande 	nfc_put_device(dev);
105452feb444SThierry Escande 
105552feb444SThierry Escande 	if (rc < 0) {
105652feb444SThierry Escande 		if (msg)
105752feb444SThierry Escande 			nlmsg_free(msg);
105852feb444SThierry Escande 
105952feb444SThierry Escande 		return rc;
106052feb444SThierry Escande 	}
106152feb444SThierry Escande 
106252feb444SThierry Escande 	return genlmsg_reply(msg, info);
106352feb444SThierry Escande }
106452feb444SThierry Escande 
nfc_genl_llc_set_params(struct sk_buff * skb,struct genl_info * info)106552feb444SThierry Escande static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info)
106652feb444SThierry Escande {
106752feb444SThierry Escande 	struct nfc_dev *dev;
106852feb444SThierry Escande 	struct nfc_llcp_local *local;
106952feb444SThierry Escande 	u8 rw = 0;
107052feb444SThierry Escande 	u16 miux = 0;
107152feb444SThierry Escande 	u32 idx;
107252feb444SThierry Escande 	int rc = 0;
107352feb444SThierry Escande 
107452feb444SThierry Escande 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
107552feb444SThierry Escande 	    (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] &&
107652feb444SThierry Escande 	     !info->attrs[NFC_ATTR_LLC_PARAM_RW] &&
107752feb444SThierry Escande 	     !info->attrs[NFC_ATTR_LLC_PARAM_MIUX]))
107852feb444SThierry Escande 		return -EINVAL;
107952feb444SThierry Escande 
108052feb444SThierry Escande 	if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) {
108152feb444SThierry Escande 		rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]);
108252feb444SThierry Escande 
108352feb444SThierry Escande 		if (rw > LLCP_MAX_RW)
108452feb444SThierry Escande 			return -EINVAL;
108552feb444SThierry Escande 	}
108652feb444SThierry Escande 
108752feb444SThierry Escande 	if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) {
108852feb444SThierry Escande 		miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]);
108952feb444SThierry Escande 
109052feb444SThierry Escande 		if (miux > LLCP_MAX_MIUX)
109152feb444SThierry Escande 			return -EINVAL;
109252feb444SThierry Escande 	}
109352feb444SThierry Escande 
109452feb444SThierry Escande 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
109552feb444SThierry Escande 
109652feb444SThierry Escande 	dev = nfc_get_device(idx);
109752feb444SThierry Escande 	if (!dev)
109852feb444SThierry Escande 		return -ENODEV;
109952feb444SThierry Escande 
110052feb444SThierry Escande 	device_lock(&dev->dev);
110152feb444SThierry Escande 
110252feb444SThierry Escande 	local = nfc_llcp_find_local(dev);
110352feb444SThierry Escande 	if (!local) {
110452feb444SThierry Escande 		rc = -ENODEV;
110552feb444SThierry Escande 		goto exit;
110652feb444SThierry Escande 	}
110752feb444SThierry Escande 
110852feb444SThierry Escande 	if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) {
110952feb444SThierry Escande 		if (dev->dep_link_up) {
111052feb444SThierry Escande 			rc = -EINPROGRESS;
11116709d4b7SLin Ma 			goto put_local;
111252feb444SThierry Escande 		}
111352feb444SThierry Escande 
111452feb444SThierry Escande 		local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]);
111552feb444SThierry Escande 	}
111652feb444SThierry Escande 
111752feb444SThierry Escande 	if (info->attrs[NFC_ATTR_LLC_PARAM_RW])
111852feb444SThierry Escande 		local->rw = rw;
111952feb444SThierry Escande 
112052feb444SThierry Escande 	if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX])
112152feb444SThierry Escande 		local->miux = cpu_to_be16(miux);
112252feb444SThierry Escande 
11236709d4b7SLin Ma put_local:
11246709d4b7SLin Ma 	nfc_llcp_local_put(local);
11256709d4b7SLin Ma 
112652feb444SThierry Escande exit:
112752feb444SThierry Escande 	device_unlock(&dev->dev);
112852feb444SThierry Escande 
112952feb444SThierry Escande 	nfc_put_device(dev);
113052feb444SThierry Escande 
113152feb444SThierry Escande 	return rc;
113252feb444SThierry Escande }
113352feb444SThierry Escande 
nfc_genl_llc_sdreq(struct sk_buff * skb,struct genl_info * info)1134d9b8d8e1SThierry Escande static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
1135d9b8d8e1SThierry Escande {
1136d9b8d8e1SThierry Escande 	struct nfc_dev *dev;
1137d9b8d8e1SThierry Escande 	struct nfc_llcp_local *local;
1138d9b8d8e1SThierry Escande 	struct nlattr *attr, *sdp_attrs[NFC_SDP_ATTR_MAX+1];
1139d9b8d8e1SThierry Escande 	u32 idx;
1140d9b8d8e1SThierry Escande 	u8 tid;
1141d9b8d8e1SThierry Escande 	char *uri;
1142d9b8d8e1SThierry Escande 	int rc = 0, rem;
1143d9b8d8e1SThierry Escande 	size_t uri_len, tlvs_len;
1144d9b8d8e1SThierry Escande 	struct hlist_head sdreq_list;
1145d9b8d8e1SThierry Escande 	struct nfc_llcp_sdp_tlv *sdreq;
1146d9b8d8e1SThierry Escande 
1147d9b8d8e1SThierry Escande 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
1148d9b8d8e1SThierry Escande 	    !info->attrs[NFC_ATTR_LLC_SDP])
1149d9b8d8e1SThierry Escande 		return -EINVAL;
1150d9b8d8e1SThierry Escande 
1151d9b8d8e1SThierry Escande 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
1152d9b8d8e1SThierry Escande 
1153d9b8d8e1SThierry Escande 	dev = nfc_get_device(idx);
11549abebb8aSJulia Lawall 	if (!dev)
11559abebb8aSJulia Lawall 		return -ENODEV;
1156d9b8d8e1SThierry Escande 
1157d9b8d8e1SThierry Escande 	device_lock(&dev->dev);
1158d9b8d8e1SThierry Escande 
1159d9b8d8e1SThierry Escande 	if (dev->dep_link_up == false) {
1160d9b8d8e1SThierry Escande 		rc = -ENOLINK;
1161d9b8d8e1SThierry Escande 		goto exit;
1162d9b8d8e1SThierry Escande 	}
1163d9b8d8e1SThierry Escande 
1164d9b8d8e1SThierry Escande 	local = nfc_llcp_find_local(dev);
1165d9b8d8e1SThierry Escande 	if (!local) {
1166d9b8d8e1SThierry Escande 		rc = -ENODEV;
1167d9b8d8e1SThierry Escande 		goto exit;
1168d9b8d8e1SThierry Escande 	}
1169d9b8d8e1SThierry Escande 
1170d9b8d8e1SThierry Escande 	INIT_HLIST_HEAD(&sdreq_list);
1171d9b8d8e1SThierry Escande 
1172d9b8d8e1SThierry Escande 	tlvs_len = 0;
1173d9b8d8e1SThierry Escande 
1174d9b8d8e1SThierry Escande 	nla_for_each_nested(attr, info->attrs[NFC_ATTR_LLC_SDP], rem) {
11758cb08174SJohannes Berg 		rc = nla_parse_nested_deprecated(sdp_attrs, NFC_SDP_ATTR_MAX,
11768cb08174SJohannes Berg 						 attr, nfc_sdp_genl_policy,
11778cb08174SJohannes Berg 						 info->extack);
1178d9b8d8e1SThierry Escande 
1179d9b8d8e1SThierry Escande 		if (rc != 0) {
1180d9b8d8e1SThierry Escande 			rc = -EINVAL;
11816709d4b7SLin Ma 			goto put_local;
1182d9b8d8e1SThierry Escande 		}
1183d9b8d8e1SThierry Escande 
1184d9b8d8e1SThierry Escande 		if (!sdp_attrs[NFC_SDP_ATTR_URI])
1185d9b8d8e1SThierry Escande 			continue;
1186d9b8d8e1SThierry Escande 
1187d9b8d8e1SThierry Escande 		uri_len = nla_len(sdp_attrs[NFC_SDP_ATTR_URI]);
1188d9b8d8e1SThierry Escande 		if (uri_len == 0)
1189d9b8d8e1SThierry Escande 			continue;
1190d9b8d8e1SThierry Escande 
1191d9b8d8e1SThierry Escande 		uri = nla_data(sdp_attrs[NFC_SDP_ATTR_URI]);
1192d9b8d8e1SThierry Escande 		if (uri == NULL || *uri == 0)
1193d9b8d8e1SThierry Escande 			continue;
1194d9b8d8e1SThierry Escande 
1195d9b8d8e1SThierry Escande 		tid = local->sdreq_next_tid++;
1196d9b8d8e1SThierry Escande 
1197d9b8d8e1SThierry Escande 		sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len);
1198d9b8d8e1SThierry Escande 		if (sdreq == NULL) {
1199d9b8d8e1SThierry Escande 			rc = -ENOMEM;
12006709d4b7SLin Ma 			goto put_local;
1201d9b8d8e1SThierry Escande 		}
1202d9b8d8e1SThierry Escande 
1203d9b8d8e1SThierry Escande 		tlvs_len += sdreq->tlv_len;
1204d9b8d8e1SThierry Escande 
1205d9b8d8e1SThierry Escande 		hlist_add_head(&sdreq->node, &sdreq_list);
1206d9b8d8e1SThierry Escande 	}
1207d9b8d8e1SThierry Escande 
1208d9b8d8e1SThierry Escande 	if (hlist_empty(&sdreq_list)) {
1209d9b8d8e1SThierry Escande 		rc = -EINVAL;
12106709d4b7SLin Ma 		goto put_local;
1211d9b8d8e1SThierry Escande 	}
1212d9b8d8e1SThierry Escande 
1213d9b8d8e1SThierry Escande 	rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len);
12146709d4b7SLin Ma 
12156709d4b7SLin Ma put_local:
12166709d4b7SLin Ma 	nfc_llcp_local_put(local);
12176709d4b7SLin Ma 
1218d9b8d8e1SThierry Escande exit:
1219d9b8d8e1SThierry Escande 	device_unlock(&dev->dev);
1220d9b8d8e1SThierry Escande 
1221d9b8d8e1SThierry Escande 	nfc_put_device(dev);
1222d9b8d8e1SThierry Escande 
1223d9b8d8e1SThierry Escande 	return rc;
1224d9b8d8e1SThierry Escande }
1225d9b8d8e1SThierry Escande 
nfc_genl_fw_download(struct sk_buff * skb,struct genl_info * info)12269ea7187cSSamuel Ortiz static int nfc_genl_fw_download(struct sk_buff *skb, struct genl_info *info)
12279674da87SEric Lapuyade {
12289674da87SEric Lapuyade 	struct nfc_dev *dev;
12299674da87SEric Lapuyade 	int rc;
12309674da87SEric Lapuyade 	u32 idx;
12319674da87SEric Lapuyade 	char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
12329674da87SEric Lapuyade 
1233280e3ebdSDefang Bo 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || !info->attrs[NFC_ATTR_FIRMWARE_NAME])
12349674da87SEric Lapuyade 		return -EINVAL;
12359674da87SEric Lapuyade 
12369674da87SEric Lapuyade 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
12379674da87SEric Lapuyade 
12389674da87SEric Lapuyade 	dev = nfc_get_device(idx);
12399674da87SEric Lapuyade 	if (!dev)
12409674da87SEric Lapuyade 		return -ENODEV;
12419674da87SEric Lapuyade 
1242872f6903SFrancis Laniel 	nla_strscpy(firmware_name, info->attrs[NFC_ATTR_FIRMWARE_NAME],
12439674da87SEric Lapuyade 		    sizeof(firmware_name));
12449674da87SEric Lapuyade 
12459ea7187cSSamuel Ortiz 	rc = nfc_fw_download(dev, firmware_name);
12469674da87SEric Lapuyade 
12479674da87SEric Lapuyade 	nfc_put_device(dev);
12489674da87SEric Lapuyade 	return rc;
12499674da87SEric Lapuyade }
12509674da87SEric Lapuyade 
nfc_genl_fw_download_done(struct nfc_dev * dev,const char * firmware_name,u32 result)1251352a5f5fSEric Lapuyade int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
1252352a5f5fSEric Lapuyade 			      u32 result)
12539674da87SEric Lapuyade {
12549674da87SEric Lapuyade 	struct sk_buff *msg;
12559674da87SEric Lapuyade 	void *hdr;
12569674da87SEric Lapuyade 
12574071bf12SDuoming Zhou 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
12589674da87SEric Lapuyade 	if (!msg)
12599674da87SEric Lapuyade 		return -ENOMEM;
12609674da87SEric Lapuyade 
12619674da87SEric Lapuyade 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
12629ea7187cSSamuel Ortiz 			  NFC_CMD_FW_DOWNLOAD);
12639674da87SEric Lapuyade 	if (!hdr)
12649674da87SEric Lapuyade 		goto free_msg;
12659674da87SEric Lapuyade 
12669674da87SEric Lapuyade 	if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
1267352a5f5fSEric Lapuyade 	    nla_put_u32(msg, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, result) ||
12689674da87SEric Lapuyade 	    nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
12699674da87SEric Lapuyade 		goto nla_put_failure;
12709674da87SEric Lapuyade 
12719674da87SEric Lapuyade 	genlmsg_end(msg, hdr);
12729674da87SEric Lapuyade 
12734071bf12SDuoming Zhou 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC);
12749674da87SEric Lapuyade 
12759674da87SEric Lapuyade 	return 0;
12769674da87SEric Lapuyade 
12779674da87SEric Lapuyade nla_put_failure:
12789674da87SEric Lapuyade free_msg:
12799674da87SEric Lapuyade 	nlmsg_free(msg);
12809674da87SEric Lapuyade 	return -EMSGSIZE;
12819674da87SEric Lapuyade }
12829674da87SEric Lapuyade 
nfc_genl_enable_se(struct sk_buff * skb,struct genl_info * info)1283be085653SSamuel Ortiz static int nfc_genl_enable_se(struct sk_buff *skb, struct genl_info *info)
1284be085653SSamuel Ortiz {
1285be085653SSamuel Ortiz 	struct nfc_dev *dev;
1286be085653SSamuel Ortiz 	int rc;
1287be085653SSamuel Ortiz 	u32 idx, se_idx;
1288be085653SSamuel Ortiz 
1289be085653SSamuel Ortiz 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
1290be085653SSamuel Ortiz 	    !info->attrs[NFC_ATTR_SE_INDEX])
1291be085653SSamuel Ortiz 		return -EINVAL;
1292be085653SSamuel Ortiz 
1293be085653SSamuel Ortiz 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
1294be085653SSamuel Ortiz 	se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
1295be085653SSamuel Ortiz 
1296be085653SSamuel Ortiz 	dev = nfc_get_device(idx);
1297be085653SSamuel Ortiz 	if (!dev)
1298be085653SSamuel Ortiz 		return -ENODEV;
1299be085653SSamuel Ortiz 
1300be085653SSamuel Ortiz 	rc = nfc_enable_se(dev, se_idx);
1301be085653SSamuel Ortiz 
1302be085653SSamuel Ortiz 	nfc_put_device(dev);
1303be085653SSamuel Ortiz 	return rc;
1304be085653SSamuel Ortiz }
1305be085653SSamuel Ortiz 
nfc_genl_disable_se(struct sk_buff * skb,struct genl_info * info)1306be085653SSamuel Ortiz static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info)
1307be085653SSamuel Ortiz {
1308be085653SSamuel Ortiz 	struct nfc_dev *dev;
1309be085653SSamuel Ortiz 	int rc;
1310be085653SSamuel Ortiz 	u32 idx, se_idx;
1311be085653SSamuel Ortiz 
1312be085653SSamuel Ortiz 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
1313be085653SSamuel Ortiz 	    !info->attrs[NFC_ATTR_SE_INDEX])
1314be085653SSamuel Ortiz 		return -EINVAL;
1315be085653SSamuel Ortiz 
1316be085653SSamuel Ortiz 	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
1317be085653SSamuel Ortiz 	se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
1318be085653SSamuel Ortiz 
1319be085653SSamuel Ortiz 	dev = nfc_get_device(idx);
1320be085653SSamuel Ortiz 	if (!dev)
1321be085653SSamuel Ortiz 		return -ENODEV;
1322be085653SSamuel Ortiz 
1323be085653SSamuel Ortiz 	rc = nfc_disable_se(dev, se_idx);
1324be085653SSamuel Ortiz 
1325be085653SSamuel Ortiz 	nfc_put_device(dev);
1326be085653SSamuel Ortiz 	return rc;
1327be085653SSamuel Ortiz }
1328be085653SSamuel Ortiz 
nfc_genl_send_se(struct sk_buff * msg,struct nfc_dev * dev,u32 portid,u32 seq,struct netlink_callback * cb,int flags)1329ac22ac46SSamuel Ortiz static int nfc_genl_send_se(struct sk_buff *msg, struct nfc_dev *dev,
1330ac22ac46SSamuel Ortiz 				u32 portid, u32 seq,
1331ac22ac46SSamuel Ortiz 				struct netlink_callback *cb,
1332ac22ac46SSamuel Ortiz 				int flags)
1333ac22ac46SSamuel Ortiz {
1334ac22ac46SSamuel Ortiz 	void *hdr;
1335ac22ac46SSamuel Ortiz 	struct nfc_se *se, *n;
1336ac22ac46SSamuel Ortiz 
1337ac22ac46SSamuel Ortiz 	list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
1338ac22ac46SSamuel Ortiz 		hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags,
1339ac22ac46SSamuel Ortiz 				  NFC_CMD_GET_SE);
1340ac22ac46SSamuel Ortiz 		if (!hdr)
1341ac22ac46SSamuel Ortiz 			goto nla_put_failure;
1342ac22ac46SSamuel Ortiz 
1343ac22ac46SSamuel Ortiz 		if (cb)
13440a833c29SMichal Kubecek 			genl_dump_check_consistent(cb, hdr);
1345ac22ac46SSamuel Ortiz 
1346ac22ac46SSamuel Ortiz 		if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
1347ac22ac46SSamuel Ortiz 		    nla_put_u32(msg, NFC_ATTR_SE_INDEX, se->idx) ||
1348ac22ac46SSamuel Ortiz 		    nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type))
1349ac22ac46SSamuel Ortiz 			goto nla_put_failure;
1350ac22ac46SSamuel Ortiz 
1351053c095aSJohannes Berg 		genlmsg_end(msg, hdr);
1352ac22ac46SSamuel Ortiz 	}
1353ac22ac46SSamuel Ortiz 
1354ac22ac46SSamuel Ortiz 	return 0;
1355ac22ac46SSamuel Ortiz 
1356ac22ac46SSamuel Ortiz nla_put_failure:
1357ac22ac46SSamuel Ortiz 	genlmsg_cancel(msg, hdr);
1358ac22ac46SSamuel Ortiz 	return -EMSGSIZE;
1359ac22ac46SSamuel Ortiz }
1360ac22ac46SSamuel Ortiz 
nfc_genl_dump_ses(struct sk_buff * skb,struct netlink_callback * cb)1361ac22ac46SSamuel Ortiz static int nfc_genl_dump_ses(struct sk_buff *skb,
1362ac22ac46SSamuel Ortiz 				 struct netlink_callback *cb)
1363ac22ac46SSamuel Ortiz {
1364ac22ac46SSamuel Ortiz 	struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
1365ac22ac46SSamuel Ortiz 	struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
1366ac22ac46SSamuel Ortiz 	bool first_call = false;
1367ac22ac46SSamuel Ortiz 
1368ac22ac46SSamuel Ortiz 	if (!iter) {
1369ac22ac46SSamuel Ortiz 		first_call = true;
1370ac22ac46SSamuel Ortiz 		iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
1371ac22ac46SSamuel Ortiz 		if (!iter)
1372ac22ac46SSamuel Ortiz 			return -ENOMEM;
1373ac22ac46SSamuel Ortiz 		cb->args[0] = (long) iter;
1374ac22ac46SSamuel Ortiz 	}
1375ac22ac46SSamuel Ortiz 
1376ac22ac46SSamuel Ortiz 	mutex_lock(&nfc_devlist_mutex);
1377ac22ac46SSamuel Ortiz 
1378ac22ac46SSamuel Ortiz 	cb->seq = nfc_devlist_generation;
1379ac22ac46SSamuel Ortiz 
1380ac22ac46SSamuel Ortiz 	if (first_call) {
1381ac22ac46SSamuel Ortiz 		nfc_device_iter_init(iter);
1382ac22ac46SSamuel Ortiz 		dev = nfc_device_iter_next(iter);
1383ac22ac46SSamuel Ortiz 	}
1384ac22ac46SSamuel Ortiz 
1385ac22ac46SSamuel Ortiz 	while (dev) {
1386ac22ac46SSamuel Ortiz 		int rc;
1387ac22ac46SSamuel Ortiz 
1388ac22ac46SSamuel Ortiz 		rc = nfc_genl_send_se(skb, dev, NETLINK_CB(cb->skb).portid,
1389ac22ac46SSamuel Ortiz 					  cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
1390ac22ac46SSamuel Ortiz 		if (rc < 0)
1391ac22ac46SSamuel Ortiz 			break;
1392ac22ac46SSamuel Ortiz 
1393ac22ac46SSamuel Ortiz 		dev = nfc_device_iter_next(iter);
1394ac22ac46SSamuel Ortiz 	}
1395ac22ac46SSamuel Ortiz 
1396ac22ac46SSamuel Ortiz 	mutex_unlock(&nfc_devlist_mutex);
1397ac22ac46SSamuel Ortiz 
1398ac22ac46SSamuel Ortiz 	cb->args[1] = (long) dev;
1399ac22ac46SSamuel Ortiz 
1400ac22ac46SSamuel Ortiz 	return skb->len;
1401ac22ac46SSamuel Ortiz }
1402ac22ac46SSamuel Ortiz 
nfc_genl_dump_ses_done(struct netlink_callback * cb)1403ac22ac46SSamuel Ortiz static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
1404ac22ac46SSamuel Ortiz {
1405ac22ac46SSamuel Ortiz 	struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
1406ac22ac46SSamuel Ortiz 
14074cd8371aSKrzysztof Kozlowski 	if (iter) {
1408ac22ac46SSamuel Ortiz 		nfc_device_iter_exit(iter);
1409ac22ac46SSamuel Ortiz 		kfree(iter);
14104cd8371aSKrzysztof Kozlowski 	}
1411ac22ac46SSamuel Ortiz 
1412ac22ac46SSamuel Ortiz 	return 0;
1413ac22ac46SSamuel Ortiz }
1414ac22ac46SSamuel Ortiz 
nfc_se_io(struct nfc_dev * dev,u32 se_idx,u8 * apdu,size_t apdu_length,se_io_cb_t cb,void * cb_context)1415cd96db6fSChristophe Ricard static int nfc_se_io(struct nfc_dev *dev, u32 se_idx,
1416cd96db6fSChristophe Ricard 		     u8 *apdu, size_t apdu_length,
1417cd96db6fSChristophe Ricard 		     se_io_cb_t cb, void *cb_context)
1418cd96db6fSChristophe Ricard {
1419cd96db6fSChristophe Ricard 	struct nfc_se *se;
1420cd96db6fSChristophe Ricard 	int rc;
1421cd96db6fSChristophe Ricard 
1422cd96db6fSChristophe Ricard 	pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
1423cd96db6fSChristophe Ricard 
1424cd96db6fSChristophe Ricard 	device_lock(&dev->dev);
1425cd96db6fSChristophe Ricard 
1426cd96db6fSChristophe Ricard 	if (!device_is_registered(&dev->dev)) {
1427cd96db6fSChristophe Ricard 		rc = -ENODEV;
1428cd96db6fSChristophe Ricard 		goto error;
1429cd96db6fSChristophe Ricard 	}
1430cd96db6fSChristophe Ricard 
1431cd96db6fSChristophe Ricard 	if (!dev->dev_up) {
1432cd96db6fSChristophe Ricard 		rc = -ENODEV;
1433cd96db6fSChristophe Ricard 		goto error;
1434cd96db6fSChristophe Ricard 	}
1435cd96db6fSChristophe Ricard 
1436cd96db6fSChristophe Ricard 	if (!dev->ops->se_io) {
1437cd96db6fSChristophe Ricard 		rc = -EOPNOTSUPP;
1438cd96db6fSChristophe Ricard 		goto error;
1439cd96db6fSChristophe Ricard 	}
1440cd96db6fSChristophe Ricard 
1441cd96db6fSChristophe Ricard 	se = nfc_find_se(dev, se_idx);
1442cd96db6fSChristophe Ricard 	if (!se) {
1443cd96db6fSChristophe Ricard 		rc = -EINVAL;
1444cd96db6fSChristophe Ricard 		goto error;
1445cd96db6fSChristophe Ricard 	}
1446cd96db6fSChristophe Ricard 
1447cd96db6fSChristophe Ricard 	if (se->state != NFC_SE_ENABLED) {
1448cd96db6fSChristophe Ricard 		rc = -ENODEV;
1449cd96db6fSChristophe Ricard 		goto error;
1450cd96db6fSChristophe Ricard 	}
1451cd96db6fSChristophe Ricard 
1452cd96db6fSChristophe Ricard 	rc = dev->ops->se_io(dev, se_idx, apdu,
1453cd96db6fSChristophe Ricard 			apdu_length, cb, cb_context);
1454cd96db6fSChristophe Ricard 
145525ff6f8aSFedor Pchelkin 	device_unlock(&dev->dev);
145625ff6f8aSFedor Pchelkin 	return rc;
145725ff6f8aSFedor Pchelkin 
1458cd96db6fSChristophe Ricard error:
1459cd96db6fSChristophe Ricard 	device_unlock(&dev->dev);
14607d834b4dSFedor Pchelkin 	kfree(cb_context);
1461cd96db6fSChristophe Ricard 	return rc;
1462cd96db6fSChristophe Ricard }
1463cd96db6fSChristophe Ricard 
14645ce3f32bSSamuel Ortiz struct se_io_ctx {
14655ce3f32bSSamuel Ortiz 	u32 dev_idx;
14665ce3f32bSSamuel Ortiz 	u32 se_idx;
14675ce3f32bSSamuel Ortiz };
14685ce3f32bSSamuel Ortiz 
se_io_cb(void * context,u8 * apdu,size_t apdu_len,int err)1469ddc1a70bSSamuel Ortiz static void se_io_cb(void *context, u8 *apdu, size_t apdu_len, int err)
14705ce3f32bSSamuel Ortiz {
14715ce3f32bSSamuel Ortiz 	struct se_io_ctx *ctx = context;
14725ce3f32bSSamuel Ortiz 	struct sk_buff *msg;
14735ce3f32bSSamuel Ortiz 	void *hdr;
14745ce3f32bSSamuel Ortiz 
14755ce3f32bSSamuel Ortiz 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
14765ce3f32bSSamuel Ortiz 	if (!msg) {
14775ce3f32bSSamuel Ortiz 		kfree(ctx);
14785ce3f32bSSamuel Ortiz 		return;
14795ce3f32bSSamuel Ortiz 	}
14805ce3f32bSSamuel Ortiz 
14815ce3f32bSSamuel Ortiz 	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
14825ce3f32bSSamuel Ortiz 			  NFC_CMD_SE_IO);
14835ce3f32bSSamuel Ortiz 	if (!hdr)
14845ce3f32bSSamuel Ortiz 		goto free_msg;
14855ce3f32bSSamuel Ortiz 
14865ce3f32bSSamuel Ortiz 	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, ctx->dev_idx) ||
14875ce3f32bSSamuel Ortiz 	    nla_put_u32(msg, NFC_ATTR_SE_INDEX, ctx->se_idx) ||
14885ce3f32bSSamuel Ortiz 	    nla_put(msg, NFC_ATTR_SE_APDU, apdu_len, apdu))
14895ce3f32bSSamuel Ortiz 		goto nla_put_failure;
14905ce3f32bSSamuel Ortiz 
14915ce3f32bSSamuel Ortiz 	genlmsg_end(msg, hdr);
14925ce3f32bSSamuel Ortiz 
14932a94fe48SJohannes Berg 	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
14945ce3f32bSSamuel Ortiz 
14955ce3f32bSSamuel Ortiz 	kfree(ctx);
14965ce3f32bSSamuel Ortiz 
14975ce3f32bSSamuel Ortiz 	return;
14985ce3f32bSSamuel Ortiz 
14995ce3f32bSSamuel Ortiz nla_put_failure:
15005ce3f32bSSamuel Ortiz free_msg:
15015ce3f32bSSamuel Ortiz 	nlmsg_free(msg);
15025ce3f32bSSamuel Ortiz 	kfree(ctx);
15035ce3f32bSSamuel Ortiz 
15045ce3f32bSSamuel Ortiz 	return;
15055ce3f32bSSamuel Ortiz }
15065ce3f32bSSamuel Ortiz 
nfc_genl_se_io(struct sk_buff * skb,struct genl_info * info)15075ce3f32bSSamuel Ortiz static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
15085ce3f32bSSamuel Ortiz {
15095ce3f32bSSamuel Ortiz 	struct nfc_dev *dev;
15105ce3f32bSSamuel Ortiz 	struct se_io_ctx *ctx;
15115ce3f32bSSamuel Ortiz 	u32 dev_idx, se_idx;
15125ce3f32bSSamuel Ortiz 	u8 *apdu;
15135ce3f32bSSamuel Ortiz 	size_t apdu_len;
1514df49908fSMiaoqian Lin 	int rc;
15155ce3f32bSSamuel Ortiz 
15165ce3f32bSSamuel Ortiz 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
15175ce3f32bSSamuel Ortiz 	    !info->attrs[NFC_ATTR_SE_INDEX] ||
15185ce3f32bSSamuel Ortiz 	    !info->attrs[NFC_ATTR_SE_APDU])
15195ce3f32bSSamuel Ortiz 		return -EINVAL;
15205ce3f32bSSamuel Ortiz 
15215ce3f32bSSamuel Ortiz 	dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
15225ce3f32bSSamuel Ortiz 	se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
15235ce3f32bSSamuel Ortiz 
15245ce3f32bSSamuel Ortiz 	dev = nfc_get_device(dev_idx);
15255ce3f32bSSamuel Ortiz 	if (!dev)
15265ce3f32bSSamuel Ortiz 		return -ENODEV;
15275ce3f32bSSamuel Ortiz 
1528df49908fSMiaoqian Lin 	if (!dev->ops || !dev->ops->se_io) {
1529df49908fSMiaoqian Lin 		rc = -EOPNOTSUPP;
1530df49908fSMiaoqian Lin 		goto put_dev;
1531df49908fSMiaoqian Lin 	}
15325ce3f32bSSamuel Ortiz 
15335ce3f32bSSamuel Ortiz 	apdu_len = nla_len(info->attrs[NFC_ATTR_SE_APDU]);
1534df49908fSMiaoqian Lin 	if (apdu_len == 0) {
1535df49908fSMiaoqian Lin 		rc = -EINVAL;
1536df49908fSMiaoqian Lin 		goto put_dev;
1537df49908fSMiaoqian Lin 	}
15385ce3f32bSSamuel Ortiz 
15395ce3f32bSSamuel Ortiz 	apdu = nla_data(info->attrs[NFC_ATTR_SE_APDU]);
1540df49908fSMiaoqian Lin 	if (!apdu) {
1541df49908fSMiaoqian Lin 		rc = -EINVAL;
1542df49908fSMiaoqian Lin 		goto put_dev;
1543df49908fSMiaoqian Lin 	}
15445ce3f32bSSamuel Ortiz 
15455ce3f32bSSamuel Ortiz 	ctx = kzalloc(sizeof(struct se_io_ctx), GFP_KERNEL);
1546df49908fSMiaoqian Lin 	if (!ctx) {
1547df49908fSMiaoqian Lin 		rc = -ENOMEM;
1548df49908fSMiaoqian Lin 		goto put_dev;
1549df49908fSMiaoqian Lin 	}
15505ce3f32bSSamuel Ortiz 
15515ce3f32bSSamuel Ortiz 	ctx->dev_idx = dev_idx;
15525ce3f32bSSamuel Ortiz 	ctx->se_idx = se_idx;
15535ce3f32bSSamuel Ortiz 
1554df49908fSMiaoqian Lin 	rc = nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
1555df49908fSMiaoqian Lin 
1556df49908fSMiaoqian Lin put_dev:
1557df49908fSMiaoqian Lin 	nfc_put_device(dev);
1558df49908fSMiaoqian Lin 	return rc;
15595ce3f32bSSamuel Ortiz }
15605ce3f32bSSamuel Ortiz 
nfc_genl_vendor_cmd(struct sk_buff * skb,struct genl_info * info)15619e58095fSSamuel Ortiz static int nfc_genl_vendor_cmd(struct sk_buff *skb,
15629e58095fSSamuel Ortiz 			       struct genl_info *info)
15639e58095fSSamuel Ortiz {
15649e58095fSSamuel Ortiz 	struct nfc_dev *dev;
156515944ad2SKrzysztof Kozlowski 	const struct nfc_vendor_cmd *cmd;
15669e58095fSSamuel Ortiz 	u32 dev_idx, vid, subcmd;
15679e58095fSSamuel Ortiz 	u8 *data;
15689e58095fSSamuel Ortiz 	size_t data_len;
156929e76924SChristophe Ricard 	int i, err;
15709e58095fSSamuel Ortiz 
15719e58095fSSamuel Ortiz 	if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
15729e58095fSSamuel Ortiz 	    !info->attrs[NFC_ATTR_VENDOR_ID] ||
15739e58095fSSamuel Ortiz 	    !info->attrs[NFC_ATTR_VENDOR_SUBCMD])
15749e58095fSSamuel Ortiz 		return -EINVAL;
15759e58095fSSamuel Ortiz 
15769e58095fSSamuel Ortiz 	dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
15779e58095fSSamuel Ortiz 	vid = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_ID]);
15789e58095fSSamuel Ortiz 	subcmd = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_SUBCMD]);
15799e58095fSSamuel Ortiz 
15809e58095fSSamuel Ortiz 	dev = nfc_get_device(dev_idx);
1581df49908fSMiaoqian Lin 	if (!dev)
15829e58095fSSamuel Ortiz 		return -ENODEV;
15839e58095fSSamuel Ortiz 
1584df49908fSMiaoqian Lin 	if (!dev->vendor_cmds || !dev->n_vendor_cmds) {
1585df49908fSMiaoqian Lin 		err = -ENODEV;
1586df49908fSMiaoqian Lin 		goto put_dev;
1587df49908fSMiaoqian Lin 	}
1588df49908fSMiaoqian Lin 
1589fe202fe9SChristophe Ricard 	if (info->attrs[NFC_ATTR_VENDOR_DATA]) {
15909e58095fSSamuel Ortiz 		data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]);
15919e58095fSSamuel Ortiz 		data_len = nla_len(info->attrs[NFC_ATTR_VENDOR_DATA]);
1592df49908fSMiaoqian Lin 		if (data_len == 0) {
1593df49908fSMiaoqian Lin 			err = -EINVAL;
1594df49908fSMiaoqian Lin 			goto put_dev;
1595df49908fSMiaoqian Lin 		}
15969e58095fSSamuel Ortiz 	} else {
1597adca3c38SChristophe Ricard 		data = NULL;
15989e58095fSSamuel Ortiz 		data_len = 0;
15999e58095fSSamuel Ortiz 	}
16009e58095fSSamuel Ortiz 
16019e58095fSSamuel Ortiz 	for (i = 0; i < dev->n_vendor_cmds; i++) {
16029e58095fSSamuel Ortiz 		cmd = &dev->vendor_cmds[i];
16039e58095fSSamuel Ortiz 
16049e58095fSSamuel Ortiz 		if (cmd->vendor_id != vid || cmd->subcmd != subcmd)
16059e58095fSSamuel Ortiz 			continue;
16069e58095fSSamuel Ortiz 
160729e76924SChristophe Ricard 		dev->cur_cmd_info = info;
160829e76924SChristophe Ricard 		err = cmd->doit(dev, data, data_len);
160929e76924SChristophe Ricard 		dev->cur_cmd_info = NULL;
1610df49908fSMiaoqian Lin 		goto put_dev;
16119e58095fSSamuel Ortiz 	}
16129e58095fSSamuel Ortiz 
1613df49908fSMiaoqian Lin 	err = -EOPNOTSUPP;
1614df49908fSMiaoqian Lin 
1615df49908fSMiaoqian Lin put_dev:
1616df49908fSMiaoqian Lin 	nfc_put_device(dev);
1617df49908fSMiaoqian Lin 	return err;
16189e58095fSSamuel Ortiz }
16199e58095fSSamuel Ortiz 
162029e76924SChristophe Ricard /* message building helper */
nfc_hdr_put(struct sk_buff * skb,u32 portid,u32 seq,int flags,u8 cmd)162129e76924SChristophe Ricard static inline void *nfc_hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
162229e76924SChristophe Ricard 				int flags, u8 cmd)
162329e76924SChristophe Ricard {
162429e76924SChristophe Ricard 	/* since there is no private header just add the generic one */
162529e76924SChristophe Ricard 	return genlmsg_put(skb, portid, seq, &nfc_genl_family, flags, cmd);
162629e76924SChristophe Ricard }
162729e76924SChristophe Ricard 
162829e76924SChristophe Ricard static struct sk_buff *
__nfc_alloc_vendor_cmd_skb(struct nfc_dev * dev,int approxlen,u32 portid,u32 seq,enum nfc_attrs attr,u32 oui,u32 subcmd,gfp_t gfp)162929e76924SChristophe Ricard __nfc_alloc_vendor_cmd_skb(struct nfc_dev *dev, int approxlen,
163029e76924SChristophe Ricard 			   u32 portid, u32 seq,
163129e76924SChristophe Ricard 			   enum nfc_attrs attr,
163229e76924SChristophe Ricard 			   u32 oui, u32 subcmd, gfp_t gfp)
163329e76924SChristophe Ricard {
163429e76924SChristophe Ricard 	struct sk_buff *skb;
163529e76924SChristophe Ricard 	void *hdr;
163629e76924SChristophe Ricard 
163729e76924SChristophe Ricard 	skb = nlmsg_new(approxlen + 100, gfp);
163829e76924SChristophe Ricard 	if (!skb)
163929e76924SChristophe Ricard 		return NULL;
164029e76924SChristophe Ricard 
164129e76924SChristophe Ricard 	hdr = nfc_hdr_put(skb, portid, seq, 0, NFC_CMD_VENDOR);
164229e76924SChristophe Ricard 	if (!hdr) {
164329e76924SChristophe Ricard 		kfree_skb(skb);
164429e76924SChristophe Ricard 		return NULL;
164529e76924SChristophe Ricard 	}
164629e76924SChristophe Ricard 
164729e76924SChristophe Ricard 	if (nla_put_u32(skb, NFC_ATTR_DEVICE_INDEX, dev->idx))
164829e76924SChristophe Ricard 		goto nla_put_failure;
164929e76924SChristophe Ricard 	if (nla_put_u32(skb, NFC_ATTR_VENDOR_ID, oui))
165029e76924SChristophe Ricard 		goto nla_put_failure;
165129e76924SChristophe Ricard 	if (nla_put_u32(skb, NFC_ATTR_VENDOR_SUBCMD, subcmd))
165229e76924SChristophe Ricard 		goto nla_put_failure;
165329e76924SChristophe Ricard 
165429e76924SChristophe Ricard 	((void **)skb->cb)[0] = dev;
165529e76924SChristophe Ricard 	((void **)skb->cb)[1] = hdr;
165629e76924SChristophe Ricard 
165729e76924SChristophe Ricard 	return skb;
165829e76924SChristophe Ricard 
165929e76924SChristophe Ricard nla_put_failure:
166029e76924SChristophe Ricard 	kfree_skb(skb);
166129e76924SChristophe Ricard 	return NULL;
166229e76924SChristophe Ricard }
166329e76924SChristophe Ricard 
__nfc_alloc_vendor_cmd_reply_skb(struct nfc_dev * dev,enum nfc_attrs attr,u32 oui,u32 subcmd,int approxlen)166429e76924SChristophe Ricard struct sk_buff *__nfc_alloc_vendor_cmd_reply_skb(struct nfc_dev *dev,
166529e76924SChristophe Ricard 						 enum nfc_attrs attr,
166629e76924SChristophe Ricard 						 u32 oui, u32 subcmd,
166729e76924SChristophe Ricard 						 int approxlen)
166829e76924SChristophe Ricard {
166929e76924SChristophe Ricard 	if (WARN_ON(!dev->cur_cmd_info))
167029e76924SChristophe Ricard 		return NULL;
167129e76924SChristophe Ricard 
167229e76924SChristophe Ricard 	return __nfc_alloc_vendor_cmd_skb(dev, approxlen,
167329e76924SChristophe Ricard 					  dev->cur_cmd_info->snd_portid,
167429e76924SChristophe Ricard 					  dev->cur_cmd_info->snd_seq, attr,
167529e76924SChristophe Ricard 					  oui, subcmd, GFP_KERNEL);
167629e76924SChristophe Ricard }
167729e76924SChristophe Ricard EXPORT_SYMBOL(__nfc_alloc_vendor_cmd_reply_skb);
167829e76924SChristophe Ricard 
nfc_vendor_cmd_reply(struct sk_buff * skb)167929e76924SChristophe Ricard int nfc_vendor_cmd_reply(struct sk_buff *skb)
168029e76924SChristophe Ricard {
168129e76924SChristophe Ricard 	struct nfc_dev *dev = ((void **)skb->cb)[0];
168229e76924SChristophe Ricard 	void *hdr = ((void **)skb->cb)[1];
168329e76924SChristophe Ricard 
168429e76924SChristophe Ricard 	/* clear CB data for netlink core to own from now on */
168529e76924SChristophe Ricard 	memset(skb->cb, 0, sizeof(skb->cb));
168629e76924SChristophe Ricard 
168729e76924SChristophe Ricard 	if (WARN_ON(!dev->cur_cmd_info)) {
168829e76924SChristophe Ricard 		kfree_skb(skb);
168929e76924SChristophe Ricard 		return -EINVAL;
169029e76924SChristophe Ricard 	}
169129e76924SChristophe Ricard 
169229e76924SChristophe Ricard 	genlmsg_end(skb, hdr);
169329e76924SChristophe Ricard 	return genlmsg_reply(skb, dev->cur_cmd_info);
169429e76924SChristophe Ricard }
169529e76924SChristophe Ricard EXPORT_SYMBOL(nfc_vendor_cmd_reply);
169629e76924SChristophe Ricard 
16974534de83SJohannes Berg static const struct genl_ops nfc_genl_ops[] = {
16984d12b8b1SLauro Ramos Venancio 	{
16994d12b8b1SLauro Ramos Venancio 		.cmd = NFC_CMD_GET_DEVICE,
1700ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17014d12b8b1SLauro Ramos Venancio 		.doit = nfc_genl_get_device,
17024d12b8b1SLauro Ramos Venancio 		.dumpit = nfc_genl_dump_devices,
17034d12b8b1SLauro Ramos Venancio 		.done = nfc_genl_dump_devices_done,
17044d12b8b1SLauro Ramos Venancio 	},
17054d12b8b1SLauro Ramos Venancio 	{
17068b3fe7b5SIlan Elias 		.cmd = NFC_CMD_DEV_UP,
1707ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17088b3fe7b5SIlan Elias 		.doit = nfc_genl_dev_up,
1709aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17108b3fe7b5SIlan Elias 	},
17118b3fe7b5SIlan Elias 	{
17128b3fe7b5SIlan Elias 		.cmd = NFC_CMD_DEV_DOWN,
1713ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17148b3fe7b5SIlan Elias 		.doit = nfc_genl_dev_down,
1715aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17168b3fe7b5SIlan Elias 	},
17178b3fe7b5SIlan Elias 	{
17184d12b8b1SLauro Ramos Venancio 		.cmd = NFC_CMD_START_POLL,
1719ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17204d12b8b1SLauro Ramos Venancio 		.doit = nfc_genl_start_poll,
1721aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17224d12b8b1SLauro Ramos Venancio 	},
17234d12b8b1SLauro Ramos Venancio 	{
17244d12b8b1SLauro Ramos Venancio 		.cmd = NFC_CMD_STOP_POLL,
1725ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17264d12b8b1SLauro Ramos Venancio 		.doit = nfc_genl_stop_poll,
1727aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17284d12b8b1SLauro Ramos Venancio 	},
17294d12b8b1SLauro Ramos Venancio 	{
17301ed28f61SSamuel Ortiz 		.cmd = NFC_CMD_DEP_LINK_UP,
1731ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17321ed28f61SSamuel Ortiz 		.doit = nfc_genl_dep_link_up,
1733aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17341ed28f61SSamuel Ortiz 	},
17351ed28f61SSamuel Ortiz 	{
17361ed28f61SSamuel Ortiz 		.cmd = NFC_CMD_DEP_LINK_DOWN,
1737ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17381ed28f61SSamuel Ortiz 		.doit = nfc_genl_dep_link_down,
1739aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17401ed28f61SSamuel Ortiz 	},
17411ed28f61SSamuel Ortiz 	{
17424d12b8b1SLauro Ramos Venancio 		.cmd = NFC_CMD_GET_TARGET,
17434495af31SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
17444495af31SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
17454d12b8b1SLauro Ramos Venancio 		.dumpit = nfc_genl_dump_targets,
17464d12b8b1SLauro Ramos Venancio 		.done = nfc_genl_dump_targets_done,
17474d12b8b1SLauro Ramos Venancio 	},
174852feb444SThierry Escande 	{
174952feb444SThierry Escande 		.cmd = NFC_CMD_LLC_GET_PARAMS,
1750ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
175152feb444SThierry Escande 		.doit = nfc_genl_llc_get_params,
175252feb444SThierry Escande 	},
175352feb444SThierry Escande 	{
175452feb444SThierry Escande 		.cmd = NFC_CMD_LLC_SET_PARAMS,
1755ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
175652feb444SThierry Escande 		.doit = nfc_genl_llc_set_params,
1757aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
175852feb444SThierry Escande 	},
1759d9b8d8e1SThierry Escande 	{
1760d9b8d8e1SThierry Escande 		.cmd = NFC_CMD_LLC_SDREQ,
1761ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1762d9b8d8e1SThierry Escande 		.doit = nfc_genl_llc_sdreq,
1763aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
1764d9b8d8e1SThierry Escande 	},
17659674da87SEric Lapuyade 	{
17669ea7187cSSamuel Ortiz 		.cmd = NFC_CMD_FW_DOWNLOAD,
1767ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17689ea7187cSSamuel Ortiz 		.doit = nfc_genl_fw_download,
1769aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17709674da87SEric Lapuyade 	},
1771be085653SSamuel Ortiz 	{
1772be085653SSamuel Ortiz 		.cmd = NFC_CMD_ENABLE_SE,
1773ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1774be085653SSamuel Ortiz 		.doit = nfc_genl_enable_se,
1775aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
1776be085653SSamuel Ortiz 	},
1777be085653SSamuel Ortiz 	{
1778be085653SSamuel Ortiz 		.cmd = NFC_CMD_DISABLE_SE,
1779ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1780be085653SSamuel Ortiz 		.doit = nfc_genl_disable_se,
1781aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
1782be085653SSamuel Ortiz 	},
1783ac22ac46SSamuel Ortiz 	{
1784ac22ac46SSamuel Ortiz 		.cmd = NFC_CMD_GET_SE,
1785ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1786ac22ac46SSamuel Ortiz 		.dumpit = nfc_genl_dump_ses,
1787ac22ac46SSamuel Ortiz 		.done = nfc_genl_dump_ses_done,
1788ac22ac46SSamuel Ortiz 	},
17895ce3f32bSSamuel Ortiz 	{
17905ce3f32bSSamuel Ortiz 		.cmd = NFC_CMD_SE_IO,
1791ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17925ce3f32bSSamuel Ortiz 		.doit = nfc_genl_se_io,
1793aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
17945ce3f32bSSamuel Ortiz 	},
17953682f49fSChristophe Ricard 	{
17963682f49fSChristophe Ricard 		.cmd = NFC_CMD_ACTIVATE_TARGET,
1797ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
17983682f49fSChristophe Ricard 		.doit = nfc_genl_activate_target,
1799aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
18003682f49fSChristophe Ricard 	},
18019e58095fSSamuel Ortiz 	{
18029e58095fSSamuel Ortiz 		.cmd = NFC_CMD_VENDOR,
1803ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
18049e58095fSSamuel Ortiz 		.doit = nfc_genl_vendor_cmd,
1805aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
18069e58095fSSamuel Ortiz 	},
18074d63adfeSMark Greer 	{
18084d63adfeSMark Greer 		.cmd = NFC_CMD_DEACTIVATE_TARGET,
1809ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
18104d63adfeSMark Greer 		.doit = nfc_genl_deactivate_target,
1811aedddb4eSLin Ma 		.flags = GENL_ADMIN_PERM,
18124d63adfeSMark Greer 	},
18134d12b8b1SLauro Ramos Venancio };
18144d12b8b1SLauro Ramos Venancio 
181556989f6dSJohannes Berg static struct genl_family nfc_genl_family __ro_after_init = {
1816489111e5SJohannes Berg 	.hdrsize = 0,
1817489111e5SJohannes Berg 	.name = NFC_GENL_NAME,
1818489111e5SJohannes Berg 	.version = NFC_GENL_VERSION,
1819489111e5SJohannes Berg 	.maxattr = NFC_ATTR_MAX,
18203b0f31f2SJohannes Berg 	.policy = nfc_genl_policy,
1821489111e5SJohannes Berg 	.module = THIS_MODULE,
1822489111e5SJohannes Berg 	.ops = nfc_genl_ops,
1823489111e5SJohannes Berg 	.n_ops = ARRAY_SIZE(nfc_genl_ops),
18249c5d03d3SJakub Kicinski 	.resv_start_op = NFC_CMD_DEACTIVATE_TARGET + 1,
1825489111e5SJohannes Berg 	.mcgrps = nfc_genl_mcgrps,
1826489111e5SJohannes Berg 	.n_mcgrps = ARRAY_SIZE(nfc_genl_mcgrps),
1827489111e5SJohannes Berg };
1828489111e5SJohannes Berg 
18293c0cc8aaSSzymon Janc 
18303c0cc8aaSSzymon Janc struct urelease_work {
18313c0cc8aaSSzymon Janc 	struct	work_struct w;
183265bc4f93SRichard Weinberger 	u32	portid;
18333c0cc8aaSSzymon Janc };
18343c0cc8aaSSzymon Janc 
nfc_urelease_event_work(struct work_struct * work)18353c0cc8aaSSzymon Janc static void nfc_urelease_event_work(struct work_struct *work)
18363c0cc8aaSSzymon Janc {
18373c0cc8aaSSzymon Janc 	struct urelease_work *w = container_of(work, struct urelease_work, w);
18383c0cc8aaSSzymon Janc 	struct class_dev_iter iter;
18393c0cc8aaSSzymon Janc 	struct nfc_dev *dev;
18403c0cc8aaSSzymon Janc 
1841c487606fSJohn W. Linville 	pr_debug("portid %d\n", w->portid);
18423c0cc8aaSSzymon Janc 
18433c0cc8aaSSzymon Janc 	mutex_lock(&nfc_devlist_mutex);
18443c0cc8aaSSzymon Janc 
18453c0cc8aaSSzymon Janc 	nfc_device_iter_init(&iter);
18463c0cc8aaSSzymon Janc 	dev = nfc_device_iter_next(&iter);
18473c0cc8aaSSzymon Janc 
18483c0cc8aaSSzymon Janc 	while (dev) {
18493c0cc8aaSSzymon Janc 		mutex_lock(&dev->genl_data.genl_data_mutex);
18503c0cc8aaSSzymon Janc 
1851c487606fSJohn W. Linville 		if (dev->genl_data.poll_req_portid == w->portid) {
18523c0cc8aaSSzymon Janc 			nfc_stop_poll(dev);
1853c487606fSJohn W. Linville 			dev->genl_data.poll_req_portid = 0;
18543c0cc8aaSSzymon Janc 		}
18553c0cc8aaSSzymon Janc 
18563c0cc8aaSSzymon Janc 		mutex_unlock(&dev->genl_data.genl_data_mutex);
18573c0cc8aaSSzymon Janc 
18583c0cc8aaSSzymon Janc 		dev = nfc_device_iter_next(&iter);
18593c0cc8aaSSzymon Janc 	}
18603c0cc8aaSSzymon Janc 
18613c0cc8aaSSzymon Janc 	nfc_device_iter_exit(&iter);
18623c0cc8aaSSzymon Janc 
18633c0cc8aaSSzymon Janc 	mutex_unlock(&nfc_devlist_mutex);
18643c0cc8aaSSzymon Janc 
18653c0cc8aaSSzymon Janc 	kfree(w);
18663c0cc8aaSSzymon Janc }
18673c0cc8aaSSzymon Janc 
nfc_genl_rcv_nl_event(struct notifier_block * this,unsigned long event,void * ptr)18684d12b8b1SLauro Ramos Venancio static int nfc_genl_rcv_nl_event(struct notifier_block *this,
18694d12b8b1SLauro Ramos Venancio 				 unsigned long event, void *ptr)
18704d12b8b1SLauro Ramos Venancio {
18714d12b8b1SLauro Ramos Venancio 	struct netlink_notify *n = ptr;
18723c0cc8aaSSzymon Janc 	struct urelease_work *w;
18734d12b8b1SLauro Ramos Venancio 
18744d12b8b1SLauro Ramos Venancio 	if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
18754d12b8b1SLauro Ramos Venancio 		goto out;
18764d12b8b1SLauro Ramos Venancio 
187715e47304SEric W. Biederman 	pr_debug("NETLINK_URELEASE event from id %d\n", n->portid);
18784d12b8b1SLauro Ramos Venancio 
18793c0cc8aaSSzymon Janc 	w = kmalloc(sizeof(*w), GFP_ATOMIC);
18803c0cc8aaSSzymon Janc 	if (w) {
188132d91b4aSGeliang Tang 		INIT_WORK(&w->w, nfc_urelease_event_work);
1882c487606fSJohn W. Linville 		w->portid = n->portid;
188332d91b4aSGeliang Tang 		schedule_work(&w->w);
18844d12b8b1SLauro Ramos Venancio 	}
18854d12b8b1SLauro Ramos Venancio 
18864d12b8b1SLauro Ramos Venancio out:
18874d12b8b1SLauro Ramos Venancio 	return NOTIFY_DONE;
18884d12b8b1SLauro Ramos Venancio }
18894d12b8b1SLauro Ramos Venancio 
nfc_genl_data_init(struct nfc_genl_data * genl_data)18904d12b8b1SLauro Ramos Venancio void nfc_genl_data_init(struct nfc_genl_data *genl_data)
18914d12b8b1SLauro Ramos Venancio {
189215e47304SEric W. Biederman 	genl_data->poll_req_portid = 0;
18934d12b8b1SLauro Ramos Venancio 	mutex_init(&genl_data->genl_data_mutex);
18944d12b8b1SLauro Ramos Venancio }
18954d12b8b1SLauro Ramos Venancio 
nfc_genl_data_exit(struct nfc_genl_data * genl_data)18964d12b8b1SLauro Ramos Venancio void nfc_genl_data_exit(struct nfc_genl_data *genl_data)
18974d12b8b1SLauro Ramos Venancio {
18984d12b8b1SLauro Ramos Venancio 	mutex_destroy(&genl_data->genl_data_mutex);
18994d12b8b1SLauro Ramos Venancio }
19004d12b8b1SLauro Ramos Venancio 
19014d12b8b1SLauro Ramos Venancio static struct notifier_block nl_notifier = {
19024d12b8b1SLauro Ramos Venancio 	.notifier_call  = nfc_genl_rcv_nl_event,
19034d12b8b1SLauro Ramos Venancio };
19044d12b8b1SLauro Ramos Venancio 
19054d12b8b1SLauro Ramos Venancio /**
19064d12b8b1SLauro Ramos Venancio  * nfc_genl_init() - Initialize netlink interface
19074d12b8b1SLauro Ramos Venancio  *
19084d12b8b1SLauro Ramos Venancio  * This initialization function registers the nfc netlink family.
19094d12b8b1SLauro Ramos Venancio  */
nfc_genl_init(void)19104d12b8b1SLauro Ramos Venancio int __init nfc_genl_init(void)
19114d12b8b1SLauro Ramos Venancio {
19124d12b8b1SLauro Ramos Venancio 	int rc;
19134d12b8b1SLauro Ramos Venancio 
1914489111e5SJohannes Berg 	rc = genl_register_family(&nfc_genl_family);
19154d12b8b1SLauro Ramos Venancio 	if (rc)
19164d12b8b1SLauro Ramos Venancio 		return rc;
19174d12b8b1SLauro Ramos Venancio 
19184d12b8b1SLauro Ramos Venancio 	netlink_register_notifier(&nl_notifier);
19194d12b8b1SLauro Ramos Venancio 
19202a94fe48SJohannes Berg 	return 0;
19214d12b8b1SLauro Ramos Venancio }
19224d12b8b1SLauro Ramos Venancio 
19234d12b8b1SLauro Ramos Venancio /**
19244d12b8b1SLauro Ramos Venancio  * nfc_genl_exit() - Deinitialize netlink interface
19254d12b8b1SLauro Ramos Venancio  *
19264d12b8b1SLauro Ramos Venancio  * This exit function unregisters the nfc netlink family.
19274d12b8b1SLauro Ramos Venancio  */
nfc_genl_exit(void)19284d12b8b1SLauro Ramos Venancio void nfc_genl_exit(void)
19294d12b8b1SLauro Ramos Venancio {
19304d12b8b1SLauro Ramos Venancio 	netlink_unregister_notifier(&nl_notifier);
19314d12b8b1SLauro Ramos Venancio 	genl_unregister_family(&nfc_genl_family);
19324d12b8b1SLauro Ramos Venancio }
1933