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
|
@ -203,6 +203,7 @@ struct nfc_dev {
|
|||
int n_vendor_cmds;
|
||||
|
||||
struct nfc_ops *ops;
|
||||
struct genl_info *cur_cmd_info;
|
||||
};
|
||||
#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;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
|
|
@ -63,6 +63,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
|
|||
[NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
|
||||
.len = NFC_FIRMWARE_NAME_MAXSIZE },
|
||||
[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] = {
|
||||
|
@ -1503,7 +1505,7 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb,
|
|||
u32 dev_idx, vid, subcmd;
|
||||
u8 *data;
|
||||
size_t data_len;
|
||||
int i;
|
||||
int i, err;
|
||||
|
||||
if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
|
||||
!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)
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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[] = {
|
||||
{
|
||||
.cmd = NFC_CMD_GET_DEVICE,
|
||||
|
|
Loading…
Reference in New Issue