NFC: Introduce target mode tx ops
[deliverable/linux.git] / net / nfc / core.c
index 3192c3f589eea87eb5f6e04f4803b584c36101fb..76c1e207d297d0cb9107537986e558d39a3759da 100644 (file)
@@ -97,7 +97,7 @@ int nfc_dev_down(struct nfc_dev *dev)
                goto error;
        }
 
-       if (dev->polling || dev->activated_target_idx != NFC_TARGET_IDX_NONE) {
+       if (dev->polling || dev->active_target) {
                rc = -EBUSY;
                goto error;
        }
@@ -121,14 +121,14 @@ error:
  * The device remains polling for targets until a target is found or
  * the nfc_stop_poll function is called.
  */
-int nfc_start_poll(struct nfc_dev *dev, u32 protocols)
+int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols)
 {
        int rc;
 
-       pr_debug("dev_name=%s protocols=0x%x\n",
-                dev_name(&dev->dev), protocols);
+       pr_debug("dev_name %s initiator protocols 0x%x target protocols 0x%x\n",
+                dev_name(&dev->dev), im_protocols, tm_protocols);
 
-       if (!protocols)
+       if (!im_protocols && !tm_protocols)
                return -EINVAL;
 
        device_lock(&dev->dev);
@@ -143,9 +143,11 @@ int nfc_start_poll(struct nfc_dev *dev, u32 protocols)
                goto error;
        }
 
-       rc = dev->ops->start_poll(dev, protocols);
-       if (!rc)
+       rc = dev->ops->start_poll(dev, im_protocols, tm_protocols);
+       if (!rc) {
                dev->polling = true;
+               dev->rf_mode = NFC_RF_NONE;
+       }
 
 error:
        device_unlock(&dev->dev);
@@ -183,11 +185,27 @@ error:
        return rc;
 }
 
+static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)
+{
+       int i;
+
+       if (dev->n_targets == 0)
+               return NULL;
+
+       for (i = 0; i < dev->n_targets ; i++) {
+               if (dev->targets[i].idx == target_idx)
+                       return &dev->targets[i];
+       }
+
+       return NULL;
+}
+
 int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
 {
        int rc = 0;
        u8 *gb;
        size_t gb_len;
+       struct nfc_target *target;
 
        pr_debug("dev_name=%s comm %d\n", dev_name(&dev->dev), comm_mode);
 
@@ -212,9 +230,17 @@ int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
                goto error;
        }
 
-       rc = dev->ops->dep_link_up(dev, target_index, comm_mode, gb, gb_len);
-       if (!rc)
-               dev->activated_target_idx = target_index;
+       target = nfc_find_target(dev, target_index);
+       if (target == NULL) {
+               rc = -ENOTCONN;
+               goto error;
+       }
+
+       rc = dev->ops->dep_link_up(dev, target, comm_mode, gb, gb_len);
+       if (!rc) {
+               dev->active_target = target;
+               dev->rf_mode = NFC_RF_INITIATOR;
+       }
 
 error:
        device_unlock(&dev->dev);
@@ -242,7 +268,7 @@ int nfc_dep_link_down(struct nfc_dev *dev)
                goto error;
        }
 
-       if (dev->dep_rf_mode == NFC_RF_TARGET) {
+       if (dev->rf_mode == NFC_RF_TARGET) {
                rc = -EOPNOTSUPP;
                goto error;
        }
@@ -250,7 +276,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;
+               dev->active_target = NULL;
                nfc_llcp_mac_is_down(dev);
                nfc_genl_dep_link_down_event(dev);
        }
@@ -264,7 +290,6 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,
                       u8 comm_mode, u8 rf_mode)
 {
        dev->dep_link_up = true;
-       dev->dep_rf_mode = rf_mode;
 
        nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode);
 
@@ -282,6 +307,7 @@ EXPORT_SYMBOL(nfc_dep_link_is_up);
 int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
 {
        int rc;
+       struct nfc_target *target;
 
        pr_debug("dev_name=%s target_idx=%u protocol=%u\n",
                 dev_name(&dev->dev), target_idx, protocol);
@@ -293,9 +319,21 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
                goto error;
        }
 
