Commit 482333b2 authored by Thierry Escande's avatar Thierry Escande Committed by Samuel Ortiz

NFC: digital: Fix ACK & NACK PDUs handling in target mode

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: default avatarThierry Escande <thierry.escande@collabora.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent f23a9868
...@@ -1141,49 +1141,64 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, ...@@ -1141,49 +1141,64 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
rc = 0; rc = 0;
break; break;
case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */ if (DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* NACK */
if ((ddev->atn_count && if (DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
(DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) != ddev->curr_nfc_dep_pni) {
ddev->curr_nfc_dep_pni)) ||
(DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
ddev->curr_nfc_dep_pni) ||
!ddev->chaining_skb || !ddev->saved_skb) {
rc = -EIO; rc = -EIO;
goto exit; 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); rc = digital_tg_send_saved_skb(ddev);
if (rc) if (rc)
goto exit; goto exit;
return; goto free_resp;
} }
kfree_skb(ddev->saved_skb); /* Otherwise, the target did not receive the previous
ddev->saved_skb = NULL; * ACK PDU from the initiator. Fallback to normal
* processing of chained PDU then.
*/
}
rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb); /* Keep on sending chained PDU */
if (rc) if (!ddev->chaining_skb ||
goto exit; DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
} else { /* NACK */ ddev->curr_nfc_dep_pni) {
if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) != rc = -EIO;
ddev->curr_nfc_dep_pni) || goto exit;
!ddev->saved_skb) { }
rc = -EIO;
goto exit;
}
ddev->atn_count = 0; kfree_skb(ddev->saved_skb);
ddev->saved_skb = NULL;
rc = digital_tg_send_saved_skb(ddev); rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
if (rc) if (rc)
goto exit; goto exit;
}
return; goto free_resp;
case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
rc = -EINVAL; rc = -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment