162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * shdlc Link Layer Control 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Intel Corporation. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/wait.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/skbuff.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "llc.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cienum shdlc_state { 1962306a36Sopenharmony_ci SHDLC_DISCONNECTED = 0, 2062306a36Sopenharmony_ci SHDLC_CONNECTING = 1, 2162306a36Sopenharmony_ci SHDLC_NEGOTIATING = 2, 2262306a36Sopenharmony_ci SHDLC_HALF_CONNECTED = 3, 2362306a36Sopenharmony_ci SHDLC_CONNECTED = 4 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct llc_shdlc { 2762306a36Sopenharmony_ci struct nfc_hci_dev *hdev; 2862306a36Sopenharmony_ci xmit_to_drv_t xmit_to_drv; 2962306a36Sopenharmony_ci rcv_to_hci_t rcv_to_hci; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci struct mutex state_mutex; 3262306a36Sopenharmony_ci enum shdlc_state state; 3362306a36Sopenharmony_ci int hard_fault; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci wait_queue_head_t *connect_wq; 3662306a36Sopenharmony_ci int connect_tries; 3762306a36Sopenharmony_ci int connect_result; 3862306a36Sopenharmony_ci struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci u8 w; /* window size */ 4162306a36Sopenharmony_ci bool srej_support; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci struct timer_list t1_timer; /* send ack timeout */ 4462306a36Sopenharmony_ci bool t1_active; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci struct timer_list t2_timer; /* guard/retransmit timeout */ 4762306a36Sopenharmony_ci bool t2_active; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci int ns; /* next seq num for send */ 5062306a36Sopenharmony_ci int nr; /* next expected seq num for receive */ 5162306a36Sopenharmony_ci int dnr; /* oldest sent unacked seq num */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci struct sk_buff_head rcv_q; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci struct sk_buff_head send_q; 5662306a36Sopenharmony_ci bool rnr; /* other side is not ready to receive */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci struct sk_buff_head ack_pending_q; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci struct work_struct sm_work; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci int tx_headroom; 6362306a36Sopenharmony_ci int tx_tailroom; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci llc_failure_t llc_failure; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define SHDLC_LLC_HEAD_ROOM 2 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define SHDLC_MAX_WINDOW 4 7162306a36Sopenharmony_ci#define SHDLC_SREJ_SUPPORT false 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define SHDLC_CONTROL_HEAD_MASK 0xe0 7462306a36Sopenharmony_ci#define SHDLC_CONTROL_HEAD_I 0x80 7562306a36Sopenharmony_ci#define SHDLC_CONTROL_HEAD_I2 0xa0 7662306a36Sopenharmony_ci#define SHDLC_CONTROL_HEAD_S 0xc0 7762306a36Sopenharmony_ci#define SHDLC_CONTROL_HEAD_U 0xe0 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define SHDLC_CONTROL_NS_MASK 0x38 8062306a36Sopenharmony_ci#define SHDLC_CONTROL_NR_MASK 0x07 8162306a36Sopenharmony_ci#define SHDLC_CONTROL_TYPE_MASK 0x18 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define SHDLC_CONTROL_M_MASK 0x1f 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cienum sframe_type { 8662306a36Sopenharmony_ci S_FRAME_RR = 0x00, 8762306a36Sopenharmony_ci S_FRAME_REJ = 0x01, 8862306a36Sopenharmony_ci S_FRAME_RNR = 0x02, 8962306a36Sopenharmony_ci S_FRAME_SREJ = 0x03 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cienum uframe_modifier { 9362306a36Sopenharmony_ci U_FRAME_UA = 0x06, 9462306a36Sopenharmony_ci U_FRAME_RSET = 0x19 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define SHDLC_CONNECT_VALUE_MS 5 9862306a36Sopenharmony_ci#define SHDLC_T1_VALUE_MS(w) ((5 * w) / 4) 9962306a36Sopenharmony_ci#define SHDLC_T2_VALUE_MS 300 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define SHDLC_DUMP_SKB(info, skb) \ 10262306a36Sopenharmony_cido { \ 10362306a36Sopenharmony_ci pr_debug("%s:\n", info); \ 10462306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \ 10562306a36Sopenharmony_ci 16, 1, skb->data, skb->len, 0); \ 10662306a36Sopenharmony_ci} while (0) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* checks x < y <= z modulo 8 */ 10962306a36Sopenharmony_cistatic bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci if (x < z) 11262306a36Sopenharmony_ci return ((x < y) && (y <= z)) ? true : false; 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci return ((y > x) || (y <= z)) ? true : false; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* checks x <= y < z modulo 8 */ 11862306a36Sopenharmony_cistatic bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (x <= z) 12162306a36Sopenharmony_ci return ((x <= y) && (y < z)) ? true : false; 12262306a36Sopenharmony_ci else /* x > z -> z+8 > x */ 12362306a36Sopenharmony_ci return ((y >= x) || (y < z)) ? true : false; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic struct sk_buff *llc_shdlc_alloc_skb(const struct llc_shdlc *shdlc, 12762306a36Sopenharmony_ci int payload_len) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct sk_buff *skb; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM + 13262306a36Sopenharmony_ci shdlc->tx_tailroom + payload_len, GFP_KERNEL); 13362306a36Sopenharmony_ci if (skb) 13462306a36Sopenharmony_ci skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return skb; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* immediately sends an S frame. */ 14062306a36Sopenharmony_cistatic int llc_shdlc_send_s_frame(const struct llc_shdlc *shdlc, 14162306a36Sopenharmony_ci enum sframe_type sframe_type, int nr) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int r; 14462306a36Sopenharmony_ci struct sk_buff *skb; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci skb = llc_shdlc_alloc_skb(shdlc, 0); 14962306a36Sopenharmony_ci if (skb == NULL) 15062306a36Sopenharmony_ci return -ENOMEM; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci *(u8 *)skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci r = shdlc->xmit_to_drv(shdlc->hdev, skb); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci kfree_skb(skb); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return r; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* immediately sends an U frame. skb may contain optional payload */ 16262306a36Sopenharmony_cistatic int llc_shdlc_send_u_frame(const struct llc_shdlc *shdlc, 16362306a36Sopenharmony_ci struct sk_buff *skb, 16462306a36Sopenharmony_ci enum uframe_modifier uframe_modifier) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int r; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci pr_debug("uframe_modifier=%d\n", uframe_modifier); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci *(u8 *)skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci r = shdlc->xmit_to_drv(shdlc->hdev, skb); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci kfree_skb(skb); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return r; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Free ack_pending frames until y_nr - 1, and reset t2 according to 18162306a36Sopenharmony_ci * the remaining oldest ack_pending frame sent time 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct sk_buff *skb; 18662306a36Sopenharmony_ci int dnr = shdlc->dnr; /* MUST initially be < y_nr */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci pr_debug("release ack pending up to frame %d excluded\n", y_nr); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci while (dnr != y_nr) { 19162306a36Sopenharmony_ci pr_debug("release ack pending frame %d\n", dnr); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci skb = skb_dequeue(&shdlc->ack_pending_q); 19462306a36Sopenharmony_ci kfree_skb(skb); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci dnr = (dnr + 1) % 8; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (skb_queue_empty(&shdlc->ack_pending_q)) { 20062306a36Sopenharmony_ci if (shdlc->t2_active) { 20162306a36Sopenharmony_ci del_timer_sync(&shdlc->t2_timer); 20262306a36Sopenharmony_ci shdlc->t2_active = false; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci pr_debug("All sent frames acked. Stopped T2(retransmit)\n"); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } else { 20762306a36Sopenharmony_ci skb = skb_peek(&shdlc->ack_pending_q); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb + 21062306a36Sopenharmony_ci msecs_to_jiffies(SHDLC_T2_VALUE_MS)); 21162306a36Sopenharmony_ci shdlc->t2_active = true; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pr_debug("Start T2(retransmit) for remaining unacked sent frames\n"); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* 21862306a36Sopenharmony_ci * Receive validated frames from lower layer. skb contains HCI payload only. 21962306a36Sopenharmony_ci * Handle according to algorithm at spec:10.8.2 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_cistatic void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc, 22262306a36Sopenharmony_ci struct sk_buff *skb, int ns, int nr) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci int x_ns = ns; 22562306a36Sopenharmony_ci int y_nr = nr; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (shdlc->state != SHDLC_CONNECTED) 23062306a36Sopenharmony_ci goto exit; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (x_ns != shdlc->nr) { 23362306a36Sopenharmony_ci llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr); 23462306a36Sopenharmony_ci goto exit; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!shdlc->t1_active) { 23862306a36Sopenharmony_ci shdlc->t1_active = true; 23962306a36Sopenharmony_ci mod_timer(&shdlc->t1_timer, jiffies + 24062306a36Sopenharmony_ci msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w))); 24162306a36Sopenharmony_ci pr_debug("(re)Start T1(send ack)\n"); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (skb->len) { 24562306a36Sopenharmony_ci shdlc->rcv_to_hci(shdlc->hdev, skb); 24662306a36Sopenharmony_ci skb = NULL; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci shdlc->nr = (shdlc->nr + 1) % 8; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { 25262306a36Sopenharmony_ci llc_shdlc_reset_t2(shdlc, y_nr); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci shdlc->dnr = y_nr; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciexit: 25862306a36Sopenharmony_ci kfree_skb(skb); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci pr_debug("remote acked up to frame %d excluded\n", y_nr); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { 26662306a36Sopenharmony_ci llc_shdlc_reset_t2(shdlc, y_nr); 26762306a36Sopenharmony_ci shdlc->dnr = y_nr; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct sk_buff *skb; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci pr_debug("ns reset to %d\n", shdlc->dnr); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) { 27862306a36Sopenharmony_ci skb_pull(skb, 1); /* remove control field */ 27962306a36Sopenharmony_ci skb_queue_head(&shdlc->send_q, skb); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci shdlc->ns = shdlc->dnr; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct sk_buff *skb; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci pr_debug("remote asks retransmission from frame %d\n", y_nr); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) { 29162306a36Sopenharmony_ci if (shdlc->t2_active) { 29262306a36Sopenharmony_ci del_timer_sync(&shdlc->t2_timer); 29362306a36Sopenharmony_ci shdlc->t2_active = false; 29462306a36Sopenharmony_ci pr_debug("Stopped T2(retransmit)\n"); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (shdlc->dnr != y_nr) { 29862306a36Sopenharmony_ci while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) { 29962306a36Sopenharmony_ci skb = skb_dequeue(&shdlc->ack_pending_q); 30062306a36Sopenharmony_ci kfree_skb(skb); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci llc_shdlc_requeue_ack_pending(shdlc); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* See spec RR:10.8.3 REJ:10.8.4 */ 30962306a36Sopenharmony_cistatic void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc, 31062306a36Sopenharmony_ci enum sframe_type s_frame_type, int nr) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct sk_buff *skb; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (shdlc->state != SHDLC_CONNECTED) 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci switch (s_frame_type) { 31862306a36Sopenharmony_ci case S_FRAME_RR: 31962306a36Sopenharmony_ci llc_shdlc_rcv_ack(shdlc, nr); 32062306a36Sopenharmony_ci if (shdlc->rnr == true) { /* see SHDLC 10.7.7 */ 32162306a36Sopenharmony_ci shdlc->rnr = false; 32262306a36Sopenharmony_ci if (shdlc->send_q.qlen == 0) { 32362306a36Sopenharmony_ci skb = llc_shdlc_alloc_skb(shdlc, 0); 32462306a36Sopenharmony_ci if (skb) 32562306a36Sopenharmony_ci skb_queue_tail(&shdlc->send_q, skb); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case S_FRAME_REJ: 33062306a36Sopenharmony_ci llc_shdlc_rcv_rej(shdlc, nr); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci case S_FRAME_RNR: 33362306a36Sopenharmony_ci llc_shdlc_rcv_ack(shdlc, nr); 33462306a36Sopenharmony_ci shdlc->rnr = true; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci default: 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci pr_debug("result=%d\n", r); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci del_timer_sync(&shdlc->connect_timer); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (r == 0) { 34862306a36Sopenharmony_ci shdlc->ns = 0; 34962306a36Sopenharmony_ci shdlc->nr = 0; 35062306a36Sopenharmony_ci shdlc->dnr = 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci shdlc->state = SHDLC_HALF_CONNECTED; 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci shdlc->state = SHDLC_DISCONNECTED; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci shdlc->connect_result = r; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci wake_up(shdlc->connect_wq); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int llc_shdlc_connect_initiate(const struct llc_shdlc *shdlc) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct sk_buff *skb; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci skb = llc_shdlc_alloc_skb(shdlc, 2); 36762306a36Sopenharmony_ci if (skb == NULL) 36862306a36Sopenharmony_ci return -ENOMEM; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci skb_put_u8(skb, SHDLC_MAX_WINDOW); 37162306a36Sopenharmony_ci skb_put_u8(skb, SHDLC_SREJ_SUPPORT ? 1 : 0); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int llc_shdlc_connect_send_ua(const struct llc_shdlc *shdlc) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct sk_buff *skb; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci skb = llc_shdlc_alloc_skb(shdlc, 0); 38162306a36Sopenharmony_ci if (skb == NULL) 38262306a36Sopenharmony_ci return -ENOMEM; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, 38862306a36Sopenharmony_ci struct sk_buff *skb, 38962306a36Sopenharmony_ci enum uframe_modifier u_frame_modifier) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci u8 w = SHDLC_MAX_WINDOW; 39262306a36Sopenharmony_ci bool srej_support = SHDLC_SREJ_SUPPORT; 39362306a36Sopenharmony_ci int r; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci pr_debug("u_frame_modifier=%d\n", u_frame_modifier); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci switch (u_frame_modifier) { 39862306a36Sopenharmony_ci case U_FRAME_RSET: 39962306a36Sopenharmony_ci switch (shdlc->state) { 40062306a36Sopenharmony_ci case SHDLC_NEGOTIATING: 40162306a36Sopenharmony_ci case SHDLC_CONNECTING: 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * We sent RSET, but chip wants to negotiate or we 40462306a36Sopenharmony_ci * got RSET before we managed to send out our. 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci if (skb->len > 0) 40762306a36Sopenharmony_ci w = skb->data[0]; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (skb->len > 1) 41062306a36Sopenharmony_ci srej_support = skb->data[1] & 0x01 ? true : 41162306a36Sopenharmony_ci false; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if ((w <= SHDLC_MAX_WINDOW) && 41462306a36Sopenharmony_ci (SHDLC_SREJ_SUPPORT || (srej_support == false))) { 41562306a36Sopenharmony_ci shdlc->w = w; 41662306a36Sopenharmony_ci shdlc->srej_support = srej_support; 41762306a36Sopenharmony_ci r = llc_shdlc_connect_send_ua(shdlc); 41862306a36Sopenharmony_ci llc_shdlc_connect_complete(shdlc, r); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case SHDLC_HALF_CONNECTED: 42262306a36Sopenharmony_ci /* 42362306a36Sopenharmony_ci * Chip resent RSET due to its timeout - Ignote it 42462306a36Sopenharmony_ci * as we already sent UA. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci case SHDLC_CONNECTED: 42862306a36Sopenharmony_ci /* 42962306a36Sopenharmony_ci * Chip wants to reset link. This is unexpected and 43062306a36Sopenharmony_ci * unsupported. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci shdlc->hard_fault = -ECONNRESET; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci default: 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci case U_FRAME_UA: 43962306a36Sopenharmony_ci if ((shdlc->state == SHDLC_CONNECTING && 44062306a36Sopenharmony_ci shdlc->connect_tries > 0) || 44162306a36Sopenharmony_ci (shdlc->state == SHDLC_NEGOTIATING)) { 44262306a36Sopenharmony_ci llc_shdlc_connect_complete(shdlc, 0); 44362306a36Sopenharmony_ci shdlc->state = SHDLC_CONNECTED; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci default: 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci kfree_skb(skb); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct sk_buff *skb; 45662306a36Sopenharmony_ci u8 control; 45762306a36Sopenharmony_ci int nr; 45862306a36Sopenharmony_ci int ns; 45962306a36Sopenharmony_ci enum sframe_type s_frame_type; 46062306a36Sopenharmony_ci enum uframe_modifier u_frame_modifier; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (shdlc->rcv_q.qlen) 46362306a36Sopenharmony_ci pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) { 46662306a36Sopenharmony_ci control = skb->data[0]; 46762306a36Sopenharmony_ci skb_pull(skb, 1); 46862306a36Sopenharmony_ci switch (control & SHDLC_CONTROL_HEAD_MASK) { 46962306a36Sopenharmony_ci case SHDLC_CONTROL_HEAD_I: 47062306a36Sopenharmony_ci case SHDLC_CONTROL_HEAD_I2: 47162306a36Sopenharmony_ci if (shdlc->state == SHDLC_HALF_CONNECTED) 47262306a36Sopenharmony_ci shdlc->state = SHDLC_CONNECTED; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ns = (control & SHDLC_CONTROL_NS_MASK) >> 3; 47562306a36Sopenharmony_ci nr = control & SHDLC_CONTROL_NR_MASK; 47662306a36Sopenharmony_ci llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr); 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci case SHDLC_CONTROL_HEAD_S: 47962306a36Sopenharmony_ci if (shdlc->state == SHDLC_HALF_CONNECTED) 48062306a36Sopenharmony_ci shdlc->state = SHDLC_CONNECTED; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3; 48362306a36Sopenharmony_ci nr = control & SHDLC_CONTROL_NR_MASK; 48462306a36Sopenharmony_ci llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr); 48562306a36Sopenharmony_ci kfree_skb(skb); 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci case SHDLC_CONTROL_HEAD_U: 48862306a36Sopenharmony_ci u_frame_modifier = control & SHDLC_CONTROL_M_MASK; 48962306a36Sopenharmony_ci llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci default: 49262306a36Sopenharmony_ci pr_err("UNKNOWN Control=%d\n", control); 49362306a36Sopenharmony_ci kfree_skb(skb); 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int llc_shdlc_w_used(int ns, int dnr) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int unack_count; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (dnr <= ns) 50462306a36Sopenharmony_ci unack_count = ns - dnr; 50562306a36Sopenharmony_ci else 50662306a36Sopenharmony_ci unack_count = 8 - dnr + ns; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return unack_count; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/* Send frames according to algorithm at spec:10.8.1 */ 51262306a36Sopenharmony_cistatic void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct sk_buff *skb; 51562306a36Sopenharmony_ci int r; 51662306a36Sopenharmony_ci unsigned long time_sent; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (shdlc->send_q.qlen) 51962306a36Sopenharmony_ci pr_debug("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n", 52062306a36Sopenharmony_ci shdlc->send_q.qlen, shdlc->ns, shdlc->dnr, 52162306a36Sopenharmony_ci shdlc->rnr == false ? "false" : "true", 52262306a36Sopenharmony_ci shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr), 52362306a36Sopenharmony_ci shdlc->ack_pending_q.qlen); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w && 52662306a36Sopenharmony_ci (shdlc->rnr == false)) { 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (shdlc->t1_active) { 52962306a36Sopenharmony_ci del_timer_sync(&shdlc->t1_timer); 53062306a36Sopenharmony_ci shdlc->t1_active = false; 53162306a36Sopenharmony_ci pr_debug("Stopped T1(send ack)\n"); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci skb = skb_dequeue(&shdlc->send_q); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci *(u8 *)skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) | 53762306a36Sopenharmony_ci shdlc->nr; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns, 54062306a36Sopenharmony_ci shdlc->nr); 54162306a36Sopenharmony_ci SHDLC_DUMP_SKB("shdlc frame written", skb); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci r = shdlc->xmit_to_drv(shdlc->hdev, skb); 54462306a36Sopenharmony_ci if (r < 0) { 54562306a36Sopenharmony_ci shdlc->hard_fault = r; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci shdlc->ns = (shdlc->ns + 1) % 8; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci time_sent = jiffies; 55262306a36Sopenharmony_ci *(unsigned long *)skb->cb = time_sent; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci skb_queue_tail(&shdlc->ack_pending_q, skb); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (shdlc->t2_active == false) { 55762306a36Sopenharmony_ci shdlc->t2_active = true; 55862306a36Sopenharmony_ci mod_timer(&shdlc->t2_timer, time_sent + 55962306a36Sopenharmony_ci msecs_to_jiffies(SHDLC_T2_VALUE_MS)); 56062306a36Sopenharmony_ci pr_debug("Started T2 (retransmit)\n"); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic void llc_shdlc_connect_timeout(struct timer_list *t) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct llc_shdlc *shdlc = from_timer(shdlc, t, connect_timer); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void llc_shdlc_t1_timeout(struct timer_list *t) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct llc_shdlc *shdlc = from_timer(shdlc, t, t1_timer); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pr_debug("SoftIRQ: need to send ack\n"); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void llc_shdlc_t2_timeout(struct timer_list *t) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct llc_shdlc *shdlc = from_timer(shdlc, t, t2_timer); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci pr_debug("SoftIRQ: need to retransmit\n"); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void llc_shdlc_sm_work(struct work_struct *work) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work); 59362306a36Sopenharmony_ci int r; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci mutex_lock(&shdlc->state_mutex); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci switch (shdlc->state) { 59862306a36Sopenharmony_ci case SHDLC_DISCONNECTED: 59962306a36Sopenharmony_ci skb_queue_purge(&shdlc->rcv_q); 60062306a36Sopenharmony_ci skb_queue_purge(&shdlc->send_q); 60162306a36Sopenharmony_ci skb_queue_purge(&shdlc->ack_pending_q); 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci case SHDLC_CONNECTING: 60462306a36Sopenharmony_ci if (shdlc->hard_fault) { 60562306a36Sopenharmony_ci llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (shdlc->connect_tries++ < 5) 61062306a36Sopenharmony_ci r = llc_shdlc_connect_initiate(shdlc); 61162306a36Sopenharmony_ci else 61262306a36Sopenharmony_ci r = -ETIME; 61362306a36Sopenharmony_ci if (r < 0) { 61462306a36Sopenharmony_ci llc_shdlc_connect_complete(shdlc, r); 61562306a36Sopenharmony_ci } else { 61662306a36Sopenharmony_ci mod_timer(&shdlc->connect_timer, jiffies + 61762306a36Sopenharmony_ci msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS)); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci shdlc->state = SHDLC_NEGOTIATING; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci case SHDLC_NEGOTIATING: 62362306a36Sopenharmony_ci if (timer_pending(&shdlc->connect_timer) == 0) { 62462306a36Sopenharmony_ci shdlc->state = SHDLC_CONNECTING; 62562306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci llc_shdlc_handle_rcv_queue(shdlc); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (shdlc->hard_fault) { 63162306a36Sopenharmony_ci llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci case SHDLC_HALF_CONNECTED: 63662306a36Sopenharmony_ci case SHDLC_CONNECTED: 63762306a36Sopenharmony_ci llc_shdlc_handle_rcv_queue(shdlc); 63862306a36Sopenharmony_ci llc_shdlc_handle_send_queue(shdlc); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) { 64162306a36Sopenharmony_ci pr_debug("Handle T1(send ack) elapsed (T1 now inactive)\n"); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci shdlc->t1_active = false; 64462306a36Sopenharmony_ci r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR, 64562306a36Sopenharmony_ci shdlc->nr); 64662306a36Sopenharmony_ci if (r < 0) 64762306a36Sopenharmony_ci shdlc->hard_fault = r; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) { 65162306a36Sopenharmony_ci pr_debug("Handle T2(retransmit) elapsed (T2 inactive)\n"); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci shdlc->t2_active = false; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci llc_shdlc_requeue_ack_pending(shdlc); 65662306a36Sopenharmony_ci llc_shdlc_handle_send_queue(shdlc); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (shdlc->hard_fault) 66062306a36Sopenharmony_ci shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault); 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci default: 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci mutex_unlock(&shdlc->state_mutex); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/* 66962306a36Sopenharmony_ci * Called from syscall context to establish shdlc link. Sleeps until 67062306a36Sopenharmony_ci * link is ready or failure. 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_cistatic int llc_shdlc_connect(struct llc_shdlc *shdlc) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci mutex_lock(&shdlc->state_mutex); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci shdlc->state = SHDLC_CONNECTING; 67962306a36Sopenharmony_ci shdlc->connect_wq = &connect_wq; 68062306a36Sopenharmony_ci shdlc->connect_tries = 0; 68162306a36Sopenharmony_ci shdlc->connect_result = 1; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci mutex_unlock(&shdlc->state_mutex); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci wait_event(connect_wq, shdlc->connect_result != 1); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return shdlc->connect_result; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic void llc_shdlc_disconnect(struct llc_shdlc *shdlc) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci mutex_lock(&shdlc->state_mutex); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci shdlc->state = SHDLC_DISCONNECTED; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci mutex_unlock(&shdlc->state_mutex); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci/* 70462306a36Sopenharmony_ci * Receive an incoming shdlc frame. Frame has already been crc-validated. 70562306a36Sopenharmony_ci * skb contains only LLC header and payload. 70662306a36Sopenharmony_ci * If skb == NULL, it is a notification that the link below is dead. 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_cistatic void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci if (skb == NULL) { 71162306a36Sopenharmony_ci pr_err("NULL Frame -> link is dead\n"); 71262306a36Sopenharmony_ci shdlc->hard_fault = -EREMOTEIO; 71362306a36Sopenharmony_ci } else { 71462306a36Sopenharmony_ci SHDLC_DUMP_SKB("incoming frame", skb); 71562306a36Sopenharmony_ci skb_queue_tail(&shdlc->rcv_q, skb); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, 72262306a36Sopenharmony_ci rcv_to_hci_t rcv_to_hci, int tx_headroom, 72362306a36Sopenharmony_ci int tx_tailroom, int *rx_headroom, int *rx_tailroom, 72462306a36Sopenharmony_ci llc_failure_t llc_failure) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct llc_shdlc *shdlc; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci *rx_headroom = SHDLC_LLC_HEAD_ROOM; 72962306a36Sopenharmony_ci *rx_tailroom = 0; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL); 73262306a36Sopenharmony_ci if (shdlc == NULL) 73362306a36Sopenharmony_ci return NULL; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci mutex_init(&shdlc->state_mutex); 73662306a36Sopenharmony_ci shdlc->state = SHDLC_DISCONNECTED; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci timer_setup(&shdlc->connect_timer, llc_shdlc_connect_timeout, 0); 73962306a36Sopenharmony_ci timer_setup(&shdlc->t1_timer, llc_shdlc_t1_timeout, 0); 74062306a36Sopenharmony_ci timer_setup(&shdlc->t2_timer, llc_shdlc_t2_timeout, 0); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci shdlc->w = SHDLC_MAX_WINDOW; 74362306a36Sopenharmony_ci shdlc->srej_support = SHDLC_SREJ_SUPPORT; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci skb_queue_head_init(&shdlc->rcv_q); 74662306a36Sopenharmony_ci skb_queue_head_init(&shdlc->send_q); 74762306a36Sopenharmony_ci skb_queue_head_init(&shdlc->ack_pending_q); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci shdlc->hdev = hdev; 75262306a36Sopenharmony_ci shdlc->xmit_to_drv = xmit_to_drv; 75362306a36Sopenharmony_ci shdlc->rcv_to_hci = rcv_to_hci; 75462306a36Sopenharmony_ci shdlc->tx_headroom = tx_headroom; 75562306a36Sopenharmony_ci shdlc->tx_tailroom = tx_tailroom; 75662306a36Sopenharmony_ci shdlc->llc_failure = llc_failure; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci return shdlc; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic void llc_shdlc_deinit(struct nfc_llc *llc) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct llc_shdlc *shdlc = nfc_llc_get_data(llc); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci skb_queue_purge(&shdlc->rcv_q); 76662306a36Sopenharmony_ci skb_queue_purge(&shdlc->send_q); 76762306a36Sopenharmony_ci skb_queue_purge(&shdlc->ack_pending_q); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci kfree(shdlc); 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic int llc_shdlc_start(struct nfc_llc *llc) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct llc_shdlc *shdlc = nfc_llc_get_data(llc); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return llc_shdlc_connect(shdlc); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int llc_shdlc_stop(struct nfc_llc *llc) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct llc_shdlc *shdlc = nfc_llc_get_data(llc); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci llc_shdlc_disconnect(shdlc); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct llc_shdlc *shdlc = nfc_llc_get_data(llc); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci llc_shdlc_recv_frame(shdlc, skb); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct llc_shdlc *shdlc = nfc_llc_get_data(llc); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci skb_queue_tail(&shdlc->send_q, skb); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci schedule_work(&shdlc->sm_work); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return 0; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic const struct nfc_llc_ops llc_shdlc_ops = { 80762306a36Sopenharmony_ci .init = llc_shdlc_init, 80862306a36Sopenharmony_ci .deinit = llc_shdlc_deinit, 80962306a36Sopenharmony_ci .start = llc_shdlc_start, 81062306a36Sopenharmony_ci .stop = llc_shdlc_stop, 81162306a36Sopenharmony_ci .rcv_from_drv = llc_shdlc_rcv_from_drv, 81262306a36Sopenharmony_ci .xmit_from_hci = llc_shdlc_xmit_from_hci, 81362306a36Sopenharmony_ci}; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciint nfc_llc_shdlc_register(void) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops); 81862306a36Sopenharmony_ci} 819