-       rc = dev->ops->activate_target(dev, target_idx, protocol);
+       if (dev->active_target) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       target = nfc_find_target(dev, target_idx);
+       if (target == NULL) {
+               rc = -ENOTCONN;
+               goto error;
+       }
+
+       rc = dev->ops->activate_target(dev, target, protocol);
        if (!rc) {
-               dev->activated_target_idx = target_idx;
+               dev->active_target = target;
+               dev->rf_mode = NFC_RF_INITIATOR;
 
                if (dev->ops->check_presence)
                        mod_timer(&dev->check_pres_timer, jiffies +
@@ -327,11 +365,21 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
                goto error;
        }
 
+       if (dev->active_target == NULL) {
+               rc = -ENOTCONN;
+               goto error;
+       }
+
+       if (dev->active_target->idx != target_idx) {
+               rc = -ENOTCONN;
+               goto error;
+       }
+
        if (dev->ops->check_presence)
                del_timer_sync(&dev->check_pres_timer);
 
-       dev->ops->deactivate_target(dev, target_idx);
-       dev->activated_target_idx = NFC_TARGET_IDX_NONE;
+       dev->ops->deactivate_target(dev, dev->active_target);
+       dev->active_target = NULL;
 
 error:
        device_unlock(&dev->dev);
@@ -365,26 +413,30 @@ 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 (dev->rf_mode == NFC_RF_INITIATOR && dev->active_target != NULL) {
+               if (dev->active_target->idx != target_idx) {
+                       rc = -EADDRNOTAVAIL;
+                       kfree_skb(skb);
+                       goto error;
+               }
+
+               if (dev->ops->check_presence)
+                       del_timer_sync(&dev->check_pres_timer);
 
-       if (target_idx != dev->activated_target_idx) {
-               rc = -EADDRNOTAVAIL;
+               rc = dev->ops->im_transceive(dev, dev->active_target, 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));
+       } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) {
+               rc = dev->ops->tm_send(dev, skb);
+       } else {
+               rc = -ENOTCONN;
                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);
@@ -402,6 +454,51 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)
 }
 EXPORT_SYMBOL(nfc_set_remote_general_bytes);
 
+u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len)
+{
+       pr_debug("dev_name=%s\n", dev_name(&dev->dev));
+
+       return nfc_llcp_general_bytes(dev, gb_len);
+}
+EXPORT_SYMBOL(nfc_get_local_general_bytes);
+
+int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode,
+                    u8 *gb, size_t gb_len)
+{
+       int rc;
+
+       device_lock(&dev->dev);
+
+       dev->polling = false;
+
+       if (gb != NULL) {
+               rc = nfc_set_remote_general_bytes(dev, gb, gb_len);
+               if (rc < 0)
+                       goto out;
+       }
+
+       dev->rf_mode = NFC_RF_TARGET;
+
+       if (protocol == NFC_PROTO_NFC_DEP_MASK)
+               nfc_dep_link_is_up(dev, 0, comm_mode, NFC_RF_TARGET);
+
+       rc = nfc_genl_tm_activated(dev, protocol);
+
+out:
+       device_unlock(&dev->dev);
+
+       return rc;
+}
+EXPORT_SYMBOL(nfc_tm_activated);
+
+int nfc_tm_deactivated(struct nfc_dev *dev)
+{
+       dev->dep_link_up = false;
+
+       return nfc_genl_tm_deactivated(dev);
+}
+EXPORT_SYMBOL(nfc_tm_deactivated);
+
 /**
  * nfc_alloc_send_skb - allocate a skb for data exchange responses
  *
@@ -456,6 +553,9 @@ EXPORT_SYMBOL(nfc_alloc_recv_skb);
  * The device driver must call this function when one or many nfc targets
  * are found. After calling this function, the device driver must stop
  * polling for targets.
+ * IMPORTANT: this function must not be called from an atomic context.
+ * In addition, it must also not be called from a context that would prevent
+ * the NFC Core to call other nfc ops entry point concurrently.
  */
 int nfc_targets_found(struct nfc_dev *dev,
                      struct nfc_target *targets, int n_targets)
