diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 | 
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/lapb/lapb_iface.c | |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/lapb/lapb_iface.c')
| -rw-r--r-- | net/lapb/lapb_iface.c | 449 | 
1 files changed, 449 insertions, 0 deletions
| diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c new file mode 100644 index 00000000000..aea6616cea3 --- /dev/null +++ b/net/lapb/lapb_iface.c @@ -0,0 +1,449 @@ +/* + *	LAPB release 002 + * + *	This code REQUIRES 2.1.15 or higher/ NET3.038 + * + *	This module: + *		This module is free software; you can redistribute it and/or + *		modify it under the terms of the GNU General Public License + *		as published by the Free Software Foundation; either version + *		2 of the License, or (at your option) any later version. + * + *	History + *	LAPB 001	Jonathan Naylor	Started Coding + *	LAPB 002	Jonathan Naylor	New timer architecture. + *	2000-10-29	Henner Eisen	lapb_data_indication() return status. + */ +  +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/kernel.h> +#include <linux/jiffies.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/sockios.h> +#include <linux/net.h> +#include <linux/inet.h> +#include <linux/if_arp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/stat.h> +#include <linux/init.h> +#include <net/lapb.h> + +static struct list_head lapb_list = LIST_HEAD_INIT(lapb_list); +static DEFINE_RWLOCK(lapb_list_lock); + +/* + *	Free an allocated lapb control block.  + */ +static void lapb_free_cb(struct lapb_cb *lapb) +{ +	kfree(lapb); +} + +static __inline__ void lapb_hold(struct lapb_cb *lapb) +{ +	atomic_inc(&lapb->refcnt); +} + +static __inline__ void lapb_put(struct lapb_cb *lapb) +{ +	if (atomic_dec_and_test(&lapb->refcnt)) +		lapb_free_cb(lapb); +} + +/* + *	Socket removal during an interrupt is now safe. + */ +static void __lapb_remove_cb(struct lapb_cb *lapb) +{ +	if (lapb->node.next) { +		list_del(&lapb->node); +		lapb_put(lapb); +	} +} + +/* + *	Add a socket to the bound sockets list. + */ +static void __lapb_insert_cb(struct lapb_cb *lapb) +{ +	list_add(&lapb->node, &lapb_list); +	lapb_hold(lapb); +} + +static struct lapb_cb *__lapb_devtostruct(struct net_device *dev) +{ +	struct list_head *entry; +	struct lapb_cb *lapb, *use = NULL; + +	list_for_each(entry, &lapb_list) { +		lapb = list_entry(entry, struct lapb_cb, node); +		if (lapb->dev == dev) { +			use = lapb; +			break; +		} +	} + +	if (use) +		lapb_hold(use); + +	return use; +} + +static struct lapb_cb *lapb_devtostruct(struct net_device *dev) +{ +	struct lapb_cb *rc; + +	read_lock_bh(&lapb_list_lock); +	rc = __lapb_devtostruct(dev); +	read_unlock_bh(&lapb_list_lock); + +	return rc; +} +/* + *	Create an empty LAPB control block. + */ +static struct lapb_cb *lapb_create_cb(void) +{ +	struct lapb_cb *lapb = kmalloc(sizeof(*lapb), GFP_ATOMIC); + + +	if (!lapb) +		goto out; + +	memset(lapb, 0x00, sizeof(*lapb)); + +	skb_queue_head_init(&lapb->write_queue); +	skb_queue_head_init(&lapb->ack_queue); + +	init_timer(&lapb->t1timer); +	init_timer(&lapb->t2timer); + +	lapb->t1      = LAPB_DEFAULT_T1; +	lapb->t2      = LAPB_DEFAULT_T2; +	lapb->n2      = LAPB_DEFAULT_N2; +	lapb->mode    = LAPB_DEFAULT_MODE; +	lapb->window  = LAPB_DEFAULT_WINDOW; +	lapb->state   = LAPB_STATE_0; +	atomic_set(&lapb->refcnt, 1); +out: +	return lapb; +} + +int lapb_register(struct net_device *dev, struct lapb_register_struct *callbacks) +{ +	struct lapb_cb *lapb; +	int rc = LAPB_BADTOKEN; + +	write_lock_bh(&lapb_list_lock); + +	lapb = __lapb_devtostruct(dev); +	if (lapb) { +		lapb_put(lapb); +		goto out; +	} + +	lapb = lapb_create_cb(); +	rc = LAPB_NOMEM; +	if (!lapb) +		goto out; + +	lapb->dev       = dev; +	lapb->callbacks = *callbacks; + +	__lapb_insert_cb(lapb); + +	lapb_start_t1timer(lapb); + +	rc = LAPB_OK; +out: +	write_unlock_bh(&lapb_list_lock); +	return rc; +} + +int lapb_unregister(struct net_device *dev) +{ +	struct lapb_cb *lapb; +	int rc = LAPB_BADTOKEN; + +	write_lock_bh(&lapb_list_lock); +	lapb = __lapb_devtostruct(dev); +	if (!lapb) +		goto out; + +	lapb_stop_t1timer(lapb); +	lapb_stop_t2timer(lapb); + +	lapb_clear_queues(lapb); + +	__lapb_remove_cb(lapb); + +	lapb_put(lapb); +	rc = LAPB_OK; +out: +	write_unlock_bh(&lapb_list_lock); +	return rc; +} + +int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms) +{ +	int rc = LAPB_BADTOKEN; +	struct lapb_cb *lapb = lapb_devtostruct(dev); + +	if (!lapb) +		goto out; + +	parms->t1      = lapb->t1 / HZ; +	parms->t2      = lapb->t2 / HZ; +	parms->n2      = lapb->n2; +	parms->n2count = lapb->n2count; +	parms->state   = lapb->state; +	parms->window  = lapb->window; +	parms->mode    = lapb->mode; + +	if (!timer_pending(&lapb->t1timer)) +		parms->t1timer = 0; +	else +		parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ; + +	if (!timer_pending(&lapb->t2timer)) +		parms->t2timer = 0; +	else +		parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ; + +	lapb_put(lapb); +	rc = LAPB_OK; +out: +	return rc; +} + +int lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms) +{ +	int rc = LAPB_BADTOKEN; +	struct lapb_cb *lapb = lapb_devtostruct(dev); + +	if (!lapb) +		goto out; + +	rc = LAPB_INVALUE; +	if (parms->t1 < 1 || parms->t2 < 1 || parms->n2 < 1) +		goto out_put; + +	if (lapb->state == LAPB_STATE_0) { +		if (((parms->mode & LAPB_EXTENDED) && +		     (parms->window < 1 || parms->window > 127)) || +		    (parms->window < 1 || parms->window > 7)) +			goto out_put; + +		lapb->mode    = parms->mode; +		lapb->window  = parms->window; +	} + +	lapb->t1    = parms->t1 * HZ; +	lapb->t2    = parms->t2 * HZ; +	lapb->n2    = parms->n2; + +	rc = LAPB_OK; +out_put: +	lapb_put(lapb); +out: +	return rc; +} + +int lapb_connect_request(struct net_device *dev) +{ +	struct lapb_cb *lapb = lapb_devtostruct(dev); +	int rc = LAPB_BADTOKEN; + +	if (!lapb) +		goto out; + +	rc = LAPB_OK; +	if (lapb->state == LAPB_STATE_1) +		goto out_put; + +	rc = LAPB_CONNECTED; +	if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4) +		goto out_put; + +	lapb_establish_data_link(lapb); + +#if LAPB_DEBUG > 0 +	printk(KERN_DEBUG "lapb: (%p) S0 -> S1\n", lapb->dev); +#endif +	lapb->state = LAPB_STATE_1; + +	rc = LAPB_OK; +out_put: +	lapb_put(lapb); +out: +	return rc; +} + +int lapb_disconnect_request(struct net_device *dev) +{ +	struct lapb_cb *lapb = lapb_devtostruct(dev); +	int rc = LAPB_BADTOKEN; + +	if (!lapb) +		goto out; + +	switch (lapb->state) { +		case LAPB_STATE_0: +			rc = LAPB_NOTCONNECTED; +			goto out_put; + +		case LAPB_STATE_1: +#if LAPB_DEBUG > 1 +			printk(KERN_DEBUG "lapb: (%p) S1 TX DISC(1)\n", lapb->dev); +#endif +#if LAPB_DEBUG > 0 +			printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->dev); +#endif +			lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); +			lapb->state = LAPB_STATE_0; +			lapb_start_t1timer(lapb); +			rc = LAPB_NOTCONNECTED; +			goto out_put; + +		case LAPB_STATE_2: +			rc = LAPB_OK; +			goto out_put; +	} + +	lapb_clear_queues(lapb); +	lapb->n2count = 0; +	lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); +	lapb_start_t1timer(lapb); +	lapb_stop_t2timer(lapb); +	lapb->state = LAPB_STATE_2; + +#if LAPB_DEBUG > 1 +	printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->dev); +#endif +#if LAPB_DEBUG > 0 +	printk(KERN_DEBUG "lapb: (%p) S3 -> S2\n", lapb->dev); +#endif + +	rc = LAPB_OK; +out_put: +	lapb_put(lapb); +out: +	return rc; +} + +int lapb_data_request(struct net_device *dev, struct sk_buff *skb) +{ +	struct lapb_cb *lapb = lapb_devtostruct(dev); +	int rc = LAPB_BADTOKEN; + +	if (!lapb) +		goto out; + +	rc = LAPB_NOTCONNECTED; +	if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4) +		goto out_put; + +	skb_queue_tail(&lapb->write_queue, skb); +	lapb_kick(lapb); +	rc = LAPB_OK; +out_put: +	lapb_put(lapb); +out: +	return rc; +} + +int lapb_data_received(struct net_device *dev, struct sk_buff *skb) +{ +	struct lapb_cb *lapb = lapb_devtostruct(dev); +	int rc = LAPB_BADTOKEN; + +	if (lapb) { +		lapb_data_input(lapb, skb); +		lapb_put(lapb); +		rc = LAPB_OK; +	} + +	return rc; +} + +void lapb_connect_confirmation(struct lapb_cb *lapb, int reason) +{ +	if (lapb->callbacks.connect_confirmation) +		lapb->callbacks.connect_confirmation(lapb->dev, reason); +} + +void lapb_connect_indication(struct lapb_cb *lapb, int reason) +{ +	if (lapb->callbacks.connect_indication) +		lapb->callbacks.connect_indication(lapb->dev, reason); +} + +void lapb_disconnect_confirmation(struct lapb_cb *lapb, int reason) +{ +	if (lapb->callbacks.disconnect_confirmation) +		lapb->callbacks.disconnect_confirmation(lapb->dev, reason); +} + +void lapb_disconnect_indication(struct lapb_cb *lapb, int reason) +{ +	if (lapb->callbacks.disconnect_indication) +		lapb->callbacks.disconnect_indication(lapb->dev, reason); +} + +int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb) +{ +	if (lapb->callbacks.data_indication) +		return lapb->callbacks.data_indication(lapb->dev, skb); + +	kfree_skb(skb); +	return NET_RX_CN_HIGH; /* For now; must be != NET_RX_DROP */  +} + +int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb) +{ +	int used = 0; + +	if (lapb->callbacks.data_transmit) { +		lapb->callbacks.data_transmit(lapb->dev, skb); +		used = 1; +	} + +	return used; +} + +EXPORT_SYMBOL(lapb_register); +EXPORT_SYMBOL(lapb_unregister); +EXPORT_SYMBOL(lapb_getparms); +EXPORT_SYMBOL(lapb_setparms); +EXPORT_SYMBOL(lapb_connect_request); +EXPORT_SYMBOL(lapb_disconnect_request); +EXPORT_SYMBOL(lapb_data_request); +EXPORT_SYMBOL(lapb_data_received); + +static int __init lapb_init(void) +{ +	return 0; +} + +static void __exit lapb_exit(void) +{ +	WARN_ON(!list_empty(&lapb_list)); +} + +MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>"); +MODULE_DESCRIPTION("The X.25 Link Access Procedure B link layer protocol"); +MODULE_LICENSE("GPL"); + +module_init(lapb_init); +module_exit(lapb_exit); | 
