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