162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	LAPB release 002
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	This code REQUIRES 2.1.15 or higher/ NET3.038
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *	History
862306a36Sopenharmony_ci *	LAPB 001	Jonathan Naylor	Started Coding
962306a36Sopenharmony_ci *	LAPB 002	Jonathan Naylor	New timer architecture.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/socket.h>
1762306a36Sopenharmony_ci#include <linux/in.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/timer.h>
2062306a36Sopenharmony_ci#include <linux/string.h>
2162306a36Sopenharmony_ci#include <linux/sockios.h>
2262306a36Sopenharmony_ci#include <linux/net.h>
2362306a36Sopenharmony_ci#include <linux/inet.h>
2462306a36Sopenharmony_ci#include <linux/skbuff.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <net/sock.h>
2762306a36Sopenharmony_ci#include <linux/uaccess.h>
2862306a36Sopenharmony_ci#include <linux/fcntl.h>
2962306a36Sopenharmony_ci#include <linux/mm.h>
3062306a36Sopenharmony_ci#include <linux/interrupt.h>
3162306a36Sopenharmony_ci#include <net/lapb.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci *  This procedure is passed a buffer descriptor for an iframe. It builds
3562306a36Sopenharmony_ci *  the rest of the control part of the frame and then writes it out.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic void lapb_send_iframe(struct lapb_cb *lapb, struct sk_buff *skb, int poll_bit)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	unsigned char *frame;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!skb)
4262306a36Sopenharmony_ci		return;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (lapb->mode & LAPB_EXTENDED) {
4562306a36Sopenharmony_ci		frame = skb_push(skb, 2);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		frame[0] = LAPB_I;
4862306a36Sopenharmony_ci		frame[0] |= lapb->vs << 1;
4962306a36Sopenharmony_ci		frame[1] = poll_bit ? LAPB_EPF : 0;
5062306a36Sopenharmony_ci		frame[1] |= lapb->vr << 1;
5162306a36Sopenharmony_ci	} else {
5262306a36Sopenharmony_ci		frame = skb_push(skb, 1);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		*frame = LAPB_I;
5562306a36Sopenharmony_ci		*frame |= poll_bit ? LAPB_SPF : 0;
5662306a36Sopenharmony_ci		*frame |= lapb->vr << 5;
5762306a36Sopenharmony_ci		*frame |= lapb->vs << 1;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	lapb_dbg(1, "(%p) S%d TX I(%d) S%d R%d\n",
6162306a36Sopenharmony_ci		 lapb->dev, lapb->state, poll_bit, lapb->vs, lapb->vr);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	lapb_transmit_buffer(lapb, skb, LAPB_COMMAND);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_civoid lapb_kick(struct lapb_cb *lapb)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct sk_buff *skb, *skbn;
6962306a36Sopenharmony_ci	unsigned short modulus, start, end;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
7262306a36Sopenharmony_ci	start = !skb_peek(&lapb->ack_queue) ? lapb->va : lapb->vs;
7362306a36Sopenharmony_ci	end   = (lapb->va + lapb->window) % modulus;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!(lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) &&
7662306a36Sopenharmony_ci	    start != end && skb_peek(&lapb->write_queue)) {
7762306a36Sopenharmony_ci		lapb->vs = start;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		/*
8062306a36Sopenharmony_ci		 * Dequeue the frame and copy it.
8162306a36Sopenharmony_ci		 */
8262306a36Sopenharmony_ci		skb = skb_dequeue(&lapb->write_queue);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		do {
8562306a36Sopenharmony_ci			skbn = skb_copy(skb, GFP_ATOMIC);
8662306a36Sopenharmony_ci			if (!skbn) {
8762306a36Sopenharmony_ci				skb_queue_head(&lapb->write_queue, skb);
8862306a36Sopenharmony_ci				break;
8962306a36Sopenharmony_ci			}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci			if (skb->sk)
9262306a36Sopenharmony_ci				skb_set_owner_w(skbn, skb->sk);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci			/*
9562306a36Sopenharmony_ci			 * Transmit the frame copy.
9662306a36Sopenharmony_ci			 */
9762306a36Sopenharmony_ci			lapb_send_iframe(lapb, skbn, LAPB_POLLOFF);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci			lapb->vs = (lapb->vs + 1) % modulus;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci			/*
10262306a36Sopenharmony_ci			 * Requeue the original data frame.
10362306a36Sopenharmony_ci			 */
10462306a36Sopenharmony_ci			skb_queue_tail(&lapb->ack_queue, skb);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		} while (lapb->vs != end && (skb = skb_dequeue(&lapb->write_queue)) != NULL);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		if (!lapb_t1timer_running(lapb))
11162306a36Sopenharmony_ci			lapb_start_t1timer(lapb);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid lapb_transmit_buffer(struct lapb_cb *lapb, struct sk_buff *skb, int type)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	unsigned char *ptr;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ptr = skb_push(skb, 1);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (lapb->mode & LAPB_MLP) {
12262306a36Sopenharmony_ci		if (lapb->mode & LAPB_DCE) {
12362306a36Sopenharmony_ci			if (type == LAPB_COMMAND)
12462306a36Sopenharmony_ci				*ptr = LAPB_ADDR_C;
12562306a36Sopenharmony_ci			if (type == LAPB_RESPONSE)
12662306a36Sopenharmony_ci				*ptr = LAPB_ADDR_D;
12762306a36Sopenharmony_ci		} else {
12862306a36Sopenharmony_ci			if (type == LAPB_COMMAND)
12962306a36Sopenharmony_ci				*ptr = LAPB_ADDR_D;
13062306a36Sopenharmony_ci			if (type == LAPB_RESPONSE)
13162306a36Sopenharmony_ci				*ptr = LAPB_ADDR_C;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci	} else {
13462306a36Sopenharmony_ci		if (lapb->mode & LAPB_DCE) {
13562306a36Sopenharmony_ci			if (type == LAPB_COMMAND)
13662306a36Sopenharmony_ci				*ptr = LAPB_ADDR_A;
13762306a36Sopenharmony_ci			if (type == LAPB_RESPONSE)
13862306a36Sopenharmony_ci				*ptr = LAPB_ADDR_B;
13962306a36Sopenharmony_ci		} else {
14062306a36Sopenharmony_ci			if (type == LAPB_COMMAND)
14162306a36Sopenharmony_ci				*ptr = LAPB_ADDR_B;
14262306a36Sopenharmony_ci			if (type == LAPB_RESPONSE)
14362306a36Sopenharmony_ci				*ptr = LAPB_ADDR_A;
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	lapb_dbg(2, "(%p) S%d TX %3ph\n", lapb->dev, lapb->state, skb->data);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (!lapb_data_transmit(lapb, skb))
15062306a36Sopenharmony_ci		kfree_skb(skb);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_civoid lapb_establish_data_link(struct lapb_cb *lapb)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	lapb->condition = 0x00;
15662306a36Sopenharmony_ci	lapb->n2count   = 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (lapb->mode & LAPB_EXTENDED) {
15962306a36Sopenharmony_ci		lapb_dbg(1, "(%p) S%d TX SABME(1)\n", lapb->dev, lapb->state);
16062306a36Sopenharmony_ci		lapb_send_control(lapb, LAPB_SABME, LAPB_POLLON, LAPB_COMMAND);
16162306a36Sopenharmony_ci	} else {
16262306a36Sopenharmony_ci		lapb_dbg(1, "(%p) S%d TX SABM(1)\n", lapb->dev, lapb->state);
16362306a36Sopenharmony_ci		lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND);
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	lapb_start_t1timer(lapb);
16762306a36Sopenharmony_ci	lapb_stop_t2timer(lapb);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_civoid lapb_enquiry_response(struct lapb_cb *lapb)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	lapb_dbg(1, "(%p) S%d TX RR(1) R%d\n",
17362306a36Sopenharmony_ci		 lapb->dev, lapb->state, lapb->vr);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	lapb_send_control(lapb, LAPB_RR, LAPB_POLLON, LAPB_RESPONSE);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_civoid lapb_timeout_response(struct lapb_cb *lapb)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	lapb_dbg(1, "(%p) S%d TX RR(0) R%d\n",
18362306a36Sopenharmony_ci		 lapb->dev, lapb->state, lapb->vr);
18462306a36Sopenharmony_ci	lapb_send_control(lapb, LAPB_RR, LAPB_POLLOFF, LAPB_RESPONSE);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_civoid lapb_check_iframes_acked(struct lapb_cb *lapb, unsigned short nr)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	if (lapb->vs == nr) {
19262306a36Sopenharmony_ci		lapb_frames_acked(lapb, nr);
19362306a36Sopenharmony_ci		lapb_stop_t1timer(lapb);
19462306a36Sopenharmony_ci		lapb->n2count = 0;
19562306a36Sopenharmony_ci	} else if (lapb->va != nr) {
19662306a36Sopenharmony_ci		lapb_frames_acked(lapb, nr);
19762306a36Sopenharmony_ci		lapb_start_t1timer(lapb);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_civoid lapb_check_need_response(struct lapb_cb *lapb, int type, int pf)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	if (type == LAPB_COMMAND && pf)
20462306a36Sopenharmony_ci		lapb_enquiry_response(lapb);
20562306a36Sopenharmony_ci}
206