xref: /kernel/linux/linux-5.10/net/lapb/lapb_iface.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	LAPB release 002
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	This code REQUIRES 2.1.15 or higher/ NET3.038
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *	History
88c2ecf20Sopenharmony_ci *	LAPB 001	Jonathan Naylor	Started Coding
98c2ecf20Sopenharmony_ci *	LAPB 002	Jonathan Naylor	New timer architecture.
108c2ecf20Sopenharmony_ci *	2000-10-29	Henner Eisen	lapb_data_indication() return status.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/socket.h>
198c2ecf20Sopenharmony_ci#include <linux/in.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
228c2ecf20Sopenharmony_ci#include <linux/timer.h>
238c2ecf20Sopenharmony_ci#include <linux/string.h>
248c2ecf20Sopenharmony_ci#include <linux/sockios.h>
258c2ecf20Sopenharmony_ci#include <linux/net.h>
268c2ecf20Sopenharmony_ci#include <linux/inet.h>
278c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
288c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <net/sock.h>
318c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
328c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
338c2ecf20Sopenharmony_ci#include <linux/mm.h>
348c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
358c2ecf20Sopenharmony_ci#include <linux/stat.h>
368c2ecf20Sopenharmony_ci#include <linux/init.h>
378c2ecf20Sopenharmony_ci#include <net/lapb.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic LIST_HEAD(lapb_list);
408c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(lapb_list_lock);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci *	Free an allocated lapb control block.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistatic void lapb_free_cb(struct lapb_cb *lapb)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	kfree(lapb);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic __inline__ void lapb_hold(struct lapb_cb *lapb)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	refcount_inc(&lapb->refcnt);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic __inline__ void lapb_put(struct lapb_cb *lapb)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&lapb->refcnt))
588c2ecf20Sopenharmony_ci		lapb_free_cb(lapb);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci *	Socket removal during an interrupt is now safe.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic void __lapb_remove_cb(struct lapb_cb *lapb)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	if (lapb->node.next) {
678c2ecf20Sopenharmony_ci		list_del(&lapb->node);
688c2ecf20Sopenharmony_ci		lapb_put(lapb);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/*
738c2ecf20Sopenharmony_ci *	Add a socket to the bound sockets list.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistatic void __lapb_insert_cb(struct lapb_cb *lapb)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	list_add(&lapb->node, &lapb_list);
788c2ecf20Sopenharmony_ci	lapb_hold(lapb);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic struct lapb_cb *__lapb_devtostruct(struct net_device *dev)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct list_head *entry;
848c2ecf20Sopenharmony_ci	struct lapb_cb *lapb, *use = NULL;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	list_for_each(entry, &lapb_list) {
878c2ecf20Sopenharmony_ci		lapb = list_entry(entry, struct lapb_cb, node);
888c2ecf20Sopenharmony_ci		if (lapb->dev == dev) {
898c2ecf20Sopenharmony_ci			use = lapb;
908c2ecf20Sopenharmony_ci			break;
918c2ecf20Sopenharmony_ci		}
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (use)
958c2ecf20Sopenharmony_ci		lapb_hold(use);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return use;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic struct lapb_cb *lapb_devtostruct(struct net_device *dev)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct lapb_cb *rc;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	read_lock_bh(&lapb_list_lock);
1058c2ecf20Sopenharmony_ci	rc = __lapb_devtostruct(dev);
1068c2ecf20Sopenharmony_ci	read_unlock_bh(&lapb_list_lock);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return rc;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci/*
1118c2ecf20Sopenharmony_ci *	Create an empty LAPB control block.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic struct lapb_cb *lapb_create_cb(void)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct lapb_cb *lapb = kzalloc(sizeof(*lapb), GFP_ATOMIC);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (!lapb)
1188c2ecf20Sopenharmony_ci		goto out;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	skb_queue_head_init(&lapb->write_queue);
1218c2ecf20Sopenharmony_ci	skb_queue_head_init(&lapb->ack_queue);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	timer_setup(&lapb->t1timer, NULL, 0);
1248c2ecf20Sopenharmony_ci	timer_setup(&lapb->t2timer, NULL, 0);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	lapb->t1      = LAPB_DEFAULT_T1;
1278c2ecf20Sopenharmony_ci	lapb->t2      = LAPB_DEFAULT_T2;
1288c2ecf20Sopenharmony_ci	lapb->n2      = LAPB_DEFAULT_N2;
1298c2ecf20Sopenharmony_ci	lapb->mode    = LAPB_DEFAULT_MODE;
1308c2ecf20Sopenharmony_ci	lapb->window  = LAPB_DEFAULT_WINDOW;
1318c2ecf20Sopenharmony_ci	lapb->state   = LAPB_STATE_0;
1328c2ecf20Sopenharmony_ci	refcount_set(&lapb->refcnt, 1);
1338c2ecf20Sopenharmony_ciout:
1348c2ecf20Sopenharmony_ci	return lapb;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ciint lapb_register(struct net_device *dev,
1388c2ecf20Sopenharmony_ci		  const struct lapb_register_struct *callbacks)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct lapb_cb *lapb;
1418c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	write_lock_bh(&lapb_list_lock);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	lapb = __lapb_devtostruct(dev);
1468c2ecf20Sopenharmony_ci	if (lapb) {
1478c2ecf20Sopenharmony_ci		lapb_put(lapb);
1488c2ecf20Sopenharmony_ci		goto out;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	lapb = lapb_create_cb();
1528c2ecf20Sopenharmony_ci	rc = LAPB_NOMEM;
1538c2ecf20Sopenharmony_ci	if (!lapb)
1548c2ecf20Sopenharmony_ci		goto out;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	lapb->dev       = dev;
1578c2ecf20Sopenharmony_ci	lapb->callbacks = callbacks;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	__lapb_insert_cb(lapb);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	lapb_start_t1timer(lapb);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	rc = LAPB_OK;
1648c2ecf20Sopenharmony_ciout:
1658c2ecf20Sopenharmony_ci	write_unlock_bh(&lapb_list_lock);
1668c2ecf20Sopenharmony_ci	return rc;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_register);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciint lapb_unregister(struct net_device *dev)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct lapb_cb *lapb;
1738c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	write_lock_bh(&lapb_list_lock);
1768c2ecf20Sopenharmony_ci	lapb = __lapb_devtostruct(dev);
1778c2ecf20Sopenharmony_ci	if (!lapb)
1788c2ecf20Sopenharmony_ci		goto out;
1798c2ecf20Sopenharmony_ci	lapb_put(lapb);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	lapb_stop_t1timer(lapb);
1828c2ecf20Sopenharmony_ci	lapb_stop_t2timer(lapb);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	lapb_clear_queues(lapb);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	__lapb_remove_cb(lapb);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	lapb_put(lapb);
1898c2ecf20Sopenharmony_ci	rc = LAPB_OK;
1908c2ecf20Sopenharmony_ciout:
1918c2ecf20Sopenharmony_ci	write_unlock_bh(&lapb_list_lock);
1928c2ecf20Sopenharmony_ci	return rc;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_unregister);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ciint lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
1998c2ecf20Sopenharmony_ci	struct lapb_cb *lapb = lapb_devtostruct(dev);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (!lapb)
2028c2ecf20Sopenharmony_ci		goto out;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	parms->t1      = lapb->t1 / HZ;
2058c2ecf20Sopenharmony_ci	parms->t2      = lapb->t2 / HZ;
2068c2ecf20Sopenharmony_ci	parms->n2      = lapb->n2;
2078c2ecf20Sopenharmony_ci	parms->n2count = lapb->n2count;
2088c2ecf20Sopenharmony_ci	parms->state   = lapb->state;
2098c2ecf20Sopenharmony_ci	parms->window  = lapb->window;
2108c2ecf20Sopenharmony_ci	parms->mode    = lapb->mode;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (!timer_pending(&lapb->t1timer))
2138c2ecf20Sopenharmony_ci		parms->t1timer = 0;
2148c2ecf20Sopenharmony_ci	else
2158c2ecf20Sopenharmony_ci		parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (!timer_pending(&lapb->t2timer))
2188c2ecf20Sopenharmony_ci		parms->t2timer = 0;
2198c2ecf20Sopenharmony_ci	else
2208c2ecf20Sopenharmony_ci		parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	lapb_put(lapb);
2238c2ecf20Sopenharmony_ci	rc = LAPB_OK;
2248c2ecf20Sopenharmony_ciout:
2258c2ecf20Sopenharmony_ci	return rc;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_getparms);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciint lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
2328c2ecf20Sopenharmony_ci	struct lapb_cb *lapb = lapb_devtostruct(dev);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (!lapb)
2358c2ecf20Sopenharmony_ci		goto out;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	rc = LAPB_INVALUE;
2388c2ecf20Sopenharmony_ci	if (parms->t1 < 1 || parms->t2 < 1 || parms->n2 < 1)
2398c2ecf20Sopenharmony_ci		goto out_put;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (lapb->state == LAPB_STATE_0) {
2428c2ecf20Sopenharmony_ci		if (parms->mode & LAPB_EXTENDED) {
2438c2ecf20Sopenharmony_ci			if (parms->window < 1 || parms->window > 127)
2448c2ecf20Sopenharmony_ci				goto out_put;
2458c2ecf20Sopenharmony_ci		} else {
2468c2ecf20Sopenharmony_ci			if (parms->window < 1 || parms->window > 7)
2478c2ecf20Sopenharmony_ci				goto out_put;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci		lapb->mode    = parms->mode;
2508c2ecf20Sopenharmony_ci		lapb->window  = parms->window;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	lapb->t1    = parms->t1 * HZ;
2548c2ecf20Sopenharmony_ci	lapb->t2    = parms->t2 * HZ;
2558c2ecf20Sopenharmony_ci	lapb->n2    = parms->n2;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	rc = LAPB_OK;
2588c2ecf20Sopenharmony_ciout_put:
2598c2ecf20Sopenharmony_ci	lapb_put(lapb);
2608c2ecf20Sopenharmony_ciout:
2618c2ecf20Sopenharmony_ci	return rc;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_setparms);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ciint lapb_connect_request(struct net_device *dev)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct lapb_cb *lapb = lapb_devtostruct(dev);
2688c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (!lapb)
2718c2ecf20Sopenharmony_ci		goto out;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	rc = LAPB_OK;
2748c2ecf20Sopenharmony_ci	if (lapb->state == LAPB_STATE_1)
2758c2ecf20Sopenharmony_ci		goto out_put;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	rc = LAPB_CONNECTED;
2788c2ecf20Sopenharmony_ci	if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4)
2798c2ecf20Sopenharmony_ci		goto out_put;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	lapb_establish_data_link(lapb);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	lapb_dbg(0, "(%p) S0 -> S1\n", lapb->dev);
2848c2ecf20Sopenharmony_ci	lapb->state = LAPB_STATE_1;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	rc = LAPB_OK;
2878c2ecf20Sopenharmony_ciout_put:
2888c2ecf20Sopenharmony_ci	lapb_put(lapb);
2898c2ecf20Sopenharmony_ciout:
2908c2ecf20Sopenharmony_ci	return rc;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_connect_request);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ciint lapb_disconnect_request(struct net_device *dev)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct lapb_cb *lapb = lapb_devtostruct(dev);
2978c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (!lapb)
3008c2ecf20Sopenharmony_ci		goto out;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	switch (lapb->state) {
3038c2ecf20Sopenharmony_ci	case LAPB_STATE_0:
3048c2ecf20Sopenharmony_ci		rc = LAPB_NOTCONNECTED;
3058c2ecf20Sopenharmony_ci		goto out_put;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	case LAPB_STATE_1:
3088c2ecf20Sopenharmony_ci		lapb_dbg(1, "(%p) S1 TX DISC(1)\n", lapb->dev);
3098c2ecf20Sopenharmony_ci		lapb_dbg(0, "(%p) S1 -> S0\n", lapb->dev);
3108c2ecf20Sopenharmony_ci		lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
3118c2ecf20Sopenharmony_ci		lapb->state = LAPB_STATE_0;
3128c2ecf20Sopenharmony_ci		lapb_start_t1timer(lapb);
3138c2ecf20Sopenharmony_ci		rc = LAPB_NOTCONNECTED;
3148c2ecf20Sopenharmony_ci		goto out_put;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	case LAPB_STATE_2:
3178c2ecf20Sopenharmony_ci		rc = LAPB_OK;
3188c2ecf20Sopenharmony_ci		goto out_put;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	lapb_clear_queues(lapb);
3228c2ecf20Sopenharmony_ci	lapb->n2count = 0;
3238c2ecf20Sopenharmony_ci	lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
3248c2ecf20Sopenharmony_ci	lapb_start_t1timer(lapb);
3258c2ecf20Sopenharmony_ci	lapb_stop_t2timer(lapb);
3268c2ecf20Sopenharmony_ci	lapb->state = LAPB_STATE_2;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	lapb_dbg(1, "(%p) S3 DISC(1)\n", lapb->dev);
3298c2ecf20Sopenharmony_ci	lapb_dbg(0, "(%p) S3 -> S2\n", lapb->dev);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	rc = LAPB_OK;
3328c2ecf20Sopenharmony_ciout_put:
3338c2ecf20Sopenharmony_ci	lapb_put(lapb);
3348c2ecf20Sopenharmony_ciout:
3358c2ecf20Sopenharmony_ci	return rc;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_disconnect_request);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ciint lapb_data_request(struct net_device *dev, struct sk_buff *skb)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct lapb_cb *lapb = lapb_devtostruct(dev);
3428c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (!lapb)
3458c2ecf20Sopenharmony_ci		goto out;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	rc = LAPB_NOTCONNECTED;
3488c2ecf20Sopenharmony_ci	if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4)
3498c2ecf20Sopenharmony_ci		goto out_put;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	skb_queue_tail(&lapb->write_queue, skb);
3528c2ecf20Sopenharmony_ci	lapb_kick(lapb);
3538c2ecf20Sopenharmony_ci	rc = LAPB_OK;
3548c2ecf20Sopenharmony_ciout_put:
3558c2ecf20Sopenharmony_ci	lapb_put(lapb);
3568c2ecf20Sopenharmony_ciout:
3578c2ecf20Sopenharmony_ci	return rc;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_data_request);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ciint lapb_data_received(struct net_device *dev, struct sk_buff *skb)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct lapb_cb *lapb = lapb_devtostruct(dev);
3648c2ecf20Sopenharmony_ci	int rc = LAPB_BADTOKEN;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (lapb) {
3678c2ecf20Sopenharmony_ci		lapb_data_input(lapb, skb);
3688c2ecf20Sopenharmony_ci		lapb_put(lapb);
3698c2ecf20Sopenharmony_ci		rc = LAPB_OK;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return rc;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lapb_data_received);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_civoid lapb_connect_confirmation(struct lapb_cb *lapb, int reason)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	if (lapb->callbacks->connect_confirmation)
3798c2ecf20Sopenharmony_ci		lapb->callbacks->connect_confirmation(lapb->dev, reason);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_civoid lapb_connect_indication(struct lapb_cb *lapb, int reason)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	if (lapb->callbacks->connect_indication)
3858c2ecf20Sopenharmony_ci		lapb->callbacks->connect_indication(lapb->dev, reason);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_civoid lapb_disconnect_confirmation(struct lapb_cb *lapb, int reason)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	if (lapb->callbacks->disconnect_confirmation)
3918c2ecf20Sopenharmony_ci		lapb->callbacks->disconnect_confirmation(lapb->dev, reason);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_civoid lapb_disconnect_indication(struct lapb_cb *lapb, int reason)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	if (lapb->callbacks->disconnect_indication)
3978c2ecf20Sopenharmony_ci		lapb->callbacks->disconnect_indication(lapb->dev, reason);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ciint lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	if (lapb->callbacks->data_indication)
4038c2ecf20Sopenharmony_ci		return lapb->callbacks->data_indication(lapb->dev, skb);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	kfree_skb(skb);
4068c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciint lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	int used = 0;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (lapb->callbacks->data_transmit) {
4148c2ecf20Sopenharmony_ci		lapb->callbacks->data_transmit(lapb->dev, skb);
4158c2ecf20Sopenharmony_ci		used = 1;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return used;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int __init lapb_init(void)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void __exit lapb_exit(void)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&lapb_list));
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
4328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("The X.25 Link Access Procedure B link layer protocol");
4338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cimodule_init(lapb_init);
4368c2ecf20Sopenharmony_cimodule_exit(lapb_exit);
437