Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+43 −28
Original line number Diff line number Diff line
@@ -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;

			rc = digital_tg_send_saved_skb(ddev);
			if (rc)
				goto exit;

				return;
			goto free_resp;
		}

			kfree_skb(ddev->saved_skb);
			ddev->saved_skb = NULL;
		/* ACK */
		if (ddev->atn_count) {
			/* The target has previously recevied one or more ATN
			 * PDUs.
			 */
			ddev->atn_count = 0;

			rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
			/* 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;
		} else { /* NACK */
			if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
						ddev->curr_nfc_dep_pni) ||
			    !ddev->saved_skb) {

				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;
		}

			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)
			goto exit;
		}

		return;
		goto free_resp;
	case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
		if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
			rc = -EINVAL;