NFC: Remove and free all SEs when releasing an NFC device
[deliverable/linux.git] / net / nfc / core.c
index 6ceee8e181ca9e4f75b1745f22d8e818ebfb1cda..5b60b9ddfc8f30a56b9df1bead13d12e57163c87 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/rfkill.h>
 #include <linux/nfc.h>
 
 #include <net/genetlink.h>
@@ -43,6 +44,47 @@ DEFINE_MUTEX(nfc_devlist_mutex);
 /* NFC device ID bitmap */
 static DEFINE_IDA(nfc_index_ida);
 
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name)
+{
+       int rc = 0;
+
+       pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->dev_up) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       if (!dev->ops->fw_upload) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       dev->fw_upload_in_progress = true;
+       rc = dev->ops->fw_upload(dev, firmware_name);
+       if (rc)
+               dev->fw_upload_in_progress = false;
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+       dev->fw_upload_in_progress = false;
+
+       return nfc_genl_fw_upload_done(dev, firmware_name);
+}
+EXPORT_SYMBOL(nfc_fw_upload_done);
+
 /**
  * nfc_dev_up - turn on the NFC device
  *
@@ -58,11 +100,21 @@ int nfc_dev_up(struct nfc_dev *dev)
 
        device_lock(&dev->dev);
 
+       if (dev->rfkill && rfkill_blocked(dev->rfkill)) {
+               rc = -ERFKILL;
+               goto error;
+       }
+
        if (!device_is_registered(&dev->dev)) {
                rc = -ENODEV;
                goto error;
        }
 
+       if (dev->fw_upload_in_progress) {
+               rc = -EBUSY;
+               goto error;
+       }
+
        if (dev->dev_up) {
                rc = -EALREADY;
                goto error;
@@ -74,6 +126,13 @@ int nfc_dev_up(struct nfc_dev *dev)
        if (!rc)
                dev->dev_up = true;
 
+       /* We have to enable the device before discovering SEs */
+       if (dev->ops->discover_se) {
+               rc = dev->ops->discover_se(dev);
+               if (!rc)
+                       pr_warn("SE discovery failed\n");
+       }
+
 error:
        device_unlock(&dev->dev);
        return rc;
@@ -117,6 +176,24 @@ error:
        return rc;
 }
 
+static int nfc_rfkill_set_block(void *data, bool blocked)
+{
+       struct nfc_dev *dev = data;
+
+       pr_debug("%s blocked %d", dev_name(&dev->dev), blocked);
+
+       if (!blocked)
+               return 0;
+
+       nfc_dev_down(dev);
+
+       return 0;
+}
+
+static const struct rfkill_ops nfc_rfkill_ops = {
+       .set_block = nfc_rfkill_set_block,
+};
+
 /**
  * nfc_start_poll - start polling for nfc targets
  *
@@ -143,6 +220,11 @@ int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols)
                goto error;
        }
 
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
        if (dev->polling) {
                rc = -EBUSY;
                goto error;
@@ -678,14 +760,79 @@ inline void nfc_driver_failure(struct nfc_dev *dev, int err)
 }
 EXPORT_SYMBOL(nfc_driver_failure);
 
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+       struct nfc_se *se, *n;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx)
+                       return -EALREADY;
+
+       se = kzalloc(sizeof(struct nfc_se), GFP_KERNEL);
+       if (!se)
+               return -ENOMEM;
+
+       se->idx = se_idx;
+       se->type = type;
+       se->state = NFC_SE_DISABLED;
+       INIT_LIST_HEAD(&se->list);
+
+       list_add(&se->list, &dev->secure_elements);
+
+       rc = nfc_genl_se_added(dev, se_idx, type);
+       if (rc < 0) {
+               list_del(&se->list);
+               kfree(se);
+
+               return rc;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(nfc_add_se);
+
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx)
+{
+       struct nfc_se *se, *n;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx) {
+                       rc = nfc_genl_se_removed(dev, se_idx);
+                       if (rc < 0)
+                               return rc;
+
+                       list_del(&se->list);
+                       kfree(se);
+
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(nfc_remove_se);
+
 static void nfc_release(struct device *d)
 {
        struct nfc_dev *dev = to_nfc_dev(d);
+       struct nfc_se *se, *n;
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
+                       nfc_genl_se_removed(dev, se->idx);
+                       list_del(&se->list);
+                       kfree(se);
+       }
+
        kfree(dev);
 }
 
@@ -757,7 +904,6 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
  */
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
-                                   u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nfc_dev *dev;
@@ -775,10 +921,9 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
 
        dev->ops = ops;
        dev->supported_protocols = supported_protocols;
-       dev->supported_se = supported_se;
-       dev->active_se = NFC_SE_NONE;
        dev->tx_headroom = tx_headroom;
        dev->tx_tailroom = tx_tailroom;
+       INIT_LIST_HEAD(&dev->secure_elements);
 
        nfc_genl_data_init(&dev->genl_data);
 
@@ -835,6 +980,15 @@ int nfc_register_device(struct nfc_dev *dev)
                pr_debug("The userspace won't be notified that the device %s was added\n",
                         dev_name(&dev->dev));
 
+       dev->rfkill = rfkill_alloc(dev_name(&dev->dev), &dev->dev,
+                                  RFKILL_TYPE_NFC, &nfc_rfkill_ops, dev);
+       if (dev->rfkill) {
+               if (rfkill_register(dev->rfkill) < 0) {
+                       rfkill_destroy(dev->rfkill);
+                       dev->rfkill = NULL;
+               }
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(nfc_register_device);
@@ -852,6 +1006,11 @@ void nfc_unregister_device(struct nfc_dev *dev)
 
        id = dev->idx;
 
+       if (dev->rfkill) {
+               rfkill_unregister(dev->rfkill);
+               rfkill_destroy(dev->rfkill);
+       }
+
        if (dev->ops->check_presence) {
                device_lock(&dev->dev);
                dev->shutting_down = true;
This page took 0.044773 seconds and 5 git commands to generate.