162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Instituto Nokia de Tecnologia 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Lauro Ramos Venancio <lauro.venancio@openbossa.org> 762306a36Sopenharmony_ci * Aloisio Almeida Jr <aloisio.almeida@openbossa.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Vendor commands implementation based on net/wireless/nl80211.c 1062306a36Sopenharmony_ci * which is: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 1362306a36Sopenharmony_ci * Copyright 2013-2014 Intel Mobile Communications GmbH 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <net/genetlink.h> 1962306a36Sopenharmony_ci#include <linux/nfc.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "nfc.h" 2362306a36Sopenharmony_ci#include "llcp.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic const struct genl_multicast_group nfc_genl_mcgrps[] = { 2662306a36Sopenharmony_ci { .name = NFC_GENL_MCAST_EVENT_NAME, }, 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct genl_family nfc_genl_family; 3062306a36Sopenharmony_cistatic const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { 3162306a36Sopenharmony_ci [NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 }, 3262306a36Sopenharmony_ci [NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING, 3362306a36Sopenharmony_ci .len = NFC_DEVICE_NAME_MAXSIZE }, 3462306a36Sopenharmony_ci [NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 }, 3562306a36Sopenharmony_ci [NFC_ATTR_TARGET_INDEX] = { .type = NLA_U32 }, 3662306a36Sopenharmony_ci [NFC_ATTR_COMM_MODE] = { .type = NLA_U8 }, 3762306a36Sopenharmony_ci [NFC_ATTR_RF_MODE] = { .type = NLA_U8 }, 3862306a36Sopenharmony_ci [NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 }, 3962306a36Sopenharmony_ci [NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 }, 4062306a36Sopenharmony_ci [NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 }, 4162306a36Sopenharmony_ci [NFC_ATTR_LLC_PARAM_LTO] = { .type = NLA_U8 }, 4262306a36Sopenharmony_ci [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 }, 4362306a36Sopenharmony_ci [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 }, 4462306a36Sopenharmony_ci [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED }, 4562306a36Sopenharmony_ci [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING, 4662306a36Sopenharmony_ci .len = NFC_FIRMWARE_NAME_MAXSIZE }, 4762306a36Sopenharmony_ci [NFC_ATTR_SE_INDEX] = { .type = NLA_U32 }, 4862306a36Sopenharmony_ci [NFC_ATTR_SE_APDU] = { .type = NLA_BINARY }, 4962306a36Sopenharmony_ci [NFC_ATTR_VENDOR_ID] = { .type = NLA_U32 }, 5062306a36Sopenharmony_ci [NFC_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, 5162306a36Sopenharmony_ci [NFC_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { 5662306a36Sopenharmony_ci [NFC_SDP_ATTR_URI] = { .type = NLA_STRING, 5762306a36Sopenharmony_ci .len = U8_MAX - 4 }, 5862306a36Sopenharmony_ci [NFC_SDP_ATTR_SAP] = { .type = NLA_U8 }, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, 6262306a36Sopenharmony_ci struct netlink_callback *cb, int flags) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci void *hdr; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 6762306a36Sopenharmony_ci &nfc_genl_family, flags, NFC_CMD_GET_TARGET); 6862306a36Sopenharmony_ci if (!hdr) 6962306a36Sopenharmony_ci return -EMSGSIZE; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci genl_dump_check_consistent(cb, hdr); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target->idx) || 7462306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_PROTOCOLS, target->supported_protocols) || 7562306a36Sopenharmony_ci nla_put_u16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res) || 7662306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res)) 7762306a36Sopenharmony_ci goto nla_put_failure; 7862306a36Sopenharmony_ci if (target->nfcid1_len > 0 && 7962306a36Sopenharmony_ci nla_put(msg, NFC_ATTR_TARGET_NFCID1, target->nfcid1_len, 8062306a36Sopenharmony_ci target->nfcid1)) 8162306a36Sopenharmony_ci goto nla_put_failure; 8262306a36Sopenharmony_ci if (target->sensb_res_len > 0 && 8362306a36Sopenharmony_ci nla_put(msg, NFC_ATTR_TARGET_SENSB_RES, target->sensb_res_len, 8462306a36Sopenharmony_ci target->sensb_res)) 8562306a36Sopenharmony_ci goto nla_put_failure; 8662306a36Sopenharmony_ci if (target->sensf_res_len > 0 && 8762306a36Sopenharmony_ci nla_put(msg, NFC_ATTR_TARGET_SENSF_RES, target->sensf_res_len, 8862306a36Sopenharmony_ci target->sensf_res)) 8962306a36Sopenharmony_ci goto nla_put_failure; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (target->is_iso15693) { 9262306a36Sopenharmony_ci if (nla_put_u8(msg, NFC_ATTR_TARGET_ISO15693_DSFID, 9362306a36Sopenharmony_ci target->iso15693_dsfid) || 9462306a36Sopenharmony_ci nla_put(msg, NFC_ATTR_TARGET_ISO15693_UID, 9562306a36Sopenharmony_ci sizeof(target->iso15693_uid), target->iso15693_uid)) 9662306a36Sopenharmony_ci goto nla_put_failure; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci genlmsg_end(msg, hdr); 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cinla_put_failure: 10362306a36Sopenharmony_ci genlmsg_cancel(msg, hdr); 10462306a36Sopenharmony_ci return -EMSGSIZE; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci const struct genl_dumpit_info *info = genl_dumpit_info(cb); 11062306a36Sopenharmony_ci struct nfc_dev *dev; 11162306a36Sopenharmony_ci u32 idx; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!info->info.attrs[NFC_ATTR_DEVICE_INDEX]) 11462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci idx = nla_get_u32(info->info.attrs[NFC_ATTR_DEVICE_INDEX]); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci dev = nfc_get_device(idx); 11962306a36Sopenharmony_ci if (!dev) 12062306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return dev; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int nfc_genl_dump_targets(struct sk_buff *skb, 12662306a36Sopenharmony_ci struct netlink_callback *cb) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci int i = cb->args[0]; 12962306a36Sopenharmony_ci struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; 13062306a36Sopenharmony_ci int rc; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!dev) { 13362306a36Sopenharmony_ci dev = __get_device_from_cb(cb); 13462306a36Sopenharmony_ci if (IS_ERR(dev)) 13562306a36Sopenharmony_ci return PTR_ERR(dev); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci cb->args[1] = (long) dev; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci device_lock(&dev->dev); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci cb->seq = dev->targets_generation; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci while (i < dev->n_targets) { 14562306a36Sopenharmony_ci rc = nfc_genl_send_target(skb, &dev->targets[i], cb, 14662306a36Sopenharmony_ci NLM_F_MULTI); 14762306a36Sopenharmony_ci if (rc < 0) 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci i++; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci device_unlock(&dev->dev); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci cb->args[0] = i; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return skb->len; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int nfc_genl_dump_targets_done(struct netlink_callback *cb) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (dev) 16562306a36Sopenharmony_ci nfc_put_device(dev); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciint nfc_genl_targets_found(struct nfc_dev *dev) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct sk_buff *msg; 17362306a36Sopenharmony_ci void *hdr; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci dev->genl_data.poll_req_portid = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 17862306a36Sopenharmony_ci if (!msg) 17962306a36Sopenharmony_ci return -ENOMEM; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 18262306a36Sopenharmony_ci NFC_EVENT_TARGETS_FOUND); 18362306a36Sopenharmony_ci if (!hdr) 18462306a36Sopenharmony_ci goto free_msg; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 18762306a36Sopenharmony_ci goto nla_put_failure; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci genlmsg_end(msg, hdr); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cinla_put_failure: 19462306a36Sopenharmony_cifree_msg: 19562306a36Sopenharmony_ci nlmsg_free(msg); 19662306a36Sopenharmony_ci return -EMSGSIZE; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct sk_buff *msg; 20262306a36Sopenharmony_ci void *hdr; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 20562306a36Sopenharmony_ci if (!msg) 20662306a36Sopenharmony_ci return -ENOMEM; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 20962306a36Sopenharmony_ci NFC_EVENT_TARGET_LOST); 21062306a36Sopenharmony_ci if (!hdr) 21162306a36Sopenharmony_ci goto free_msg; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || 21462306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target_idx)) 21562306a36Sopenharmony_ci goto nla_put_failure; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci genlmsg_end(msg, hdr); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cinla_put_failure: 22462306a36Sopenharmony_cifree_msg: 22562306a36Sopenharmony_ci nlmsg_free(msg); 22662306a36Sopenharmony_ci return -EMSGSIZE; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciint nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct sk_buff *msg; 23262306a36Sopenharmony_ci void *hdr; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 23562306a36Sopenharmony_ci if (!msg) 23662306a36Sopenharmony_ci return -ENOMEM; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 23962306a36Sopenharmony_ci NFC_EVENT_TM_ACTIVATED); 24062306a36Sopenharmony_ci if (!hdr) 24162306a36Sopenharmony_ci goto free_msg; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 24462306a36Sopenharmony_ci goto nla_put_failure; 24562306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_TM_PROTOCOLS, protocol)) 24662306a36Sopenharmony_ci goto nla_put_failure; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci genlmsg_end(msg, hdr); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cinla_put_failure: 25562306a36Sopenharmony_cifree_msg: 25662306a36Sopenharmony_ci nlmsg_free(msg); 25762306a36Sopenharmony_ci return -EMSGSIZE; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciint nfc_genl_tm_deactivated(struct nfc_dev *dev) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct sk_buff *msg; 26362306a36Sopenharmony_ci void *hdr; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 26662306a36Sopenharmony_ci if (!msg) 26762306a36Sopenharmony_ci return -ENOMEM; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 27062306a36Sopenharmony_ci NFC_EVENT_TM_DEACTIVATED); 27162306a36Sopenharmony_ci if (!hdr) 27262306a36Sopenharmony_ci goto free_msg; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 27562306a36Sopenharmony_ci goto nla_put_failure; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci genlmsg_end(msg, hdr); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cinla_put_failure: 28462306a36Sopenharmony_cifree_msg: 28562306a36Sopenharmony_ci nlmsg_free(msg); 28662306a36Sopenharmony_ci return -EMSGSIZE; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int nfc_genl_setup_device_added(struct nfc_dev *dev, struct sk_buff *msg) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || 29262306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || 29362306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) || 29462306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) || 29562306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode)) 29662306a36Sopenharmony_ci return -1; 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciint nfc_genl_device_added(struct nfc_dev *dev) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct sk_buff *msg; 30362306a36Sopenharmony_ci void *hdr; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 30662306a36Sopenharmony_ci if (!msg) 30762306a36Sopenharmony_ci return -ENOMEM; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 31062306a36Sopenharmony_ci NFC_EVENT_DEVICE_ADDED); 31162306a36Sopenharmony_ci if (!hdr) 31262306a36Sopenharmony_ci goto free_msg; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (nfc_genl_setup_device_added(dev, msg)) 31562306a36Sopenharmony_ci goto nla_put_failure; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci genlmsg_end(msg, hdr); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cinla_put_failure: 32462306a36Sopenharmony_cifree_msg: 32562306a36Sopenharmony_ci nlmsg_free(msg); 32662306a36Sopenharmony_ci return -EMSGSIZE; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciint nfc_genl_device_removed(struct nfc_dev *dev) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct sk_buff *msg; 33262306a36Sopenharmony_ci void *hdr; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 33562306a36Sopenharmony_ci if (!msg) 33662306a36Sopenharmony_ci return -ENOMEM; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 33962306a36Sopenharmony_ci NFC_EVENT_DEVICE_REMOVED); 34062306a36Sopenharmony_ci if (!hdr) 34162306a36Sopenharmony_ci goto free_msg; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 34462306a36Sopenharmony_ci goto nla_put_failure; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci genlmsg_end(msg, hdr); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cinla_put_failure: 35362306a36Sopenharmony_cifree_msg: 35462306a36Sopenharmony_ci nlmsg_free(msg); 35562306a36Sopenharmony_ci return -EMSGSIZE; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciint nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct sk_buff *msg; 36162306a36Sopenharmony_ci struct nlattr *sdp_attr, *uri_attr; 36262306a36Sopenharmony_ci struct nfc_llcp_sdp_tlv *sdres; 36362306a36Sopenharmony_ci struct hlist_node *n; 36462306a36Sopenharmony_ci void *hdr; 36562306a36Sopenharmony_ci int rc = -EMSGSIZE; 36662306a36Sopenharmony_ci int i; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 36962306a36Sopenharmony_ci if (!msg) 37062306a36Sopenharmony_ci return -ENOMEM; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 37362306a36Sopenharmony_ci NFC_EVENT_LLC_SDRES); 37462306a36Sopenharmony_ci if (!hdr) 37562306a36Sopenharmony_ci goto free_msg; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 37862306a36Sopenharmony_ci goto nla_put_failure; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci sdp_attr = nla_nest_start_noflag(msg, NFC_ATTR_LLC_SDP); 38162306a36Sopenharmony_ci if (sdp_attr == NULL) { 38262306a36Sopenharmony_ci rc = -ENOMEM; 38362306a36Sopenharmony_ci goto nla_put_failure; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci i = 1; 38762306a36Sopenharmony_ci hlist_for_each_entry_safe(sdres, n, sdres_list, node) { 38862306a36Sopenharmony_ci pr_debug("uri: %s, sap: %d\n", sdres->uri, sdres->sap); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci uri_attr = nla_nest_start_noflag(msg, i++); 39162306a36Sopenharmony_ci if (uri_attr == NULL) { 39262306a36Sopenharmony_ci rc = -ENOMEM; 39362306a36Sopenharmony_ci goto nla_put_failure; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (nla_put_u8(msg, NFC_SDP_ATTR_SAP, sdres->sap)) 39762306a36Sopenharmony_ci goto nla_put_failure; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (nla_put_string(msg, NFC_SDP_ATTR_URI, sdres->uri)) 40062306a36Sopenharmony_ci goto nla_put_failure; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci nla_nest_end(msg, uri_attr); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci hlist_del(&sdres->node); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci nfc_llcp_free_sdp_tlv(sdres); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci nla_nest_end(msg, sdp_attr); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci genlmsg_end(msg, hdr); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cinla_put_failure: 41662306a36Sopenharmony_cifree_msg: 41762306a36Sopenharmony_ci nlmsg_free(msg); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci nfc_llcp_free_sdp_tlv_list(sdres_list); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return rc; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciint nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct sk_buff *msg; 42762306a36Sopenharmony_ci void *hdr; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 43062306a36Sopenharmony_ci if (!msg) 43162306a36Sopenharmony_ci return -ENOMEM; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 43462306a36Sopenharmony_ci NFC_EVENT_SE_ADDED); 43562306a36Sopenharmony_ci if (!hdr) 43662306a36Sopenharmony_ci goto free_msg; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || 43962306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) || 44062306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_SE_TYPE, type)) 44162306a36Sopenharmony_ci goto nla_put_failure; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci genlmsg_end(msg, hdr); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cinla_put_failure: 45062306a36Sopenharmony_cifree_msg: 45162306a36Sopenharmony_ci nlmsg_free(msg); 45262306a36Sopenharmony_ci return -EMSGSIZE; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciint nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct sk_buff *msg; 45862306a36Sopenharmony_ci void *hdr; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 46162306a36Sopenharmony_ci if (!msg) 46262306a36Sopenharmony_ci return -ENOMEM; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 46562306a36Sopenharmony_ci NFC_EVENT_SE_REMOVED); 46662306a36Sopenharmony_ci if (!hdr) 46762306a36Sopenharmony_ci goto free_msg; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || 47062306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx)) 47162306a36Sopenharmony_ci goto nla_put_failure; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci genlmsg_end(msg, hdr); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cinla_put_failure: 48062306a36Sopenharmony_cifree_msg: 48162306a36Sopenharmony_ci nlmsg_free(msg); 48262306a36Sopenharmony_ci return -EMSGSIZE; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ciint nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx, 48662306a36Sopenharmony_ci struct nfc_evt_transaction *evt_transaction) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct nfc_se *se; 48962306a36Sopenharmony_ci struct sk_buff *msg; 49062306a36Sopenharmony_ci void *hdr; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 49362306a36Sopenharmony_ci if (!msg) 49462306a36Sopenharmony_ci return -ENOMEM; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 49762306a36Sopenharmony_ci NFC_EVENT_SE_TRANSACTION); 49862306a36Sopenharmony_ci if (!hdr) 49962306a36Sopenharmony_ci goto free_msg; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci se = nfc_find_se(dev, se_idx); 50262306a36Sopenharmony_ci if (!se) 50362306a36Sopenharmony_ci goto free_msg; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || 50662306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) || 50762306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type) || 50862306a36Sopenharmony_ci nla_put(msg, NFC_ATTR_SE_AID, evt_transaction->aid_len, 50962306a36Sopenharmony_ci evt_transaction->aid) || 51062306a36Sopenharmony_ci nla_put(msg, NFC_ATTR_SE_PARAMS, evt_transaction->params_len, 51162306a36Sopenharmony_ci evt_transaction->params)) 51262306a36Sopenharmony_ci goto nla_put_failure; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* evt_transaction is no more used */ 51562306a36Sopenharmony_ci devm_kfree(&dev->dev, evt_transaction); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci genlmsg_end(msg, hdr); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cinla_put_failure: 52462306a36Sopenharmony_cifree_msg: 52562306a36Sopenharmony_ci /* evt_transaction is no more used */ 52662306a36Sopenharmony_ci devm_kfree(&dev->dev, evt_transaction); 52762306a36Sopenharmony_ci nlmsg_free(msg); 52862306a36Sopenharmony_ci return -EMSGSIZE; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ciint nfc_genl_se_connectivity(struct nfc_dev *dev, u8 se_idx) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci const struct nfc_se *se; 53462306a36Sopenharmony_ci struct sk_buff *msg; 53562306a36Sopenharmony_ci void *hdr; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 53862306a36Sopenharmony_ci if (!msg) 53962306a36Sopenharmony_ci return -ENOMEM; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 54262306a36Sopenharmony_ci NFC_EVENT_SE_CONNECTIVITY); 54362306a36Sopenharmony_ci if (!hdr) 54462306a36Sopenharmony_ci goto free_msg; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci se = nfc_find_se(dev, se_idx); 54762306a36Sopenharmony_ci if (!se) 54862306a36Sopenharmony_ci goto free_msg; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || 55162306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) || 55262306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type)) 55362306a36Sopenharmony_ci goto nla_put_failure; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci genlmsg_end(msg, hdr); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cinla_put_failure: 56262306a36Sopenharmony_cifree_msg: 56362306a36Sopenharmony_ci nlmsg_free(msg); 56462306a36Sopenharmony_ci return -EMSGSIZE; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, 56862306a36Sopenharmony_ci u32 portid, u32 seq, 56962306a36Sopenharmony_ci struct netlink_callback *cb, 57062306a36Sopenharmony_ci int flags) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci void *hdr; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags, 57562306a36Sopenharmony_ci NFC_CMD_GET_DEVICE); 57662306a36Sopenharmony_ci if (!hdr) 57762306a36Sopenharmony_ci return -EMSGSIZE; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (cb) 58062306a36Sopenharmony_ci genl_dump_check_consistent(cb, hdr); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (nfc_genl_setup_device_added(dev, msg)) 58362306a36Sopenharmony_ci goto nla_put_failure; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci genlmsg_end(msg, hdr); 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cinla_put_failure: 58962306a36Sopenharmony_ci genlmsg_cancel(msg, hdr); 59062306a36Sopenharmony_ci return -EMSGSIZE; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic int nfc_genl_dump_devices(struct sk_buff *skb, 59462306a36Sopenharmony_ci struct netlink_callback *cb) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; 59762306a36Sopenharmony_ci struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; 59862306a36Sopenharmony_ci bool first_call = false; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (!iter) { 60162306a36Sopenharmony_ci first_call = true; 60262306a36Sopenharmony_ci iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL); 60362306a36Sopenharmony_ci if (!iter) 60462306a36Sopenharmony_ci return -ENOMEM; 60562306a36Sopenharmony_ci cb->args[0] = (long) iter; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci mutex_lock(&nfc_devlist_mutex); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci cb->seq = nfc_devlist_generation; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (first_call) { 61362306a36Sopenharmony_ci nfc_device_iter_init(iter); 61462306a36Sopenharmony_ci dev = nfc_device_iter_next(iter); 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci while (dev) { 61862306a36Sopenharmony_ci int rc; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).portid, 62162306a36Sopenharmony_ci cb->nlh->nlmsg_seq, cb, NLM_F_MULTI); 62262306a36Sopenharmony_ci if (rc < 0) 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci dev = nfc_device_iter_next(iter); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mutex_unlock(&nfc_devlist_mutex); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci cb->args[1] = (long) dev; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return skb->len; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int nfc_genl_dump_devices_done(struct netlink_callback *cb) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (iter) { 64062306a36Sopenharmony_ci nfc_device_iter_exit(iter); 64162306a36Sopenharmony_ci kfree(iter); 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ciint nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx, 64862306a36Sopenharmony_ci u8 comm_mode, u8 rf_mode) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct sk_buff *msg; 65162306a36Sopenharmony_ci void *hdr; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci pr_debug("DEP link is up\n"); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 65662306a36Sopenharmony_ci if (!msg) 65762306a36Sopenharmony_ci return -ENOMEM; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, NFC_CMD_DEP_LINK_UP); 66062306a36Sopenharmony_ci if (!hdr) 66162306a36Sopenharmony_ci goto free_msg; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 66462306a36Sopenharmony_ci goto nla_put_failure; 66562306a36Sopenharmony_ci if (rf_mode == NFC_RF_INITIATOR && 66662306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target_idx)) 66762306a36Sopenharmony_ci goto nla_put_failure; 66862306a36Sopenharmony_ci if (nla_put_u8(msg, NFC_ATTR_COMM_MODE, comm_mode) || 66962306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_RF_MODE, rf_mode)) 67062306a36Sopenharmony_ci goto nla_put_failure; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci genlmsg_end(msg, hdr); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci dev->dep_link_up = true; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return 0; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cinla_put_failure: 68162306a36Sopenharmony_cifree_msg: 68262306a36Sopenharmony_ci nlmsg_free(msg); 68362306a36Sopenharmony_ci return -EMSGSIZE; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ciint nfc_genl_dep_link_down_event(struct nfc_dev *dev) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci struct sk_buff *msg; 68962306a36Sopenharmony_ci void *hdr; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci pr_debug("DEP link is down\n"); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 69462306a36Sopenharmony_ci if (!msg) 69562306a36Sopenharmony_ci return -ENOMEM; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 69862306a36Sopenharmony_ci NFC_CMD_DEP_LINK_DOWN); 69962306a36Sopenharmony_ci if (!hdr) 70062306a36Sopenharmony_ci goto free_msg; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 70362306a36Sopenharmony_ci goto nla_put_failure; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci genlmsg_end(msg, hdr); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cinla_put_failure: 71262306a36Sopenharmony_cifree_msg: 71362306a36Sopenharmony_ci nlmsg_free(msg); 71462306a36Sopenharmony_ci return -EMSGSIZE; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct sk_buff *msg; 72062306a36Sopenharmony_ci struct nfc_dev *dev; 72162306a36Sopenharmony_ci u32 idx; 72262306a36Sopenharmony_ci int rc = -ENOBUFS; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci dev = nfc_get_device(idx); 73062306a36Sopenharmony_ci if (!dev) 73162306a36Sopenharmony_ci return -ENODEV; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 73462306a36Sopenharmony_ci if (!msg) { 73562306a36Sopenharmony_ci rc = -ENOMEM; 73662306a36Sopenharmony_ci goto out_putdev; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci rc = nfc_genl_send_device(msg, dev, info->snd_portid, info->snd_seq, 74062306a36Sopenharmony_ci NULL, 0); 74162306a36Sopenharmony_ci if (rc < 0) 74262306a36Sopenharmony_ci goto out_free; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci nfc_put_device(dev); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return genlmsg_reply(msg, info); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ciout_free: 74962306a36Sopenharmony_ci nlmsg_free(msg); 75062306a36Sopenharmony_ciout_putdev: 75162306a36Sopenharmony_ci nfc_put_device(dev); 75262306a36Sopenharmony_ci return rc; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int nfc_genl_dev_up(struct sk_buff *skb, struct genl_info *info) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct nfc_dev *dev; 75862306a36Sopenharmony_ci int rc; 75962306a36Sopenharmony_ci u32 idx; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) 76262306a36Sopenharmony_ci return -EINVAL; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci dev = nfc_get_device(idx); 76762306a36Sopenharmony_ci if (!dev) 76862306a36Sopenharmony_ci return -ENODEV; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci rc = nfc_dev_up(dev); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci nfc_put_device(dev); 77362306a36Sopenharmony_ci return rc; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int nfc_genl_dev_down(struct sk_buff *skb, struct genl_info *info) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct nfc_dev *dev; 77962306a36Sopenharmony_ci int rc; 78062306a36Sopenharmony_ci u32 idx; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) 78362306a36Sopenharmony_ci return -EINVAL; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci dev = nfc_get_device(idx); 78862306a36Sopenharmony_ci if (!dev) 78962306a36Sopenharmony_ci return -ENODEV; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci rc = nfc_dev_down(dev); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci nfc_put_device(dev); 79462306a36Sopenharmony_ci return rc; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct nfc_dev *dev; 80062306a36Sopenharmony_ci int rc; 80162306a36Sopenharmony_ci u32 idx; 80262306a36Sopenharmony_ci u32 im_protocols = 0, tm_protocols = 0; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci pr_debug("Poll start\n"); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 80762306a36Sopenharmony_ci ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] && 80862306a36Sopenharmony_ci !info->attrs[NFC_ATTR_PROTOCOLS]) && 80962306a36Sopenharmony_ci !info->attrs[NFC_ATTR_TM_PROTOCOLS])) 81062306a36Sopenharmony_ci return -EINVAL; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_TM_PROTOCOLS]) 81562306a36Sopenharmony_ci tm_protocols = nla_get_u32(info->attrs[NFC_ATTR_TM_PROTOCOLS]); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_IM_PROTOCOLS]) 81862306a36Sopenharmony_ci im_protocols = nla_get_u32(info->attrs[NFC_ATTR_IM_PROTOCOLS]); 81962306a36Sopenharmony_ci else if (info->attrs[NFC_ATTR_PROTOCOLS]) 82062306a36Sopenharmony_ci im_protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci dev = nfc_get_device(idx); 82362306a36Sopenharmony_ci if (!dev) 82462306a36Sopenharmony_ci return -ENODEV; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci mutex_lock(&dev->genl_data.genl_data_mutex); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci rc = nfc_start_poll(dev, im_protocols, tm_protocols); 82962306a36Sopenharmony_ci if (!rc) 83062306a36Sopenharmony_ci dev->genl_data.poll_req_portid = info->snd_portid; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci mutex_unlock(&dev->genl_data.genl_data_mutex); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci nfc_put_device(dev); 83562306a36Sopenharmony_ci return rc; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct nfc_dev *dev; 84162306a36Sopenharmony_ci int rc; 84262306a36Sopenharmony_ci u32 idx; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) 84562306a36Sopenharmony_ci return -EINVAL; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci dev = nfc_get_device(idx); 85062306a36Sopenharmony_ci if (!dev) 85162306a36Sopenharmony_ci return -ENODEV; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci device_lock(&dev->dev); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (!dev->polling) { 85662306a36Sopenharmony_ci device_unlock(&dev->dev); 85762306a36Sopenharmony_ci nfc_put_device(dev); 85862306a36Sopenharmony_ci return -EINVAL; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci device_unlock(&dev->dev); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci mutex_lock(&dev->genl_data.genl_data_mutex); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (dev->genl_data.poll_req_portid != info->snd_portid) { 86662306a36Sopenharmony_ci rc = -EBUSY; 86762306a36Sopenharmony_ci goto out; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci rc = nfc_stop_poll(dev); 87162306a36Sopenharmony_ci dev->genl_data.poll_req_portid = 0; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciout: 87462306a36Sopenharmony_ci mutex_unlock(&dev->genl_data.genl_data_mutex); 87562306a36Sopenharmony_ci nfc_put_device(dev); 87662306a36Sopenharmony_ci return rc; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct nfc_dev *dev; 88262306a36Sopenharmony_ci u32 device_idx, target_idx, protocol; 88362306a36Sopenharmony_ci int rc; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 88662306a36Sopenharmony_ci !info->attrs[NFC_ATTR_TARGET_INDEX] || 88762306a36Sopenharmony_ci !info->attrs[NFC_ATTR_PROTOCOLS]) 88862306a36Sopenharmony_ci return -EINVAL; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci dev = nfc_get_device(device_idx); 89362306a36Sopenharmony_ci if (!dev) 89462306a36Sopenharmony_ci return -ENODEV; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]); 89762306a36Sopenharmony_ci protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci nfc_deactivate_target(dev, target_idx, NFC_TARGET_MODE_SLEEP); 90062306a36Sopenharmony_ci rc = nfc_activate_target(dev, target_idx, protocol); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci nfc_put_device(dev); 90362306a36Sopenharmony_ci return rc; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int nfc_genl_deactivate_target(struct sk_buff *skb, 90762306a36Sopenharmony_ci struct genl_info *info) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct nfc_dev *dev; 91062306a36Sopenharmony_ci u32 device_idx, target_idx; 91162306a36Sopenharmony_ci int rc; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 91462306a36Sopenharmony_ci !info->attrs[NFC_ATTR_TARGET_INDEX]) 91562306a36Sopenharmony_ci return -EINVAL; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci dev = nfc_get_device(device_idx); 92062306a36Sopenharmony_ci if (!dev) 92162306a36Sopenharmony_ci return -ENODEV; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci rc = nfc_deactivate_target(dev, target_idx, NFC_TARGET_MODE_SLEEP); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci nfc_put_device(dev); 92862306a36Sopenharmony_ci return rc; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct nfc_dev *dev; 93462306a36Sopenharmony_ci int rc, tgt_idx; 93562306a36Sopenharmony_ci u32 idx; 93662306a36Sopenharmony_ci u8 comm; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci pr_debug("DEP link up\n"); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 94162306a36Sopenharmony_ci !info->attrs[NFC_ATTR_COMM_MODE]) 94262306a36Sopenharmony_ci return -EINVAL; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 94562306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_TARGET_INDEX]) 94662306a36Sopenharmony_ci tgt_idx = NFC_TARGET_IDX_ANY; 94762306a36Sopenharmony_ci else 94862306a36Sopenharmony_ci tgt_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci comm = nla_get_u8(info->attrs[NFC_ATTR_COMM_MODE]); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (comm != NFC_COMM_ACTIVE && comm != NFC_COMM_PASSIVE) 95362306a36Sopenharmony_ci return -EINVAL; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci dev = nfc_get_device(idx); 95662306a36Sopenharmony_ci if (!dev) 95762306a36Sopenharmony_ci return -ENODEV; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci rc = nfc_dep_link_up(dev, tgt_idx, comm); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci nfc_put_device(dev); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci return rc; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct nfc_dev *dev; 96962306a36Sopenharmony_ci int rc; 97062306a36Sopenharmony_ci u32 idx; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 97362306a36Sopenharmony_ci !info->attrs[NFC_ATTR_TARGET_INDEX]) 97462306a36Sopenharmony_ci return -EINVAL; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci dev = nfc_get_device(idx); 97962306a36Sopenharmony_ci if (!dev) 98062306a36Sopenharmony_ci return -ENODEV; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci rc = nfc_dep_link_down(dev); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci nfc_put_device(dev); 98562306a36Sopenharmony_ci return rc; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic int nfc_genl_send_params(struct sk_buff *msg, 98962306a36Sopenharmony_ci struct nfc_llcp_local *local, 99062306a36Sopenharmony_ci u32 portid, u32 seq) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci void *hdr; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0, 99562306a36Sopenharmony_ci NFC_CMD_LLC_GET_PARAMS); 99662306a36Sopenharmony_ci if (!hdr) 99762306a36Sopenharmony_ci return -EMSGSIZE; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) || 100062306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) || 100162306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) || 100262306a36Sopenharmony_ci nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux))) 100362306a36Sopenharmony_ci goto nla_put_failure; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci genlmsg_end(msg, hdr); 100662306a36Sopenharmony_ci return 0; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cinla_put_failure: 100962306a36Sopenharmony_ci genlmsg_cancel(msg, hdr); 101062306a36Sopenharmony_ci return -EMSGSIZE; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct nfc_dev *dev; 101662306a36Sopenharmony_ci struct nfc_llcp_local *local; 101762306a36Sopenharmony_ci int rc = 0; 101862306a36Sopenharmony_ci struct sk_buff *msg = NULL; 101962306a36Sopenharmony_ci u32 idx; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 102262306a36Sopenharmony_ci !info->attrs[NFC_ATTR_FIRMWARE_NAME]) 102362306a36Sopenharmony_ci return -EINVAL; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci dev = nfc_get_device(idx); 102862306a36Sopenharmony_ci if (!dev) 102962306a36Sopenharmony_ci return -ENODEV; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci device_lock(&dev->dev); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci local = nfc_llcp_find_local(dev); 103462306a36Sopenharmony_ci if (!local) { 103562306a36Sopenharmony_ci rc = -ENODEV; 103662306a36Sopenharmony_ci goto exit; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 104062306a36Sopenharmony_ci if (!msg) { 104162306a36Sopenharmony_ci rc = -ENOMEM; 104262306a36Sopenharmony_ci goto put_local; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ciput_local: 104862306a36Sopenharmony_ci nfc_llcp_local_put(local); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ciexit: 105162306a36Sopenharmony_ci device_unlock(&dev->dev); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci nfc_put_device(dev); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci if (rc < 0) { 105662306a36Sopenharmony_ci if (msg) 105762306a36Sopenharmony_ci nlmsg_free(msg); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return rc; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci return genlmsg_reply(msg, info); 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci struct nfc_dev *dev; 106862306a36Sopenharmony_ci struct nfc_llcp_local *local; 106962306a36Sopenharmony_ci u8 rw = 0; 107062306a36Sopenharmony_ci u16 miux = 0; 107162306a36Sopenharmony_ci u32 idx; 107262306a36Sopenharmony_ci int rc = 0; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 107562306a36Sopenharmony_ci (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] && 107662306a36Sopenharmony_ci !info->attrs[NFC_ATTR_LLC_PARAM_RW] && 107762306a36Sopenharmony_ci !info->attrs[NFC_ATTR_LLC_PARAM_MIUX])) 107862306a36Sopenharmony_ci return -EINVAL; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) { 108162306a36Sopenharmony_ci rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (rw > LLCP_MAX_RW) 108462306a36Sopenharmony_ci return -EINVAL; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) { 108862306a36Sopenharmony_ci miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (miux > LLCP_MAX_MIUX) 109162306a36Sopenharmony_ci return -EINVAL; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci dev = nfc_get_device(idx); 109762306a36Sopenharmony_ci if (!dev) 109862306a36Sopenharmony_ci return -ENODEV; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci device_lock(&dev->dev); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci local = nfc_llcp_find_local(dev); 110362306a36Sopenharmony_ci if (!local) { 110462306a36Sopenharmony_ci rc = -ENODEV; 110562306a36Sopenharmony_ci goto exit; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) { 110962306a36Sopenharmony_ci if (dev->dep_link_up) { 111062306a36Sopenharmony_ci rc = -EINPROGRESS; 111162306a36Sopenharmony_ci goto put_local; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]); 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) 111862306a36Sopenharmony_ci local->rw = rw; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) 112162306a36Sopenharmony_ci local->miux = cpu_to_be16(miux); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ciput_local: 112462306a36Sopenharmony_ci nfc_llcp_local_put(local); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ciexit: 112762306a36Sopenharmony_ci device_unlock(&dev->dev); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci nfc_put_device(dev); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci return rc; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci struct nfc_dev *dev; 113762306a36Sopenharmony_ci struct nfc_llcp_local *local; 113862306a36Sopenharmony_ci struct nlattr *attr, *sdp_attrs[NFC_SDP_ATTR_MAX+1]; 113962306a36Sopenharmony_ci u32 idx; 114062306a36Sopenharmony_ci u8 tid; 114162306a36Sopenharmony_ci char *uri; 114262306a36Sopenharmony_ci int rc = 0, rem; 114362306a36Sopenharmony_ci size_t uri_len, tlvs_len; 114462306a36Sopenharmony_ci struct hlist_head sdreq_list; 114562306a36Sopenharmony_ci struct nfc_llcp_sdp_tlv *sdreq; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 114862306a36Sopenharmony_ci !info->attrs[NFC_ATTR_LLC_SDP]) 114962306a36Sopenharmony_ci return -EINVAL; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci dev = nfc_get_device(idx); 115462306a36Sopenharmony_ci if (!dev) 115562306a36Sopenharmony_ci return -ENODEV; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci device_lock(&dev->dev); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (dev->dep_link_up == false) { 116062306a36Sopenharmony_ci rc = -ENOLINK; 116162306a36Sopenharmony_ci goto exit; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci local = nfc_llcp_find_local(dev); 116562306a36Sopenharmony_ci if (!local) { 116662306a36Sopenharmony_ci rc = -ENODEV; 116762306a36Sopenharmony_ci goto exit; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci INIT_HLIST_HEAD(&sdreq_list); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci tlvs_len = 0; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci nla_for_each_nested(attr, info->attrs[NFC_ATTR_LLC_SDP], rem) { 117562306a36Sopenharmony_ci rc = nla_parse_nested_deprecated(sdp_attrs, NFC_SDP_ATTR_MAX, 117662306a36Sopenharmony_ci attr, nfc_sdp_genl_policy, 117762306a36Sopenharmony_ci info->extack); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (rc != 0) { 118062306a36Sopenharmony_ci rc = -EINVAL; 118162306a36Sopenharmony_ci goto put_local; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (!sdp_attrs[NFC_SDP_ATTR_URI]) 118562306a36Sopenharmony_ci continue; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci uri_len = nla_len(sdp_attrs[NFC_SDP_ATTR_URI]); 118862306a36Sopenharmony_ci if (uri_len == 0) 118962306a36Sopenharmony_ci continue; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci uri = nla_data(sdp_attrs[NFC_SDP_ATTR_URI]); 119262306a36Sopenharmony_ci if (uri == NULL || *uri == 0) 119362306a36Sopenharmony_ci continue; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci tid = local->sdreq_next_tid++; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len); 119862306a36Sopenharmony_ci if (sdreq == NULL) { 119962306a36Sopenharmony_ci rc = -ENOMEM; 120062306a36Sopenharmony_ci goto put_local; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci tlvs_len += sdreq->tlv_len; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci hlist_add_head(&sdreq->node, &sdreq_list); 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (hlist_empty(&sdreq_list)) { 120962306a36Sopenharmony_ci rc = -EINVAL; 121062306a36Sopenharmony_ci goto put_local; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ciput_local: 121662306a36Sopenharmony_ci nfc_llcp_local_put(local); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ciexit: 121962306a36Sopenharmony_ci device_unlock(&dev->dev); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci nfc_put_device(dev); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci return rc; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic int nfc_genl_fw_download(struct sk_buff *skb, struct genl_info *info) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci struct nfc_dev *dev; 122962306a36Sopenharmony_ci int rc; 123062306a36Sopenharmony_ci u32 idx; 123162306a36Sopenharmony_ci char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || !info->attrs[NFC_ATTR_FIRMWARE_NAME]) 123462306a36Sopenharmony_ci return -EINVAL; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci dev = nfc_get_device(idx); 123962306a36Sopenharmony_ci if (!dev) 124062306a36Sopenharmony_ci return -ENODEV; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci nla_strscpy(firmware_name, info->attrs[NFC_ATTR_FIRMWARE_NAME], 124362306a36Sopenharmony_ci sizeof(firmware_name)); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci rc = nfc_fw_download(dev, firmware_name); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci nfc_put_device(dev); 124862306a36Sopenharmony_ci return rc; 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ciint nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name, 125262306a36Sopenharmony_ci u32 result) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci struct sk_buff *msg; 125562306a36Sopenharmony_ci void *hdr; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 125862306a36Sopenharmony_ci if (!msg) 125962306a36Sopenharmony_ci return -ENOMEM; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 126262306a36Sopenharmony_ci NFC_CMD_FW_DOWNLOAD); 126362306a36Sopenharmony_ci if (!hdr) 126462306a36Sopenharmony_ci goto free_msg; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) || 126762306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, result) || 126862306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) 126962306a36Sopenharmony_ci goto nla_put_failure; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci genlmsg_end(msg, hdr); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_ATOMIC); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci return 0; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cinla_put_failure: 127862306a36Sopenharmony_cifree_msg: 127962306a36Sopenharmony_ci nlmsg_free(msg); 128062306a36Sopenharmony_ci return -EMSGSIZE; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic int nfc_genl_enable_se(struct sk_buff *skb, struct genl_info *info) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct nfc_dev *dev; 128662306a36Sopenharmony_ci int rc; 128762306a36Sopenharmony_ci u32 idx, se_idx; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 129062306a36Sopenharmony_ci !info->attrs[NFC_ATTR_SE_INDEX]) 129162306a36Sopenharmony_ci return -EINVAL; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 129462306a36Sopenharmony_ci se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci dev = nfc_get_device(idx); 129762306a36Sopenharmony_ci if (!dev) 129862306a36Sopenharmony_ci return -ENODEV; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci rc = nfc_enable_se(dev, se_idx); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci nfc_put_device(dev); 130362306a36Sopenharmony_ci return rc; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci struct nfc_dev *dev; 130962306a36Sopenharmony_ci int rc; 131062306a36Sopenharmony_ci u32 idx, se_idx; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 131362306a36Sopenharmony_ci !info->attrs[NFC_ATTR_SE_INDEX]) 131462306a36Sopenharmony_ci return -EINVAL; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 131762306a36Sopenharmony_ci se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci dev = nfc_get_device(idx); 132062306a36Sopenharmony_ci if (!dev) 132162306a36Sopenharmony_ci return -ENODEV; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci rc = nfc_disable_se(dev, se_idx); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci nfc_put_device(dev); 132662306a36Sopenharmony_ci return rc; 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_cistatic int nfc_genl_send_se(struct sk_buff *msg, struct nfc_dev *dev, 133062306a36Sopenharmony_ci u32 portid, u32 seq, 133162306a36Sopenharmony_ci struct netlink_callback *cb, 133262306a36Sopenharmony_ci int flags) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci void *hdr; 133562306a36Sopenharmony_ci struct nfc_se *se, *n; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci list_for_each_entry_safe(se, n, &dev->secure_elements, list) { 133862306a36Sopenharmony_ci hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags, 133962306a36Sopenharmony_ci NFC_CMD_GET_SE); 134062306a36Sopenharmony_ci if (!hdr) 134162306a36Sopenharmony_ci goto nla_put_failure; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (cb) 134462306a36Sopenharmony_ci genl_dump_check_consistent(cb, hdr); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || 134762306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_SE_INDEX, se->idx) || 134862306a36Sopenharmony_ci nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type)) 134962306a36Sopenharmony_ci goto nla_put_failure; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci genlmsg_end(msg, hdr); 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci return 0; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cinla_put_failure: 135762306a36Sopenharmony_ci genlmsg_cancel(msg, hdr); 135862306a36Sopenharmony_ci return -EMSGSIZE; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cistatic int nfc_genl_dump_ses(struct sk_buff *skb, 136262306a36Sopenharmony_ci struct netlink_callback *cb) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; 136562306a36Sopenharmony_ci struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; 136662306a36Sopenharmony_ci bool first_call = false; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci if (!iter) { 136962306a36Sopenharmony_ci first_call = true; 137062306a36Sopenharmony_ci iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL); 137162306a36Sopenharmony_ci if (!iter) 137262306a36Sopenharmony_ci return -ENOMEM; 137362306a36Sopenharmony_ci cb->args[0] = (long) iter; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci mutex_lock(&nfc_devlist_mutex); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci cb->seq = nfc_devlist_generation; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci if (first_call) { 138162306a36Sopenharmony_ci nfc_device_iter_init(iter); 138262306a36Sopenharmony_ci dev = nfc_device_iter_next(iter); 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci while (dev) { 138662306a36Sopenharmony_ci int rc; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci rc = nfc_genl_send_se(skb, dev, NETLINK_CB(cb->skb).portid, 138962306a36Sopenharmony_ci cb->nlh->nlmsg_seq, cb, NLM_F_MULTI); 139062306a36Sopenharmony_ci if (rc < 0) 139162306a36Sopenharmony_ci break; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci dev = nfc_device_iter_next(iter); 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci mutex_unlock(&nfc_devlist_mutex); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci cb->args[1] = (long) dev; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci return skb->len; 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_cistatic int nfc_genl_dump_ses_done(struct netlink_callback *cb) 140462306a36Sopenharmony_ci{ 140562306a36Sopenharmony_ci struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (iter) { 140862306a36Sopenharmony_ci nfc_device_iter_exit(iter); 140962306a36Sopenharmony_ci kfree(iter); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci return 0; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic int nfc_se_io(struct nfc_dev *dev, u32 se_idx, 141662306a36Sopenharmony_ci u8 *apdu, size_t apdu_length, 141762306a36Sopenharmony_ci se_io_cb_t cb, void *cb_context) 141862306a36Sopenharmony_ci{ 141962306a36Sopenharmony_ci struct nfc_se *se; 142062306a36Sopenharmony_ci int rc; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci device_lock(&dev->dev); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (!device_is_registered(&dev->dev)) { 142762306a36Sopenharmony_ci rc = -ENODEV; 142862306a36Sopenharmony_ci goto error; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (!dev->dev_up) { 143262306a36Sopenharmony_ci rc = -ENODEV; 143362306a36Sopenharmony_ci goto error; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci if (!dev->ops->se_io) { 143762306a36Sopenharmony_ci rc = -EOPNOTSUPP; 143862306a36Sopenharmony_ci goto error; 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci se = nfc_find_se(dev, se_idx); 144262306a36Sopenharmony_ci if (!se) { 144362306a36Sopenharmony_ci rc = -EINVAL; 144462306a36Sopenharmony_ci goto error; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (se->state != NFC_SE_ENABLED) { 144862306a36Sopenharmony_ci rc = -ENODEV; 144962306a36Sopenharmony_ci goto error; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci rc = dev->ops->se_io(dev, se_idx, apdu, 145362306a36Sopenharmony_ci apdu_length, cb, cb_context); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci device_unlock(&dev->dev); 145662306a36Sopenharmony_ci return rc; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_cierror: 145962306a36Sopenharmony_ci device_unlock(&dev->dev); 146062306a36Sopenharmony_ci kfree(cb_context); 146162306a36Sopenharmony_ci return rc; 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistruct se_io_ctx { 146562306a36Sopenharmony_ci u32 dev_idx; 146662306a36Sopenharmony_ci u32 se_idx; 146762306a36Sopenharmony_ci}; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_cistatic void se_io_cb(void *context, u8 *apdu, size_t apdu_len, int err) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct se_io_ctx *ctx = context; 147262306a36Sopenharmony_ci struct sk_buff *msg; 147362306a36Sopenharmony_ci void *hdr; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 147662306a36Sopenharmony_ci if (!msg) { 147762306a36Sopenharmony_ci kfree(ctx); 147862306a36Sopenharmony_ci return; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, 148262306a36Sopenharmony_ci NFC_CMD_SE_IO); 148362306a36Sopenharmony_ci if (!hdr) 148462306a36Sopenharmony_ci goto free_msg; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, ctx->dev_idx) || 148762306a36Sopenharmony_ci nla_put_u32(msg, NFC_ATTR_SE_INDEX, ctx->se_idx) || 148862306a36Sopenharmony_ci nla_put(msg, NFC_ATTR_SE_APDU, apdu_len, apdu)) 148962306a36Sopenharmony_ci goto nla_put_failure; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci genlmsg_end(msg, hdr); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci kfree(ctx); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_cinla_put_failure: 150062306a36Sopenharmony_cifree_msg: 150162306a36Sopenharmony_ci nlmsg_free(msg); 150262306a36Sopenharmony_ci kfree(ctx); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci return; 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_cistatic int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci struct nfc_dev *dev; 151062306a36Sopenharmony_ci struct se_io_ctx *ctx; 151162306a36Sopenharmony_ci u32 dev_idx, se_idx; 151262306a36Sopenharmony_ci u8 *apdu; 151362306a36Sopenharmony_ci size_t apdu_len; 151462306a36Sopenharmony_ci int rc; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 151762306a36Sopenharmony_ci !info->attrs[NFC_ATTR_SE_INDEX] || 151862306a36Sopenharmony_ci !info->attrs[NFC_ATTR_SE_APDU]) 151962306a36Sopenharmony_ci return -EINVAL; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 152262306a36Sopenharmony_ci se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci dev = nfc_get_device(dev_idx); 152562306a36Sopenharmony_ci if (!dev) 152662306a36Sopenharmony_ci return -ENODEV; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci if (!dev->ops || !dev->ops->se_io) { 152962306a36Sopenharmony_ci rc = -EOPNOTSUPP; 153062306a36Sopenharmony_ci goto put_dev; 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci apdu_len = nla_len(info->attrs[NFC_ATTR_SE_APDU]); 153462306a36Sopenharmony_ci if (apdu_len == 0) { 153562306a36Sopenharmony_ci rc = -EINVAL; 153662306a36Sopenharmony_ci goto put_dev; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci apdu = nla_data(info->attrs[NFC_ATTR_SE_APDU]); 154062306a36Sopenharmony_ci if (!apdu) { 154162306a36Sopenharmony_ci rc = -EINVAL; 154262306a36Sopenharmony_ci goto put_dev; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci ctx = kzalloc(sizeof(struct se_io_ctx), GFP_KERNEL); 154662306a36Sopenharmony_ci if (!ctx) { 154762306a36Sopenharmony_ci rc = -ENOMEM; 154862306a36Sopenharmony_ci goto put_dev; 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci ctx->dev_idx = dev_idx; 155262306a36Sopenharmony_ci ctx->se_idx = se_idx; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci rc = nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ciput_dev: 155762306a36Sopenharmony_ci nfc_put_device(dev); 155862306a36Sopenharmony_ci return rc; 155962306a36Sopenharmony_ci} 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_cistatic int nfc_genl_vendor_cmd(struct sk_buff *skb, 156262306a36Sopenharmony_ci struct genl_info *info) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci struct nfc_dev *dev; 156562306a36Sopenharmony_ci const struct nfc_vendor_cmd *cmd; 156662306a36Sopenharmony_ci u32 dev_idx, vid, subcmd; 156762306a36Sopenharmony_ci u8 *data; 156862306a36Sopenharmony_ci size_t data_len; 156962306a36Sopenharmony_ci int i, err; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || 157262306a36Sopenharmony_ci !info->attrs[NFC_ATTR_VENDOR_ID] || 157362306a36Sopenharmony_ci !info->attrs[NFC_ATTR_VENDOR_SUBCMD]) 157462306a36Sopenharmony_ci return -EINVAL; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); 157762306a36Sopenharmony_ci vid = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_ID]); 157862306a36Sopenharmony_ci subcmd = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_SUBCMD]); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci dev = nfc_get_device(dev_idx); 158162306a36Sopenharmony_ci if (!dev) 158262306a36Sopenharmony_ci return -ENODEV; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (!dev->vendor_cmds || !dev->n_vendor_cmds) { 158562306a36Sopenharmony_ci err = -ENODEV; 158662306a36Sopenharmony_ci goto put_dev; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (info->attrs[NFC_ATTR_VENDOR_DATA]) { 159062306a36Sopenharmony_ci data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]); 159162306a36Sopenharmony_ci data_len = nla_len(info->attrs[NFC_ATTR_VENDOR_DATA]); 159262306a36Sopenharmony_ci if (data_len == 0) { 159362306a36Sopenharmony_ci err = -EINVAL; 159462306a36Sopenharmony_ci goto put_dev; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci } else { 159762306a36Sopenharmony_ci data = NULL; 159862306a36Sopenharmony_ci data_len = 0; 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci for (i = 0; i < dev->n_vendor_cmds; i++) { 160262306a36Sopenharmony_ci cmd = &dev->vendor_cmds[i]; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (cmd->vendor_id != vid || cmd->subcmd != subcmd) 160562306a36Sopenharmony_ci continue; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci dev->cur_cmd_info = info; 160862306a36Sopenharmony_ci err = cmd->doit(dev, data, data_len); 160962306a36Sopenharmony_ci dev->cur_cmd_info = NULL; 161062306a36Sopenharmony_ci goto put_dev; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci err = -EOPNOTSUPP; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ciput_dev: 161662306a36Sopenharmony_ci nfc_put_device(dev); 161762306a36Sopenharmony_ci return err; 161862306a36Sopenharmony_ci} 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci/* message building helper */ 162162306a36Sopenharmony_cistatic inline void *nfc_hdr_put(struct sk_buff *skb, u32 portid, u32 seq, 162262306a36Sopenharmony_ci int flags, u8 cmd) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci /* since there is no private header just add the generic one */ 162562306a36Sopenharmony_ci return genlmsg_put(skb, portid, seq, &nfc_genl_family, flags, cmd); 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_cistatic struct sk_buff * 162962306a36Sopenharmony_ci__nfc_alloc_vendor_cmd_skb(struct nfc_dev *dev, int approxlen, 163062306a36Sopenharmony_ci u32 portid, u32 seq, 163162306a36Sopenharmony_ci enum nfc_attrs attr, 163262306a36Sopenharmony_ci u32 oui, u32 subcmd, gfp_t gfp) 163362306a36Sopenharmony_ci{ 163462306a36Sopenharmony_ci struct sk_buff *skb; 163562306a36Sopenharmony_ci void *hdr; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci skb = nlmsg_new(approxlen + 100, gfp); 163862306a36Sopenharmony_ci if (!skb) 163962306a36Sopenharmony_ci return NULL; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci hdr = nfc_hdr_put(skb, portid, seq, 0, NFC_CMD_VENDOR); 164262306a36Sopenharmony_ci if (!hdr) { 164362306a36Sopenharmony_ci kfree_skb(skb); 164462306a36Sopenharmony_ci return NULL; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci if (nla_put_u32(skb, NFC_ATTR_DEVICE_INDEX, dev->idx)) 164862306a36Sopenharmony_ci goto nla_put_failure; 164962306a36Sopenharmony_ci if (nla_put_u32(skb, NFC_ATTR_VENDOR_ID, oui)) 165062306a36Sopenharmony_ci goto nla_put_failure; 165162306a36Sopenharmony_ci if (nla_put_u32(skb, NFC_ATTR_VENDOR_SUBCMD, subcmd)) 165262306a36Sopenharmony_ci goto nla_put_failure; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci ((void **)skb->cb)[0] = dev; 165562306a36Sopenharmony_ci ((void **)skb->cb)[1] = hdr; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci return skb; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cinla_put_failure: 166062306a36Sopenharmony_ci kfree_skb(skb); 166162306a36Sopenharmony_ci return NULL; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistruct sk_buff *__nfc_alloc_vendor_cmd_reply_skb(struct nfc_dev *dev, 166562306a36Sopenharmony_ci enum nfc_attrs attr, 166662306a36Sopenharmony_ci u32 oui, u32 subcmd, 166762306a36Sopenharmony_ci int approxlen) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci if (WARN_ON(!dev->cur_cmd_info)) 167062306a36Sopenharmony_ci return NULL; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci return __nfc_alloc_vendor_cmd_skb(dev, approxlen, 167362306a36Sopenharmony_ci dev->cur_cmd_info->snd_portid, 167462306a36Sopenharmony_ci dev->cur_cmd_info->snd_seq, attr, 167562306a36Sopenharmony_ci oui, subcmd, GFP_KERNEL); 167662306a36Sopenharmony_ci} 167762306a36Sopenharmony_ciEXPORT_SYMBOL(__nfc_alloc_vendor_cmd_reply_skb); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ciint nfc_vendor_cmd_reply(struct sk_buff *skb) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci struct nfc_dev *dev = ((void **)skb->cb)[0]; 168262306a36Sopenharmony_ci void *hdr = ((void **)skb->cb)[1]; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* clear CB data for netlink core to own from now on */ 168562306a36Sopenharmony_ci memset(skb->cb, 0, sizeof(skb->cb)); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci if (WARN_ON(!dev->cur_cmd_info)) { 168862306a36Sopenharmony_ci kfree_skb(skb); 168962306a36Sopenharmony_ci return -EINVAL; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci genlmsg_end(skb, hdr); 169362306a36Sopenharmony_ci return genlmsg_reply(skb, dev->cur_cmd_info); 169462306a36Sopenharmony_ci} 169562306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_vendor_cmd_reply); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_cistatic const struct genl_ops nfc_genl_ops[] = { 169862306a36Sopenharmony_ci { 169962306a36Sopenharmony_ci .cmd = NFC_CMD_GET_DEVICE, 170062306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 170162306a36Sopenharmony_ci .doit = nfc_genl_get_device, 170262306a36Sopenharmony_ci .dumpit = nfc_genl_dump_devices, 170362306a36Sopenharmony_ci .done = nfc_genl_dump_devices_done, 170462306a36Sopenharmony_ci }, 170562306a36Sopenharmony_ci { 170662306a36Sopenharmony_ci .cmd = NFC_CMD_DEV_UP, 170762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 170862306a36Sopenharmony_ci .doit = nfc_genl_dev_up, 170962306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 171062306a36Sopenharmony_ci }, 171162306a36Sopenharmony_ci { 171262306a36Sopenharmony_ci .cmd = NFC_CMD_DEV_DOWN, 171362306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 171462306a36Sopenharmony_ci .doit = nfc_genl_dev_down, 171562306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 171662306a36Sopenharmony_ci }, 171762306a36Sopenharmony_ci { 171862306a36Sopenharmony_ci .cmd = NFC_CMD_START_POLL, 171962306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 172062306a36Sopenharmony_ci .doit = nfc_genl_start_poll, 172162306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 172262306a36Sopenharmony_ci }, 172362306a36Sopenharmony_ci { 172462306a36Sopenharmony_ci .cmd = NFC_CMD_STOP_POLL, 172562306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 172662306a36Sopenharmony_ci .doit = nfc_genl_stop_poll, 172762306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 172862306a36Sopenharmony_ci }, 172962306a36Sopenharmony_ci { 173062306a36Sopenharmony_ci .cmd = NFC_CMD_DEP_LINK_UP, 173162306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 173262306a36Sopenharmony_ci .doit = nfc_genl_dep_link_up, 173362306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 173462306a36Sopenharmony_ci }, 173562306a36Sopenharmony_ci { 173662306a36Sopenharmony_ci .cmd = NFC_CMD_DEP_LINK_DOWN, 173762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 173862306a36Sopenharmony_ci .doit = nfc_genl_dep_link_down, 173962306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 174062306a36Sopenharmony_ci }, 174162306a36Sopenharmony_ci { 174262306a36Sopenharmony_ci .cmd = NFC_CMD_GET_TARGET, 174362306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | 174462306a36Sopenharmony_ci GENL_DONT_VALIDATE_DUMP_STRICT, 174562306a36Sopenharmony_ci .dumpit = nfc_genl_dump_targets, 174662306a36Sopenharmony_ci .done = nfc_genl_dump_targets_done, 174762306a36Sopenharmony_ci }, 174862306a36Sopenharmony_ci { 174962306a36Sopenharmony_ci .cmd = NFC_CMD_LLC_GET_PARAMS, 175062306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 175162306a36Sopenharmony_ci .doit = nfc_genl_llc_get_params, 175262306a36Sopenharmony_ci }, 175362306a36Sopenharmony_ci { 175462306a36Sopenharmony_ci .cmd = NFC_CMD_LLC_SET_PARAMS, 175562306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 175662306a36Sopenharmony_ci .doit = nfc_genl_llc_set_params, 175762306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 175862306a36Sopenharmony_ci }, 175962306a36Sopenharmony_ci { 176062306a36Sopenharmony_ci .cmd = NFC_CMD_LLC_SDREQ, 176162306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 176262306a36Sopenharmony_ci .doit = nfc_genl_llc_sdreq, 176362306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 176462306a36Sopenharmony_ci }, 176562306a36Sopenharmony_ci { 176662306a36Sopenharmony_ci .cmd = NFC_CMD_FW_DOWNLOAD, 176762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 176862306a36Sopenharmony_ci .doit = nfc_genl_fw_download, 176962306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 177062306a36Sopenharmony_ci }, 177162306a36Sopenharmony_ci { 177262306a36Sopenharmony_ci .cmd = NFC_CMD_ENABLE_SE, 177362306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 177462306a36Sopenharmony_ci .doit = nfc_genl_enable_se, 177562306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 177662306a36Sopenharmony_ci }, 177762306a36Sopenharmony_ci { 177862306a36Sopenharmony_ci .cmd = NFC_CMD_DISABLE_SE, 177962306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 178062306a36Sopenharmony_ci .doit = nfc_genl_disable_se, 178162306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 178262306a36Sopenharmony_ci }, 178362306a36Sopenharmony_ci { 178462306a36Sopenharmony_ci .cmd = NFC_CMD_GET_SE, 178562306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 178662306a36Sopenharmony_ci .dumpit = nfc_genl_dump_ses, 178762306a36Sopenharmony_ci .done = nfc_genl_dump_ses_done, 178862306a36Sopenharmony_ci }, 178962306a36Sopenharmony_ci { 179062306a36Sopenharmony_ci .cmd = NFC_CMD_SE_IO, 179162306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 179262306a36Sopenharmony_ci .doit = nfc_genl_se_io, 179362306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 179462306a36Sopenharmony_ci }, 179562306a36Sopenharmony_ci { 179662306a36Sopenharmony_ci .cmd = NFC_CMD_ACTIVATE_TARGET, 179762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 179862306a36Sopenharmony_ci .doit = nfc_genl_activate_target, 179962306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 180062306a36Sopenharmony_ci }, 180162306a36Sopenharmony_ci { 180262306a36Sopenharmony_ci .cmd = NFC_CMD_VENDOR, 180362306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 180462306a36Sopenharmony_ci .doit = nfc_genl_vendor_cmd, 180562306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 180662306a36Sopenharmony_ci }, 180762306a36Sopenharmony_ci { 180862306a36Sopenharmony_ci .cmd = NFC_CMD_DEACTIVATE_TARGET, 180962306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 181062306a36Sopenharmony_ci .doit = nfc_genl_deactivate_target, 181162306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 181262306a36Sopenharmony_ci }, 181362306a36Sopenharmony_ci}; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_cistatic struct genl_family nfc_genl_family __ro_after_init = { 181662306a36Sopenharmony_ci .hdrsize = 0, 181762306a36Sopenharmony_ci .name = NFC_GENL_NAME, 181862306a36Sopenharmony_ci .version = NFC_GENL_VERSION, 181962306a36Sopenharmony_ci .maxattr = NFC_ATTR_MAX, 182062306a36Sopenharmony_ci .policy = nfc_genl_policy, 182162306a36Sopenharmony_ci .module = THIS_MODULE, 182262306a36Sopenharmony_ci .ops = nfc_genl_ops, 182362306a36Sopenharmony_ci .n_ops = ARRAY_SIZE(nfc_genl_ops), 182462306a36Sopenharmony_ci .resv_start_op = NFC_CMD_DEACTIVATE_TARGET + 1, 182562306a36Sopenharmony_ci .mcgrps = nfc_genl_mcgrps, 182662306a36Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(nfc_genl_mcgrps), 182762306a36Sopenharmony_ci}; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_cistruct urelease_work { 183162306a36Sopenharmony_ci struct work_struct w; 183262306a36Sopenharmony_ci u32 portid; 183362306a36Sopenharmony_ci}; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_cistatic void nfc_urelease_event_work(struct work_struct *work) 183662306a36Sopenharmony_ci{ 183762306a36Sopenharmony_ci struct urelease_work *w = container_of(work, struct urelease_work, w); 183862306a36Sopenharmony_ci struct class_dev_iter iter; 183962306a36Sopenharmony_ci struct nfc_dev *dev; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci pr_debug("portid %d\n", w->portid); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci mutex_lock(&nfc_devlist_mutex); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci nfc_device_iter_init(&iter); 184662306a36Sopenharmony_ci dev = nfc_device_iter_next(&iter); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci while (dev) { 184962306a36Sopenharmony_ci mutex_lock(&dev->genl_data.genl_data_mutex); 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci if (dev->genl_data.poll_req_portid == w->portid) { 185262306a36Sopenharmony_ci nfc_stop_poll(dev); 185362306a36Sopenharmony_ci dev->genl_data.poll_req_portid = 0; 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci mutex_unlock(&dev->genl_data.genl_data_mutex); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci dev = nfc_device_iter_next(&iter); 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci nfc_device_iter_exit(&iter); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci mutex_unlock(&nfc_devlist_mutex); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci kfree(w); 186662306a36Sopenharmony_ci} 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_cistatic int nfc_genl_rcv_nl_event(struct notifier_block *this, 186962306a36Sopenharmony_ci unsigned long event, void *ptr) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci struct netlink_notify *n = ptr; 187262306a36Sopenharmony_ci struct urelease_work *w; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) 187562306a36Sopenharmony_ci goto out; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci pr_debug("NETLINK_URELEASE event from id %d\n", n->portid); 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci w = kmalloc(sizeof(*w), GFP_ATOMIC); 188062306a36Sopenharmony_ci if (w) { 188162306a36Sopenharmony_ci INIT_WORK(&w->w, nfc_urelease_event_work); 188262306a36Sopenharmony_ci w->portid = n->portid; 188362306a36Sopenharmony_ci schedule_work(&w->w); 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ciout: 188762306a36Sopenharmony_ci return NOTIFY_DONE; 188862306a36Sopenharmony_ci} 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_civoid nfc_genl_data_init(struct nfc_genl_data *genl_data) 189162306a36Sopenharmony_ci{ 189262306a36Sopenharmony_ci genl_data->poll_req_portid = 0; 189362306a36Sopenharmony_ci mutex_init(&genl_data->genl_data_mutex); 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_civoid nfc_genl_data_exit(struct nfc_genl_data *genl_data) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci mutex_destroy(&genl_data->genl_data_mutex); 189962306a36Sopenharmony_ci} 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_cistatic struct notifier_block nl_notifier = { 190262306a36Sopenharmony_ci .notifier_call = nfc_genl_rcv_nl_event, 190362306a36Sopenharmony_ci}; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci/** 190662306a36Sopenharmony_ci * nfc_genl_init() - Initialize netlink interface 190762306a36Sopenharmony_ci * 190862306a36Sopenharmony_ci * This initialization function registers the nfc netlink family. 190962306a36Sopenharmony_ci */ 191062306a36Sopenharmony_ciint __init nfc_genl_init(void) 191162306a36Sopenharmony_ci{ 191262306a36Sopenharmony_ci int rc; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci rc = genl_register_family(&nfc_genl_family); 191562306a36Sopenharmony_ci if (rc) 191662306a36Sopenharmony_ci return rc; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci netlink_register_notifier(&nl_notifier); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci return 0; 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci/** 192462306a36Sopenharmony_ci * nfc_genl_exit() - Deinitialize netlink interface 192562306a36Sopenharmony_ci * 192662306a36Sopenharmony_ci * This exit function unregisters the nfc netlink family. 192762306a36Sopenharmony_ci */ 192862306a36Sopenharmony_civoid nfc_genl_exit(void) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci netlink_unregister_notifier(&nl_notifier); 193162306a36Sopenharmony_ci genl_unregister_family(&nfc_genl_family); 193262306a36Sopenharmony_ci} 1933