diff options
Diffstat (limited to 'net/nfc')
-rw-r--r-- | net/nfc/hci/llc_shdlc.c | 38 |
1 files changed, 31 insertions, 7 deletions
diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index a7931c7c57f..3afde1ecd92 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -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); |