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: Thierry Escande <thierry.escande@collabora.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Thierry Escande 2016-07-08 15:52:42 +02:00 committed by Samuel Ortiz
parent f23a9868b1
commit 482333b277
1 changed files with 47 additions and 32 deletions

View File

@ -1141,37 +1141,9 @@ 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) {
rc = -EIO;
goto exit;
}
if (ddev->atn_count) {
ddev->atn_count = 0;
rc = digital_tg_send_saved_skb(ddev);
if (rc)
goto exit;
return;
}
kfree_skb(ddev->saved_skb);
ddev->saved_skb = NULL;
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) {
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;
}
@ -1181,9 +1153,52 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
rc = digital_tg_send_saved_skb(ddev);
if (rc)
goto exit;
goto free_resp;
}
return;
/* 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;
goto free_resp;
}
/* Otherwise, the target did not receive the previous
* ACK PDU from the initiator. Fallback to normal
* processing of chained PDU then.
*/
}
/* Keep on sending chained PDU */
if (!ddev->chaining_skb ||
DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
ddev->curr_nfc_dep_pni) {
rc = -EIO;
goto exit;
}
kfree_skb(ddev->saved_skb);
ddev->saved_skb = NULL;
rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
if (rc)
goto exit;
goto free_resp;
case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
rc = -EINVAL;