/*
* Copyright (C) 2011 Instituto Nokia de Tecnologia
*
* Authors:
* Lauro Ramos Venancio <lauro.venancio@openbossa.org>
* Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
#include <net/genetlink.h>
#include <linux/nfc.h>
#include <linux/slab.h>
#include "nfc.h"
static struct genl_multicast_group nfc_genl_event_mcgrp = {
.name = NFC_GENL_MCAST_EVENT_NAME,
};
static struct genl_family nfc_genl_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = NFC_GENL_NAME,
.version = NFC_GENL_VERSION,
.maxattr = NFC_ATTR_MAX,
};
static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
[NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 },
[NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING,
.len = NFC_DEVICE_NAME_MAXSIZE },
[NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 },
[NFC_ATTR_COMM_MODE] = { .type = NLA_U8 },
[NFC_ATTR_RF_MODE] = { .type = NLA_U8 },
[NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 },
[NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 },
[NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 },
};
static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
struct netlink_callback *cb, int flags)
{
void *hdr;
hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&nfc_genl_family, flags, NFC_CMD_GET_TARGET);
if (!hdr)
return -EMSGSIZE;
genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
if (nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target->idx) ||
nla_put_u32(msg, NFC_ATTR_PROTOCOLS, target->supported_protocols) ||
nla_put_u16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res) ||
nla_put_u8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res))
goto nla_put_failure;
if (target->nfcid1_len > 0 &&
nla_put(msg, NFC_ATTR_TARGET_NFCID1, target->nfcid1_len,
target->nfcid1))
goto nla_put_failure;
if (target->sensb_res_len > 0 &&
nla_put(msg, NFC_ATTR_TARGET_SENSB_RES, target->sensb_res_len,
target->sensb_res))
goto nla_put_failure;
if (target->sensf_res_len > 0 &&
nla_put(msg, NFC_ATTR_TARGET_SENSF_RES, target->sensf_res_len,
target->sensf_res))
goto nla_put_failure;
return genlmsg_end(msg, hdr);
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
{
struct nfc_dev *dev;
int rc;
u32 idx;
rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize,
nfc_genl_family.attrbuf,
nfc_genl_family.maxattr,
nfc_genl_policy);
if (rc < 0)
return ERR_PTR(rc);
if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX])
return ERR_PTR(-EINVAL);
idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]);
dev = nfc_get_device(idx);
if (!dev)
return ERR_PTR(-ENODEV);
return dev;
}
static int nfc_genl_dump_targets(struct sk_buff *skb,
struct netlink_callback *cb)
{
int i = cb->args[0];
struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
int rc;
if (!dev) {
dev = __get_device_from_cb(cb);
if (IS_ERR(dev))
return PTR_ERR(dev);
cb->args[1] = (long) dev;
}
device_lock(&dev->dev);
cb->seq = dev->targets_generation;
while (i < dev->n_targets) {
rc = nfc_genl_send_target(skb, &dev->targets[i], cb,
NLM_F_MULTI);
if (rc < 0)
break;
i++;
}
device_unlock(&dev->dev);
cb->args[0] = i;
return skb->len;
}
static int nfc_genl_dump_targets_done(struct netlink_callback *cb)
{
struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
if (dev)
nfc_put_device(dev);
return 0;
}
int nfc_genl_targets_found(struct nfc_dev *dev