These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / nfc / netlink.c
index 3763036..f58c1fb 100644 (file)
@@ -5,6 +5,12 @@
  *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
  *    Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
  *
+ * Vendor commands implementation based on net/wireless/nl80211.c
+ * which is:
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014  Intel Mobile Communications GmbH
+ *
  * 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
@@ -57,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] = {
@@ -877,7 +885,7 @@ static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
        target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
        protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
 
-       nfc_deactivate_target(dev, target_idx);
+       nfc_deactivate_target(dev, target_idx, NFC_TARGET_MODE_SLEEP);
        rc = nfc_activate_target(dev, target_idx, protocol);
 
        nfc_put_device(dev);
@@ -1101,10 +1109,8 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
        idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
 
        dev = nfc_get_device(idx);
-       if (!dev) {
-               rc = -ENODEV;
-               goto exit;
-       }
+       if (!dev)
+               return -ENODEV;
 
        device_lock(&dev->dev);
 
@@ -1489,6 +1495,131 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
        return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
 }
 
+static int nfc_genl_vendor_cmd(struct sk_buff *skb,
+                              struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       struct nfc_vendor_cmd *cmd;
+       u32 dev_idx, vid, subcmd;
+       u8 *data;
+       size_t data_len;
+       int i, err;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_VENDOR_ID] ||
+           !info->attrs[NFC_ATTR_VENDOR_SUBCMD])
+               return -EINVAL;
+
+       dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       vid = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_ID]);
+       subcmd = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_SUBCMD]);
+
+       dev = nfc_get_device(dev_idx);
+       if (!dev || !dev->vendor_cmds || !dev->n_vendor_cmds)
+               return -ENODEV;
+
+       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;
+       } else {
+               data = NULL;
+               data_len = 0;
+       }
+
+       for (i = 0; i < dev->n_vendor_cmds; i++) {
+               cmd = &dev->vendor_cmds[i];
+
+               if (cmd->vendor_id != vid || cmd->subcmd != subcmd)
+                       continue;
+
+               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,
@@ -1579,6 +1710,11 @@ static const struct genl_ops nfc_genl_ops[] = {
                .doit = nfc_genl_activate_target,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_VENDOR,
+               .doit = nfc_genl_vendor_cmd,
+               .policy = nfc_genl_policy,
+       },
 };