NFC: Don't handle consequent RSET frames after UA
[cascardo/linux.git] / net / nfc / hci / llc_shdlc.c
index a7931c7..3afde1e 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);