Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[deliverable/linux.git] / net / nfc / core.c
index 32a7b615e65fdbce82a7a3027f065e6b84084198..3192c3f589eea87eb5f6e04f4803b584c36101fb 100644 (file)
@@ -33,6 +33,8 @@
 
 #define VERSION "0.1"
 
+#define NFC_CHECK_PRES_FREQ_MS 2000
+
 int nfc_devlist_generation;
 DEFINE_MUTEX(nfc_devlist_mutex);
 
@@ -95,7 +97,7 @@ int nfc_dev_down(struct nfc_dev *dev)
                goto error;
        }
 
-       if (dev->polling || dev->remote_activated) {
+       if (dev->polling || dev->activated_target_idx != NFC_TARGET_IDX_NONE) {
                rc = -EBUSY;
                goto error;
        }
@@ -211,6 +213,8 @@ int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
        }
 
        rc = dev->ops->dep_link_up(dev, target_index, comm_mode, gb, gb_len);
+       if (!rc)
+               dev->activated_target_idx = target_index;
 
 error:
        device_unlock(&dev->dev);
@@ -246,6 +250,7 @@ int nfc_dep_link_down(struct nfc_dev *dev)
        rc = dev->ops->dep_link_down(dev);
        if (!rc) {
                dev->dep_link_up = false;
+               dev->activated_target_idx = NFC_TARGET_IDX_NONE;
                nfc_llcp_mac_is_down(dev);
                nfc_genl_dep_link_down_event(dev);
        }
@@ -289,8 +294,13 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
        }
 
        rc = dev->ops->activate_target(dev, target_idx, protocol);
-       if (!rc)
-               dev->remote_activated = true;
+       if (!rc) {
+               dev->activated_target_idx = target_idx;
+
+               if (dev->ops->check_presence)
+                       mod_timer(&dev->check_pres_timer, jiffies +
+                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
+       }
 
 error:
        device_unlock(&dev->dev);
@@ -317,8 +327,11 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
                goto error;
        }
 
+       if (dev->ops->check_presence)
+               del_timer_sync(&dev->check_pres_timer);
+
        dev->ops->deactivate_target(dev, target_idx);
-       dev->remote_activated = false;
+       dev->activated_target_idx = NFC_TARGET_IDX_NONE;
 
 error:
        device_unlock(&dev->dev);
@@ -352,8 +365,27 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                goto error;
        }
 
+       if (dev->activated_target_idx == NFC_TARGET_IDX_NONE) {
+               rc = -ENOTCONN;
+               kfree_skb(skb);
+               goto error;
+       }
+
+       if (target_idx != dev->activated_target_idx) {
+               rc = -EADDRNOTAVAIL;
+               kfree_skb(skb);
+               goto error;
+       }
+
+       if (dev->ops->check_presence)
+               del_timer_sync(&dev->check_pres_timer);
+
        rc = dev->ops->data_exchange(dev, target_idx, skb, cb, cb_context);
 
+       if (!rc && dev->ops->check_presence)
+               mod_timer(&dev->check_pres_timer, jiffies +
+                         msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
+
 error:
        device_unlock(&dev->dev);
        return rc;
@@ -428,10 +460,15 @@ EXPORT_SYMBOL(nfc_alloc_recv_skb);
 int nfc_targets_found(struct nfc_dev *dev,
                      struct nfc_target *targets, int n_targets)
 {
+       int i;
+
        pr_debug("dev_name=%s n_targets=%d\n", dev_name(&dev->dev), n_targets);
 
        dev->polling = false;
 
+       for (i = 0; i < n_targets; i++)
+               targets[i].idx = dev->target_next_idx++;
+
        spin_lock_bh(&dev->targets_lock);
 
        dev->targets_generation++;
@@ -455,17 +492,92 @@ int nfc_targets_found(struct nfc_dev *dev,
 }
 EXPORT_SYMBOL(nfc_targets_found);
 
+int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
+{
+       struct nfc_target *tg;
+       int i;
+
+       pr_debug("dev_name %s n_target %d\n", dev_name(&dev->dev), target_idx);
+
+       spin_lock_bh(&dev->targets_lock);
+
+       for (i = 0; i < dev->n_targets; i++) {
+               tg = &dev->targets[i];
+               if (tg->idx == target_idx)
+                       break;
+       }
+
+       if (i == dev->n_targets) {
+               spin_unlock_bh(&dev->targets_lock);
+               return -EINVAL;
+       }
+
+       dev->targets_generation++;
+       dev->n_targets--;
+       dev->activated_target_idx = NFC_TARGET_IDX_NONE;
+
+       if (dev->n_targets) {
+               memcpy(&dev->targets[i], &dev->targets[i + 1],
+                      (dev->n_targets - i) * sizeof(struct nfc_target));
+       } else {
+               kfree(dev->targets);
+               dev->targets = NULL;
+       }
+
+       spin_unlock_bh(&dev->targets_lock);
+
+       nfc_genl_target_lost(dev, target_idx);
+
+       return 0;
+}
+EXPORT_SYMBOL(nfc_target_lost);
+
 static void nfc_release(struct device *d)
 {
        struct nfc_dev *dev = to_nfc_dev(d);
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
+       if (dev->ops->check_presence) {
+               del_timer_sync(&dev->check_pres_timer);
+               destroy_workqueue(dev->check_pres_wq);
+       }
+
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
        kfree(dev);
 }
 
+static void nfc_check_pres_work(struct work_struct *work)
+{
+       struct nfc_dev *dev = container_of(work, struct nfc_dev,
+                                          check_pres_work);
+       int rc;
+
+       device_lock(&dev->dev);
+
+       if (dev->activated_target_idx != NFC_TARGET_IDX_NONE &&
+           timer_pending(&dev->check_pres_timer) == 0) {
+               rc = dev->ops->check_presence(dev, dev->activated_target_idx);
+               if (!rc) {
+                       mod_timer(&dev->check_pres_timer, jiffies +
+                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
+               } else {
+                       nfc_target_lost(dev, dev->activated_target_idx);
+                       dev->activated_target_idx = NFC_TARGET_IDX_NONE;
+               }
+       }
+
+       device_unlock(&dev->dev);
+}
+
+static void nfc_check_pres_timeout(unsigned long data)
+{
+       struct nfc_dev *dev = (struct nfc_dev *)data;
+
+       queue_work(dev->check_pres_wq, &dev->check_pres_work);
+}
+
 struct class nfc_class = {
        .name = "nfc",
        .dev_release = nfc_release,
@@ -531,6 +643,26 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
        /* first generation must not be 0 */
        dev->targets_generation = 1;
 
+       dev->activated_target_idx = NFC_TARGET_IDX_NONE;
+
+       if (ops->check_presence) {
+               char name[32];
+               init_timer(&dev->check_pres_timer);
+               dev->check_pres_timer.data = (unsigned long)dev;
+               dev->check_pres_timer.function = nfc_check_pres_timeout;
+
+               INIT_WORK(&dev->check_pres_work, nfc_check_pres_work);
+               snprintf(name, sizeof(name), "nfc%d_check_pres_wq", dev->idx);
+               dev->check_pres_wq = alloc_workqueue(name, WQ_NON_REENTRANT |
+                                                    WQ_UNBOUND |
+                                                    WQ_MEM_RECLAIM, 1);
+               if (dev->check_pres_wq == NULL) {
+                       kfree(dev);
+                       return NULL;
+               }
+       }
+
+
        return dev;
 }
 EXPORT_SYMBOL(nfc_allocate_device);
This page took 0.03215 seconds and 5 git commands to generate.