From fe202fe95564023223ce1910c9e352f391abb1d5 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Fri, 14 Aug 2015 22:33:40 +0200 Subject: nfc: netlink: Add check on NFC_ATTR_VENDOR_DATA NFC_ATTR_VENDOR_DATA is an optional vendor_cmd argument. The current code was potentially using a non existing argument leading to potential catastrophic results. Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/netlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index f85f37ed19b2..81dfaaacfc4d 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1518,8 +1518,8 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb, if (!dev || !dev->vendor_cmds || !dev->n_vendor_cmds) return -ENODEV; - data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]); - if (data) { + if (info->attrs[NFC_ATTR_VENDOR_DATA]) { + data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]); data_len = nla_len(info->attrs[NFC_ATTR_VENDOR_DATA]); if (data_len == 0) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From adca3c38d807b341a965d0aba8721d0784d8471b Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Mon, 17 Aug 2015 08:33:43 +0200 Subject: nfc: netlink: Warning fix When NFC_ATTR_VENDOR_DATA is not set, data_len is 0 and data is NULL. Fixes the following warning: net/nfc/netlink.c:1536:3: warning: 'data' may be used uninitialized +in this function [-Wmaybe-uninitialized] return cmd->doit(dev, data, data_len); Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/netlink.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 81dfaaacfc4d..73d1ca7c546c 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1524,6 +1524,7 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb, if (data_len == 0) return -EINVAL; } else { + data = NULL; data_len = 0; } -- cgit v1.2.3-59-g8ed1b From fdf79bd48876812acf0de58ed7a8bc1b3a3c67d6 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Thu, 20 Aug 2015 17:26:00 +0200 Subject: NFC: nci: Add post_setup handler Some drivers require non-standard configuration after NCI_CORE_INIT request, because they need to know ndev->manufact_specific_info or ndev->manufact_id. This patch adds post_setup handler allowing to do such custom configuration. Signed-off-by: Robert Baldyga Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 01fc8c531115..1bdaa5f51107 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -79,6 +79,7 @@ struct nci_ops { int (*close)(struct nci_dev *ndev); int (*send)(struct nci_dev *ndev, struct sk_buff *skb); int (*setup)(struct nci_dev *ndev); + int (*post_setup)(struct nci_dev *ndev); int (*fw_download)(struct nci_dev *ndev, const char *firmware_name); __u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol); int (*discover_se)(struct nci_dev *ndev); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 95af2d24d5be..d9045ec172e3 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -388,6 +388,10 @@ static int nci_open_device(struct nci_dev *ndev) msecs_to_jiffies(NCI_INIT_TIMEOUT)); } + if (ndev->ops->post_setup) { + rc = ndev->ops->post_setup(ndev); + } + if (!rc) { rc = __nci_request(ndev, nci_init_complete_req, 0, msecs_to_jiffies(NCI_INIT_TIMEOUT)); -- cgit v1.2.3-59-g8ed1b From 025a0cb8380b7100d39fb426db9192b6c59595dc Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Thu, 20 Aug 2015 17:26:01 +0200 Subject: NFC: nci: export nci_core_reset and nci_core_init Some drivers needs to have ability to reinit NCI core, for example after updating firmware in setup() of post_setup() callback. This patch makes nci_core_reset() and nci_core_init() functions public, to make it possible. Signed-off-by: Robert Baldyga Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 2 ++ net/nfc/nci/core.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 1bdaa5f51107..d0d0f1e53bb9 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -278,6 +278,8 @@ int nci_request(struct nci_dev *ndev, unsigned long opt), unsigned long opt, __u32 timeout); int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload); +int nci_core_reset(struct nci_dev *ndev); +int nci_core_init(struct nci_dev *ndev); int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index d9045ec172e3..943889b87a34 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -351,6 +351,20 @@ int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload) } EXPORT_SYMBOL(nci_prop_cmd); +int nci_core_reset(struct nci_dev *ndev) +{ + return __nci_request(ndev, nci_reset_req, 0, + msecs_to_jiffies(NCI_RESET_TIMEOUT)); +} +EXPORT_SYMBOL(nci_core_reset); + +int nci_core_init(struct nci_dev *ndev) +{ + return __nci_request(ndev, nci_init_req, 0, + msecs_to_jiffies(NCI_INIT_TIMEOUT)); +} +EXPORT_SYMBOL(nci_core_init); + static int nci_open_device(struct nci_dev *ndev) { int rc = 0; -- cgit v1.2.3-59-g8ed1b From 5a9e0ffc0f128ecdf7c770f76c268e4f9f3c9118 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 19 Aug 2015 21:26:42 +0200 Subject: nfc: nci: hci: Add check on skb nci_hci_send_cmd parameter skb can be NULL and may lead to a NULL pointer error. Add a check condition before setting HCI rx buffer. Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index af002df640c7..609f92283d1b 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -233,7 +233,7 @@ int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, msecs_to_jiffies(NCI_DATA_TIMEOUT)); - if (r == NCI_STATUS_OK) + if (r == NCI_STATUS_OK && skb) *skb = conn_info->rx_skb; return r; -- cgit v1.2.3-59-g8ed1b From 29e76924cf087bc6a9114a9244828fd13ae959bb Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 19 Aug 2015 21:26:43 +0200 Subject: 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 Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 41 ++++++++++++++++++++++++ net/nfc/netlink.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index f9e58ae45f9c..30afc9a6718c 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -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 */ diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 73d1ca7c546c..853172c27f68 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -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, -- cgit v1.2.3-59-g8ed1b