NFC: digital: Fix ACK & NACK PDUs handling in target mode
authorThierry Escande <thierry.escande@collabora.com>
Fri, 8 Jul 2016 13:52:42 +0000 (15:52 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Sun, 10 Jul 2016 23:58:46 +0000 (01:58 +0200)
When the target receives a NACK PDU, it re-sends the last sent PDU.

ACK PDUs are received by the target as a reply from the initiator to
chained I-PDUs. There are 3 cases to handle:
- If the target has previously received 1 or more ATN PDUs and the PNI
  in the ACK PDU is equal to the target PNI - 1, then it means that the
  initiator did not received the last issued PDU from the target. In
  this case it re-sends this PDU.
- If the target has received 1 or more ATN PDUs but the ACK PNI is not
  the target PNI - 1, then this means that this ACK is the reply of the
  previous chained I-PDU sent by the target. The target did not received
  it on the first attempt and it is being re-sent by the initiator. The
  process continues as usual.
- No ATN PDU received before this ACK PDU. This is the reply of a
  chained I-PDU. The target keeps on processing its chained I-PDU.

The code has been refactored to avoid too many indentation levels.

Also, ACK and NACK PDUs were not freed. This is now fixed.

Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
net/nfc/digital_dep.c

index 1778c23..e026877 100644 (file)
@@ -1141,49 +1141,64 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
                rc = 0;
                break;
        case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
-               if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */
-                       if ((ddev->atn_count &&
-                            (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
-                                               ddev->curr_nfc_dep_pni)) ||
-                           (DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
-                                               ddev->curr_nfc_dep_pni) ||
-                           !ddev->chaining_skb || !ddev->saved_skb) {
+               if (DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* NACK */
+                       if (DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
+                                               ddev->curr_nfc_dep_pni) {
                                rc = -EIO;
                                goto exit;
                        }
 
-                       if (ddev->atn_count) {
-                               ddev->atn_count = 0;
+                       ddev->atn_count = 0;
+
+                       rc = digital_tg_send_saved_skb(ddev);
+                       if (rc)
+                               goto exit;
+
+                       goto free_resp;
+               }
+
+               /* ACK */
+               if (ddev->atn_count) {
+                       /* The target has previously recevied one or more ATN
+                        * PDUs.
+                        */
+                       ddev->atn_count = 0;
 
+                       /* If the ACK PNI is equal to the target PNI - 1 means
+                        * that the initiator did not receive the previous PDU
+                        * sent by the target so re-send it.
+                        */
+                       if (DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) ==
+                                               ddev->curr_nfc_dep_pni) {
                                rc = digital_tg_send_saved_skb(ddev);
                                if (rc)
                                        goto exit;
 
-                               return;
+                               goto free_resp;
                        }
 
-                       kfree_skb(ddev->saved_skb);
-                       ddev->saved_skb = NULL;
+                       /* Otherwise, the target did not receive the previous
+                        * ACK PDU from the initiator. Fallback to normal
+                        * processing of chained PDU then.
+                        */
+               }
 
-                       rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
-                       if (rc)
-                               goto exit;
-               } else { /* NACK */
-                       if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
-                                               ddev->curr_nfc_dep_pni) ||
-                           !ddev->saved_skb) {
-                               rc = -EIO;
-                               goto exit;
-                       }
+               /* Keep on sending chained PDU */
+               if (!ddev->chaining_skb ||
+                   DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
+                                       ddev->curr_nfc_dep_pni) {
+                       rc = -EIO;
+                       goto exit;
+               }
 
-                       ddev->atn_count = 0;
+               kfree_skb(ddev->saved_skb);
+               ddev->saved_skb = NULL;
 
-                       rc = digital_tg_send_saved_skb(ddev);
-                       if (rc)
-                               goto exit;
-               }
+               rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
+               if (rc)
+                       goto exit;
 
-               return;
+               goto free_resp;
        case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
                if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
                        rc = -EINVAL;