nfc: netlink: Add capability to reply to vendor_cmd with data
A proprietary vendor command may send back useful data to the user application. For example, the field level applied on the NFC router antenna. Still based on net/wireless/nl80211.c implementation, add nfc_vendor_cmd_alloc_reply_skb and nfc_vendor_cmd_reply in order to send back over netlink data generated by a proprietary command. Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
parent
5a9e0ffc0f
commit
29e76924cf
2 changed files with 125 additions and 2 deletions
|
@ -203,6 +203,7 @@ struct nfc_dev {
|
||||||
int n_vendor_cmds;
|
int n_vendor_cmds;
|
||||||
|
|
||||||
struct nfc_ops *ops;
|
struct nfc_ops *ops;
|
||||||
|
struct genl_info *cur_cmd_info;
|
||||||
};
|
};
|
||||||
#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
|
#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
|
||||||
|
|
||||||
|
@ -318,4 +319,44 @@ static inline int nfc_set_vendor_cmds(struct nfc_dev *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sk_buff *__nfc_alloc_vendor_cmd_reply_skb(struct nfc_dev *dev,
|
||||||
|
enum nfc_attrs attr,
|
||||||
|
u32 oui, u32 subcmd,
|
||||||
|
int approxlen);
|
||||||
|
int nfc_vendor_cmd_reply(struct sk_buff *skb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nfc_vendor_cmd_alloc_reply_skb - allocate vendor command reply
|
||||||
|
* @dev: nfc device
|
||||||
|
* @oui: vendor oui
|
||||||
|
* @approxlen: an upper bound of the length of the data that will
|
||||||
|
* be put into the skb
|
||||||
|
*
|
||||||
|
* This function allocates and pre-fills an skb for a reply to
|
||||||
|
* a vendor command. Since it is intended for a reply, calling
|
||||||
|
* it outside of a vendor command's doit() operation is invalid.
|
||||||
|
*
|
||||||
|
* The returned skb is pre-filled with some identifying data in
|
||||||
|
* a way that any data that is put into the skb (with skb_put(),
|
||||||
|
* nla_put() or similar) will end up being within the
|
||||||
|
* %NFC_ATTR_VENDOR_DATA attribute, so all that needs to be done
|
||||||
|
* with the skb is adding data for the corresponding userspace tool
|
||||||
|
* which can then read that data out of the vendor data attribute.
|
||||||
|
* You must not modify the skb in any other way.
|
||||||
|
*
|
||||||
|
* When done, call nfc_vendor_cmd_reply() with the skb and return
|
||||||
|
* its error code as the result of the doit() operation.
|
||||||
|
*
|
||||||
|
* Return: An allocated and pre-filled skb. %NULL if any errors happen.
|
||||||
|
*/
|
||||||
|
static inline struct sk_buff *
|
||||||
|
nfc_vendor_cmd_alloc_reply_skb(struct nfc_dev *dev,
|
||||||
|
u32 oui, u32 subcmd, int approxlen)
|
||||||
|
{
|
||||||
|
return __nfc_alloc_vendor_cmd_reply_skb(dev,
|
||||||
|
NFC_ATTR_VENDOR_DATA,
|
||||||
|
oui,
|
||||||
|
subcmd, approxlen);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __NET_NFC_H */
|
#endif /* __NET_NFC_H */
|
||||||
|
|
|
@ -63,6 +63,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
|
||||||
[NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
|
[NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
|
||||||
.len = NFC_FIRMWARE_NAME_MAXSIZE },
|
.len = NFC_FIRMWARE_NAME_MAXSIZE },
|
||||||
[NFC_ATTR_SE_APDU] = { .type = NLA_BINARY },
|
[NFC_ATTR_SE_APDU] = { .type = NLA_BINARY },
|
||||||
|
[NFC_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
|
static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
|
||||||
|
@ -1503,7 +1505,7 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb,
|
||||||
u32 dev_idx, vid, subcmd;
|
u32 dev_idx, vid, subcmd;
|
||||||
u8 *data;
|
u8 *data;
|
||||||
size_t data_len;
|
size_t data_len;
|
||||||
int i;
|
int i, err;
|
||||||
|
|
||||||
if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
|
if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
|
||||||
!info->attrs[NFC_ATTR_VENDOR_ID] ||
|
!info->attrs[NFC_ATTR_VENDOR_ID] ||
|
||||||
|
@ -1534,12 +1536,92 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb,
|
||||||
if (cmd->vendor_id != vid || cmd->subcmd != subcmd)
|
if (cmd->vendor_id != vid || cmd->subcmd != subcmd)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
return cmd->doit(dev, data, data_len);
|
dev->cur_cmd_info = info;
|
||||||
|
err = cmd->doit(dev, data, data_len);
|
||||||
|
dev->cur_cmd_info = NULL;
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* message building helper */
|
||||||
|
static inline void *nfc_hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
|
||||||
|
int flags, u8 cmd)
|
||||||
|
{
|
||||||
|
/* since there is no private header just add the generic one */
|
||||||
|
return genlmsg_put(skb, portid, seq, &nfc_genl_family, flags, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *
|
||||||
|
__nfc_alloc_vendor_cmd_skb(struct nfc_dev *dev, int approxlen,
|
||||||
|
u32 portid, u32 seq,
|
||||||
|
enum nfc_attrs attr,
|
||||||
|
u32 oui, u32 subcmd, gfp_t gfp)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
void *hdr;
|
||||||
|
|
||||||
|
skb = nlmsg_new(approxlen + 100, gfp);
|
||||||
|
if (!skb)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
hdr = nfc_hdr_put(skb, portid, seq, 0, NFC_CMD_VENDOR);
|
||||||
|
if (!hdr) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nla_put_u32(skb, NFC_ATTR_DEVICE_INDEX, dev->idx))
|
||||||
|
goto nla_put_failure;
|
||||||
|
if (nla_put_u32(skb, NFC_ATTR_VENDOR_ID, oui))
|
||||||
|
goto nla_put_failure;
|
||||||
|
if (nla_put_u32(skb, NFC_ATTR_VENDOR_SUBCMD, subcmd))
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
((void **)skb->cb)[0] = dev;
|
||||||
|
((void **)skb->cb)[1] = hdr;
|
||||||
|
|
||||||
|
return skb;
|
||||||
|
|
||||||
|
nla_put_failure:
|
||||||
|
kfree_skb(skb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sk_buff *__nfc_alloc_vendor_cmd_reply_skb(struct nfc_dev *dev,
|
||||||
|
enum nfc_attrs attr,
|
||||||
|
u32 oui, u32 subcmd,
|
||||||
|
int approxlen)
|
||||||
|
{
|
||||||
|
if (WARN_ON(!dev->cur_cmd_info))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return __nfc_alloc_vendor_cmd_skb(dev, approxlen,
|
||||||
|
dev->cur_cmd_info->snd_portid,
|
||||||
|
dev->cur_cmd_info->snd_seq, attr,
|
||||||
|
oui, subcmd, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__nfc_alloc_vendor_cmd_reply_skb);
|
||||||
|
|
||||||
|
int nfc_vendor_cmd_reply(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct nfc_dev *dev = ((void **)skb->cb)[0];
|
||||||
|
void *hdr = ((void **)skb->cb)[1];
|
||||||
|
|
||||||
|
/* clear CB data for netlink core to own from now on */
|
||||||
|
memset(skb->cb, 0, sizeof(skb->cb));
|
||||||
|
|
||||||
|
if (WARN_ON(!dev->cur_cmd_info)) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
genlmsg_end(skb, hdr);
|
||||||
|
return genlmsg_reply(skb, dev->cur_cmd_info);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(nfc_vendor_cmd_reply);
|
||||||
|
|
||||||
static const struct genl_ops nfc_genl_ops[] = {
|
static const struct genl_ops nfc_genl_ops[] = {
|
||||||
{
|
{
|
||||||
.cmd = NFC_CMD_GET_DEVICE,
|
.cmd = NFC_CMD_GET_DEVICE,
|
||||||
|
|
Loading…
Add table
Reference in a new issue