NFC: Don't handle consequent RSET frames after UA
authorWaldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Fri, 21 Sep 2012 12:02:46 +0000 (14:02 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 24 Sep 2012 22:17:27 +0000 (00:17 +0200)
During processing incoming RSET frame chip, possibly due to
its internal timout, can retrnasmit an another RSET which
is next queued for processing in shdlc layer.

In case when we accept processed RSET skip those remaining on
the rcv queue until chip will send it's first S or I frame.
This will mean the chip completed connection as well.

Signed-off-by: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
net/nfc/hci/llc_shdlc.c

index a7931c7c57f27419145768b5332d0a2b0a5dce41..3afde1ecd925506a489526bc3b1764630c319772 100644 (file)
@@ -32,7 +32,8 @@ enum shdlc_state {
        SHDLC_DISCONNECTED = 0,
        SHDLC_CONNECTING = 1,
        SHDLC_NEGOCIATING = 2,
-       SHDLC_CONNECTED = 3
+       SHDLC_HALF_CONNECTED = 3,
+       SHDLC_CONNECTED = 4
 };
 
 struct llc_shdlc {
@@ -363,7 +364,7 @@ static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r)
                shdlc->nr = 0;
                shdlc->dnr = 0;
 
-               shdlc->state = SHDLC_CONNECTED;
+               shdlc->state = SHDLC_HALF_CONNECTED;
        } else {
                shdlc->state = SHDLC_DISCONNECTED;
        }
@@ -414,9 +415,13 @@ static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc,
 
        switch (u_frame_modifier) {
        case U_FRAME_RSET:
-               if ((shdlc->state == SHDLC_NEGOCIATING) ||
-                                       (shdlc->state == SHDLC_CONNECTING)) {
-                       /* we sent RSET, but chip wants to negociate */
+               switch (shdlc->state) {
+               case SHDLC_NEGOCIATING:
+               case SHDLC_CONNECTING:
+                       /*
+                        * We sent RSET, but chip wants to negociate or we
+                        * got RSET before we managed to send out our.
+                        */
                        if (skb->len > 0)
                                w = skb->data[0];
 
@@ -431,19 +436,31 @@ static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc,
                                r = llc_shdlc_connect_send_ua(shdlc);
                                llc_shdlc_connect_complete(shdlc, r);
                        }
-               } else if (shdlc->state == SHDLC_CONNECTED) {
+                       break;
+               case SHDLC_HALF_CONNECTED:
+                       /*
+                        * Chip resent RSET due to its timeout - Ignote it
+                        * as we already sent UA.
+                        */
+                       break;
+               case SHDLC_CONNECTED:
                        /*
                         * Chip wants to reset link. This is unexpected and
                         * unsupported.
                         */
                        shdlc->hard_fault = -ECONNRESET;
+                       break;
+               default:
+                       break;
                }
                break;
        case U_FRAME_UA:
                if ((shdlc->state == SHDLC_CONNECTING &&
                     shdlc->connect_tries > 0) ||
-                   (shdlc->state == SHDLC_NEGOCIATING))
+                   (shdlc->state == SHDLC_NEGOCIATING)) {
                        llc_shdlc_connect_complete(shdlc, 0);
+                       shdlc->state = SHDLC_CONNECTED;
+               }
                break;
        default:
                break;
@@ -470,11 +487,17 @@ static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc)
                switch (control & SHDLC_CONTROL_HEAD_MASK) {
                case SHDLC_CONTROL_HEAD_I:
                case SHDLC_CONTROL_HEAD_I2:
+                       if (shdlc->state == SHDLC_HALF_CONNECTED)
+                               shdlc->state = SHDLC_CONNECTED;
+
                        ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
                        nr = control & SHDLC_CONTROL_NR_MASK;
                        llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
                        break;
                case SHDLC_CONTROL_HEAD_S:
+                       if (shdlc->state == SHDLC_HALF_CONNECTED)
+                               shdlc->state = SHDLC_CONNECTED;
+
                        s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
                        nr = control & SHDLC_CONTROL_NR_MASK;
                        llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
@@ -633,6 +656,7 @@ static void llc_shdlc_sm_work(struct work_struct *work)
                        break;
                }
                break;
+       case SHDLC_HALF_CONNECTED:
        case SHDLC_CONNECTED:
                llc_shdlc_handle_rcv_queue(shdlc);
                llc_shdlc_handle_send_queue(shdlc);