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