lapb_iface.c revision d97a077a15ae21e161e74def7762caa99200e4cf
1/*
2 *	LAPB release 002
3 *
4 *	This code REQUIRES 2.1.15 or higher/ NET3.038
5 *
6 *	This module:
7 *		This module is free software; you can redistribute it and/or
8 *		modify it under the terms of the GNU General Public License
9 *		as published by the Free Software Foundation; either version
10 *		2 of the License, or (at your option) any later version.
11 *
12 *	History
13 *	LAPB 001	Jonathan Naylor	Started Coding
14 *	LAPB 002	Jonathan Naylor	New timer architecture.
15 *	2000-10-29	Henner Eisen	lapb_data_indication() return status.
16 */
17
18#include <linux/module.h>
19#include <linux/errno.h>
20#include <linux/types.h>
21#include <linux/socket.h>
22#include <linux/in.h>
23#include <linux/kernel.h>
24#include <linux/jiffies.h>
25#include <linux/timer.h>
26#include <linux/string.h>
27#include <linux/sockios.h>
28#include <linux/net.h>
29#include <linux/inet.h>
30#include <linux/if_arp.h>
31#include <linux/skbuff.h>
32#include <linux/slab.h>
33#include <net/sock.h>
34#include <asm/uaccess.h>
35#include <asm/system.h>
36#include <linux/fcntl.h>
37#include <linux/mm.h>
38#include <linux/interrupt.h>
39#include <linux/stat.h>
40#include <linux/init.h>
41#include <net/lapb.h>
42
43static LIST_HEAD(lapb_list);
44static DEFINE_RWLOCK(lapb_list_lock);
45
46/*
47 *	Free an allocated lapb control block.
48 */
49static void lapb_free_cb(struct lapb_cb *lapb)
50{
51	kfree(lapb);
52}
53
54static __inline__ void lapb_hold(struct lapb_cb *lapb)
55{
56	atomic_inc(&lapb->refcnt);
57}
58
59static __inline__ void lapb_put(struct lapb_cb *lapb)
60{
61	if (atomic_dec_and_test(&lapb->refcnt))
62		lapb_free_cb(lapb);
63}
64
65/*
66 *	Socket removal during an interrupt is now safe.
67 */
68static void __lapb_remove_cb(struct lapb_cb *lapb)
69{
70	if (lapb->node.next) {
71		list_del(&lapb->node);
72		lapb_put(lapb);
73	}
74}
75
76/*
77 *	Add a socket to the bound sockets list.
78 */
79static void __lapb_insert_cb(struct lapb_cb *lapb)
80{
81	list_add(&lapb->node, &lapb_list);
82	lapb_hold(lapb);
83}
84
85static struct lapb_cb *__lapb_devtostruct(struct net_device *dev)
86{
87	struct list_head *entry;
88	struct lapb_cb *lapb, *use = NULL;
89
90	list_for_each(entry, &lapb_list) {
91		lapb = list_entry(entry, struct lapb_cb, node);
92		if (lapb->dev == dev) {
93			use = lapb;
94			break;
95		}
96	}
97
98	if (use)
99		lapb_hold(use);
100
101	return use;
102}
103
104static struct lapb_cb *lapb_devtostruct(struct net_device *dev)
105{
106	struct lapb_cb *rc;
107
108	read_lock_bh(&lapb_list_lock);
109	rc = __lapb_devtostruct(dev);
110	read_unlock_bh(&lapb_list_lock);
111
112	return rc;
113}
114/*
115 *	Create an empty LAPB control block.
116 */
117static struct lapb_cb *lapb_create_cb(void)
118{
119	struct lapb_cb *lapb = kzalloc(sizeof(*lapb), GFP_ATOMIC);
120
121
122	if (!lapb)
123		goto out;
124
125	skb_queue_head_init(&lapb->write_queue);
126	skb_queue_head_init(&lapb->ack_queue);
127
128	init_timer(&lapb->t1timer);
129	init_timer(&lapb->t2timer);
130
131	lapb->t1      = LAPB_DEFAULT_T1;
132	lapb->t2      = LAPB_DEFAULT_T2;
133	lapb->n2      = LAPB_DEFAULT_N2;
134	lapb->mode    = LAPB_DEFAULT_MODE;
135	lapb->window  = LAPB_DEFAULT_WINDOW;
136	lapb->state   = LAPB_STATE_0;
137	atomic_set(&lapb->refcnt, 1);
138out:
139	return lapb;
140}
141
142int lapb_register(struct net_device *dev,
143		  const struct lapb_register_struct *callbacks)
144{
145	struct lapb_cb *lapb;
146	int rc = LAPB_BADTOKEN;
147
148	write_lock_bh(&lapb_list_lock);
149
150	lapb = __lapb_devtostruct(dev);
151	if (lapb) {
152		lapb_put(lapb);
153		goto out;
154	}
155
156	lapb = lapb_create_cb();
157	rc = LAPB_NOMEM;
158	if (!lapb)
159		goto out;
160
161	lapb->dev       = dev;
162	lapb->callbacks = callbacks;
163
164	__lapb_insert_cb(lapb);
165
166	lapb_start_t1timer(lapb);
167
168	rc = LAPB_OK;
169out:
170	write_unlock_bh(&lapb_list_lock);
171	return rc;
172}
173
174int lapb_unregister(struct net_device *dev)
175{
176	struct lapb_cb *lapb;
177	int rc = LAPB_BADTOKEN;
178
179	write_lock_bh(&lapb_list_lock);
180	lapb = __lapb_devtostruct(dev);
181	if (!lapb)
182		goto out;
183
184	lapb_stop_t1timer(lapb);
185	lapb_stop_t2timer(lapb);
186
187	lapb_clear_queues(lapb);
188
189	__lapb_remove_cb(lapb);
190
191	lapb_put(lapb);
192	rc = LAPB_OK;
193out:
194	write_unlock_bh(&lapb_list_lock);
195	return rc;
196}
197
198int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms)
199{
200	int rc = LAPB_BADTOKEN;
201	struct lapb_cb *lapb = lapb_devtostruct(dev);
202
203	if (!lapb)
204		goto out;
205
206	parms->t1      = lapb->t1 / HZ;
207	parms->t2      = lapb->t2 / HZ;
208	parms->n2      = lapb->n2;
209	parms->n2count = lapb->n2count;
210	parms->state   = lapb->state;
211	parms->window  = lapb->window;
212	parms->mode    = lapb->mode;
213
214	if (!timer_pending(&lapb->t1timer))
215		parms->t1timer = 0;
216	else
217		parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ;
218
219	if (!timer_pending(&lapb->t2timer))
220		parms->t2timer = 0;
221	else
222		parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ;
223
224	lapb_put(lapb);
225	rc = LAPB_OK;
226out:
227	return rc;
228}
229
230int lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms)
231{
232	int rc = LAPB_BADTOKEN;
233	struct lapb_cb *lapb = lapb_devtostruct(dev);
234
235	if (!lapb)
236		goto out;
237
238	rc = LAPB_INVALUE;
239	if (parms->t1 < 1 || parms->t2 < 1 || parms->n2 < 1)
240		goto out_put;
241
242	if (lapb->state == LAPB_STATE_0) {
243		if (parms->mode & LAPB_EXTENDED) {
244			if (parms->window < 1 || parms->window > 127)
245				goto out_put;
246		} else {
247			if (parms->window < 1 || parms->window > 7)
248				goto out_put;
249		}
250		lapb->mode    = parms->mode;
251		lapb->window  = parms->window;
252	}
253
254	lapb->t1    = parms->t1 * HZ;
255	lapb->t2    = parms->t2 * HZ;
256	lapb->n2    = parms->n2;
257
258	rc = LAPB_OK;
259out_put:
260	lapb_put(lapb);
261out:
262	return rc;
263}
264
265int lapb_connect_request(struct net_device *dev)
266{
267	struct lapb_cb *lapb = lapb_devtostruct(dev);
268	int rc = LAPB_BADTOKEN;
269
270	if (!lapb)
271		goto out;
272
273	rc = LAPB_OK;
274	if (lapb->state == LAPB_STATE_1)
275		goto out_put;
276
277	rc = LAPB_CONNECTED;
278	if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4)
279		goto out_put;
280
281	lapb_establish_data_link(lapb);
282
283#if LAPB_DEBUG > 0
284	printk(KERN_DEBUG "lapb: (%p) S0 -> S1\n", lapb->dev);
285#endif
286	lapb->state = LAPB_STATE_1;
287
288	rc = LAPB_OK;
289out_put:
290	lapb_put(lapb);
291out:
292	return rc;
293}
294
295int lapb_disconnect_request(struct net_device *dev)
296{
297	struct lapb_cb *lapb = lapb_devtostruct(dev);
298	int rc = LAPB_BADTOKEN;
299
300	if (!lapb)
301		goto out;
302
303	switch (lapb->state) {
304	case LAPB_STATE_0:
305		rc = LAPB_NOTCONNECTED;
306		goto out_put;
307
308	case LAPB_STATE_1:
309#if LAPB_DEBUG > 1
310		printk(KERN_DEBUG "lapb: (%p) S1 TX DISC(1)\n", lapb->dev);
311#endif
312#if LAPB_DEBUG > 0
313		printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->dev);
314#endif
315		lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
316		lapb->state = LAPB_STATE_0;
317		lapb_start_t1timer(lapb);
318		rc = LAPB_NOTCONNECTED;
319		goto out_put;
320
321	case LAPB_STATE_2:
322		rc = LAPB_OK;
323		goto out_put;
324	}
325
326	lapb_clear_queues(lapb);
327	lapb->n2count = 0;
328	lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
329	lapb_start_t1timer(lapb);
330	lapb_stop_t2timer(lapb);
331	lapb->state = LAPB_STATE_2;
332
333#if LAPB_DEBUG > 1
334	printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->dev);
335#endif
336#if LAPB_DEBUG > 0
337	printk(KERN_DEBUG "lapb: (%p) S3 -> S2\n", lapb->dev);
338#endif
339
340	rc = LAPB_OK;
341out_put:
342	lapb_put(lapb);
343out:
344	return rc;
345}
346
347int lapb_data_request(struct net_device *dev, struct sk_buff *skb)
348{
349	struct lapb_cb *lapb = lapb_devtostruct(dev);
350	int rc = LAPB_BADTOKEN;
351
352	if (!lapb)
353		goto out;
354
355	rc = LAPB_NOTCONNECTED;
356	if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4)
357		goto out_put;
358
359	skb_queue_tail(&lapb->write_queue, skb);
360	lapb_kick(lapb);
361	rc = LAPB_OK;
362out_put:
363	lapb_put(lapb);
364out:
365	return rc;
366}
367
368int lapb_data_received(struct net_device *dev, struct sk_buff *skb)
369{
370	struct lapb_cb *lapb = lapb_devtostruct(dev);
371	int rc = LAPB_BADTOKEN;
372
373	if (lapb) {
374		lapb_data_input(lapb, skb);
375		lapb_put(lapb);
376		rc = LAPB_OK;
377	}
378
379	return rc;
380}
381
382void lapb_connect_confirmation(struct lapb_cb *lapb, int reason)
383{
384	if (lapb->callbacks->connect_confirmation)
385		lapb->callbacks->connect_confirmation(lapb->dev, reason);
386}
387
388void lapb_connect_indication(struct lapb_cb *lapb, int reason)
389{
390	if (lapb->callbacks->connect_indication)
391		lapb->callbacks->connect_indication(lapb->dev, reason);
392}
393
394void lapb_disconnect_confirmation(struct lapb_cb *lapb, int reason)
395{
396	if (lapb->callbacks->disconnect_confirmation)
397		lapb->callbacks->disconnect_confirmation(lapb->dev, reason);
398}
399
400void lapb_disconnect_indication(struct lapb_cb *lapb, int reason)
401{
402	if (lapb->callbacks->disconnect_indication)
403		lapb->callbacks->disconnect_indication(lapb->dev, reason);
404}
405
406int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb)
407{
408	if (lapb->callbacks->data_indication)
409		return lapb->callbacks->data_indication(lapb->dev, skb);
410
411	kfree_skb(skb);
412	return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */
413}
414
415int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb)
416{
417	int used = 0;
418
419	if (lapb->callbacks->data_transmit) {
420		lapb->callbacks->data_transmit(lapb->dev, skb);
421		used = 1;
422	}
423
424	return used;
425}
426
427EXPORT_SYMBOL(lapb_register);
428EXPORT_SYMBOL(lapb_unregister);
429EXPORT_SYMBOL(lapb_getparms);
430EXPORT_SYMBOL(lapb_setparms);
431EXPORT_SYMBOL(lapb_connect_request);
432EXPORT_SYMBOL(lapb_disconnect_request);
433EXPORT_SYMBOL(lapb_data_request);
434EXPORT_SYMBOL(lapb_data_received);
435
436static int __init lapb_init(void)
437{
438	return 0;
439}
440
441static void __exit lapb_exit(void)
442{
443	WARN_ON(!list_empty(&lapb_list));
444}
445
446MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
447MODULE_DESCRIPTION("The X.25 Link Access Procedure B link layer protocol");
448MODULE_LICENSE("GPL");
449
450module_init(lapb_init);
451module_exit(lapb_exit);
452