@@ -469,7 +569,7 @@ int nfc_targets_found(struct nfc_dev *dev,
        for (i = 0; i < n_targets; i++)
                targets[i].idx = dev->target_next_idx++;
 
-       spin_lock_bh(&dev->targets_lock);
+       device_lock(&dev->dev);
 
        dev->targets_generation++;
 
@@ -479,12 +579,12 @@ int nfc_targets_found(struct nfc_dev *dev,
 
        if (!dev->targets) {
                dev->n_targets = 0;
-               spin_unlock_bh(&dev->targets_lock);
+               device_unlock(&dev->dev);
                return -ENOMEM;
        }
 
        dev->n_targets = n_targets;
-       spin_unlock_bh(&dev->targets_lock);
+       device_unlock(&dev->dev);
 
        nfc_genl_targets_found(dev);
 
@@ -492,6 +592,18 @@ int nfc_targets_found(struct nfc_dev *dev,
 }
 EXPORT_SYMBOL(nfc_targets_found);
 
+/**
+ * nfc_target_lost - inform that an activated target went out of field
+ *
+ * @dev: The nfc device that had the activated target in field
+ * @target_idx: the nfc index of the target
+ *
+ * The device driver must call this function when the activated target
+ * goes out of the field.
+ * IMPORTANT: this function must not be called from an atomic context.
+ * In addition, it must also not be called from a context that would prevent
+ * the NFC Core to call other nfc ops entry point concurrently.
+ */
 int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
 {
        struct nfc_target *tg;
@@ -499,7 +611,7 @@ int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
 
        pr_debug("dev_name %s n_target %d\n", dev_name(&dev->dev), target_idx);
 
-       spin_lock_bh(&dev->targets_lock);
+       device_lock(&dev->dev);
 
        for (i = 0; i < dev->n_targets; i++) {
                tg = &dev->targets[i];
@@ -508,13 +620,13 @@ int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
        }
 
        if (i == dev->n_targets) {
-               spin_unlock_bh(&dev->targets_lock);
+               device_unlock(&dev->dev);
                return -EINVAL;
        }
 
        dev->targets_generation++;
        dev->n_targets--;
-       dev->activated_target_idx = NFC_TARGET_IDX_NONE;
+       dev->active_target = NULL;
 
        if (dev->n_targets) {
                memcpy(&dev->targets[i], &dev->targets[i + 1],
@@ -524,7 +636,7 @@ int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
                dev->targets = NULL;
        }
 
-       spin_unlock_bh(&dev->targets_lock);
+       device_unlock(&dev->dev);
 
        nfc_genl_target_lost(dev, target_idx);
 
@@ -556,15 +668,16 @@ static void nfc_check_pres_work(struct work_struct *work)
 
        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 (dev->active_target && timer_pending(&dev->check_pres_timer) == 0) {
+               rc = dev->ops->check_presence(dev, dev->active_target);
                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;
+                       u32 active_target_idx = dev->active_target->idx;
+                       device_unlock(&dev->dev);
+                       nfc_target_lost(dev, active_target_idx);
+                       return;
                }
        }
 
@@ -617,7 +730,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
        struct nfc_dev *dev;
 
        if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
-           !ops->deactivate_target || !ops->data_exchange)
+           !ops->deactivate_target || !ops->im_transceive)
                return NULL;
 
        if (!supported_protocols)
@@ -637,14 +750,12 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
        dev->tx_headroom = tx_headroom;
        dev->tx_tailroom = tx_tailroom;
 
-       spin_lock_init(&dev->targets_lock);
        nfc_genl_data_init(&dev->genl_data);
 
+
        /* 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);
@@ -662,7 +773,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                }
        }
 
-
        return dev;
 }
 EXPORT_SYMBOL(nfc_allocate_device);
This page took 0.041533 seconds and 5 git commands to generate.