aboutsummaryrefslogtreecommitdiffstats
path: root/net/wanrouter/wanmain.c
blob: 2bf23406637ae1eb849abb8633fd4658f5cf3332 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
/*****************************************************************************
* wanmain.c	WAN Multiprotocol Router Module. Main code.
*
*		This module is completely hardware-independent and provides
*		the following common services for the WAN Link Drivers:
*		 o WAN device management (registering, unregistering)
*		 o Network interface management
*		 o Physical connection management (dial-up, incoming calls)
*		 o Logical connection management (switched virtual circuits)
*		 o Protocol encapsulation/decapsulation
*
* Author:	Gideon Hack
*
* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
*
*		This program 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.
* ============================================================================
* Nov 24, 2000  Nenad Corbic	Updated for 2.4.X kernels
* Nov 07, 2000  Nenad Corbic	Fixed the Mulit-Port PPP for kernels 2.2.16 and
*  				greater.
* Aug 2,  2000  Nenad Corbic	Block the Multi-Port PPP from running on
*  			        kernels 2.2.16 or greater.  The SyncPPP
*  			        has changed.
* Jul 13, 2000  Nenad Corbic	Added SyncPPP support
* 				Added extra debugging in device_setup().
* Oct 01, 1999  Gideon Hack     Update for s514 PCI card
* Dec 27, 1996	Gene Kozin	Initial version (based on Sangoma's WANPIPE)
* Jan 16, 1997	Gene Kozin	router_devlist made public
* Jan 31, 1997  Alan Cox	Hacked it about a bit for 2.1
* Jun 27, 1997  Alan Cox	realigned with vendor code
* Oct 15, 1997  Farhan Thawar   changed wan_encapsulate to add a pad byte of 0
* Apr 20, 1998	Alan Cox	Fixed 2.1 symbols
* May 17, 1998  K. Baranowski	Fixed SNAP encapsulation in wan_encapsulate
* Dec 15, 1998  Arnaldo Melo    support for firmwares of up to 128000 bytes
*                               check wandev->setup return value
* Dec 22, 1998  Arnaldo Melo    vmalloc/vfree used in device_setup to allocate
*                               kernel memory and copy configuration data to
*                               kernel space (for big firmwares)
* Jun 02, 1999  Gideon Hack	Updates for Linux 2.0.X and 2.2.X kernels.
*****************************************************************************/

#include <linux/stddef.h>	/* offsetof(), etc. */
#include <linux/capability.h>
#include <linux/errno.h>	/* return codes */
#include <linux/kernel.h>
#include <linux/module.h>	/* support for loadable modules */
#include <linux/slab.h>		/* kmalloc(), kfree() */
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/string.h>	/* inline mem*, str* functions */

#include <asm/byteorder.h>	/* htons(), etc. */
#include <linux/wanrouter.h>	/* WAN router API definitions */

#include <linux/vmalloc.h>	/* vmalloc, vfree */
#include <asm/uaccess.h>        /* copy_to/from_user */
#include <linux/init.h>         /* __initfunc et al. */

#define KMEM_SAFETYZONE 8

#define DEV_TO_SLAVE(dev)	(*((struct net_device **)netdev_priv(dev)))

/*
 * 	Function Prototypes
 */

/*
 *	WAN device IOCTL handlers
 */

static DEFINE_MUTEX(wanrouter_mutex);
static int wanrouter_device_setup(struct wan_device *wandev,
				  wandev_conf_t __user *u_conf);
static int wanrouter_device_stat(struct wan_device *wandev,
				 wandev_stat_t __user *u_stat);
static int wanrouter_device_shutdown(struct wan_device *wandev);
static int wanrouter_device_new_if(struct wan_device *wandev,
				   wanif_conf_t __user *u_conf);
static int wanrouter_device_del_if(struct wan_device *wandev,
				   char __user *u_name);

/*
 *	Miscellaneous
 */

static struct wan_device *wanrouter_find_device(char *name);
static int wanrouter_delete_interface(struct wan_device *wandev, char *name);
static void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
	__acquires(lock);
static void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
	__releases(lock);



/*
 *	Global Data
 */

static char wanrouter_fullname[]  = "Sangoma WANPIPE Router";
static char wanrouter_copyright[] = "(c) 1995-2000 Sangoma Technologies Inc.";
static char wanrouter_modname[] = ROUTER_NAME; /* short module name */
struct wan_device* wanrouter_router_devlist; /* list of registered devices */

/*
 *	Organize Unique Identifiers for encapsulation/decapsulation
 */

#if 0
static unsigned char wanrouter_oui_ether[] = { 0x00, 0x00, 0x00 };
static unsigned char wanrouter_oui_802_2[] = { 0x00, 0x80, 0xC2 };
#endif

static int __init wanrouter_init(void)
{
	int err;

	printk(KERN_INFO "%s v%u.%u %s\n",
	       wanrouter_fullname, ROUTER_VERSION, ROUTER_RELEASE,
	       wanrouter_copyright);

	err = wanrouter_proc_init();
	if (err)
		printk(KERN_INFO "%s: can't create entry in proc filesystem!\n",
		       wanrouter_modname);

	return err;
}

static void __exit wanrouter_cleanup (void)
{
	wanrouter_proc_cleanup();
}

/*
 * This is just plain dumb.  We should move the bugger to drivers/net/wan,
 * slap it first in directory and make it module_init().  The only reason
 * for subsys_initcall() here is that net goes after drivers (why, BTW?)
 */
subsys_initcall(wanrouter_init);
module_exit(wanrouter_cleanup);

/*
 * 	Kernel APIs
 */

/*
 * 	Register WAN device.
 * 	o verify device credentials
 * 	o create an entry for the device in the /proc/net/router directory
 * 	o initialize internally maintained fields of the wan_device structure
 * 	o link device data space to a singly-linked list
 * 	o if it's the first device, then start kernel 'thread'
 * 	o increment module use count
 *
 * 	Return:
 *	0	Ok
 *	< 0	error.
 *
 * 	Context:	process
 */


int register_wan_device(struct wan_device *wandev)
{
	int err, namelen;

	if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC) ||
	    (wandev->name == NULL))
		return -EINVAL;

	namelen = strlen(wandev->name);
	if (!namelen || (namelen > WAN_DRVNAME_SZ))
		return -EINVAL;

	if (wanrouter_find_device(wandev->name))
		return -EEXIST;

#ifdef WANDEBUG
	printk(KERN_INFO "%s: registering WAN device %s\n",
	       wanrouter_modname, wandev->name);
#endif

	/*
	 *	Register /proc directory entry
	 */
	err = wanrouter_proc_add(wandev);
	if (err) {
		printk(KERN_INFO
			"%s: can't create /proc/net/router/%s entry!\n",
			wanrouter_modname, wandev->name);
		return err;
	}

	/*
	 *	Initialize fields of the wan_device structure maintained by the
	 *	router and update local data.
	 */

	wandev->ndev = 0;
	wandev->dev  = NULL;
	wandev->next = wanrouter_router_devlist;
	wanrouter_router_devlist = wandev;
	return 0;
}

/*
 *	Unregister WAN device.
 *	o shut down device
 *	o unlink device data space from the linked list
 *	o delete device entry in the /proc/net/router directory
 *	o decrement module use count
 *
 *	Return:		0	Ok
 *			<0	error.
 *	Context:	process
 */


int unregister_wan_device(char *name)
{
	struct wan_device *wandev, *prev;

	if (name == NULL)
		return -EINVAL;

	for (wandev = wanrouter_router_devlist, prev = NULL;
		wandev && strcmp(wandev->name, name);
		prev = wandev, wandev = wandev->next)
		;
	if (wandev == NULL)
		return -ENODEV;

#ifdef WANDEBUG
	printk(KERN_INFO "%s: unregistering WAN device %s\n",
	       wanrouter_modname, name);
#endif

	if (wandev->state != WAN_UNCONFIGURED)
		wanrouter_device_shutdown(wandev);

	if (prev)
		prev->next = wandev->next;
	else
		wanrouter_router_devlist = wandev->next;

	wanrouter_proc_delete(wandev);
	return 0;
}

#if 0

/*
 *	Encapsulate packet.
 *
 *	Return:	encapsulation header size
 *		< 0	- unsupported Ethertype
 *
 *	Notes:
 *	1. This function may be called on interrupt context.
 */


int wanrouter_encapsulate(struct sk_buff *skb, struct net_device *dev,
			  unsigned short type)
{
	int hdr_len = 0;

	switch (type) {
	case ETH_P_IP:		/* IP datagram encapsulation */
		hdr_len += 1;
		skb_push(skb, 1);
		skb->data[0] = NLPID_IP;
		break;

	case ETH_P_IPX:		/* SNAP encapsulation */
	case ETH_P_ARP:
		hdr_len += 7;
		skb_push(skb, 7);
		skb->data[0] = 0;
		skb->data[1] = NLPID_SNAP;
		skb_copy_to_linear_data_offset(skb, 2, wanrouter_oui_ether,
					       sizeof(wanrouter_oui_ether));
		*((unsigned short*)&skb->data[5]) = htons(type);
		break;

	default:		/* Unknown packet type */
		printk(KERN_INFO
			"%s: unsupported Ethertype 0x%04X on interface %s!\n",
			wanrouter_modname, type, dev->name);
		hdr_len = -EINVAL;
	}
	return hdr_len;
}


/*
 *	Decapsulate packet.
 *
 *	Return:	Ethertype (in network order)
 *			0	unknown encapsulation
 *
 *	Notes:
 *	1. This function may be called on interrupt context.
 */


__be16 wanrouter_type_trans(struct sk_buff *skb, struct net_device *dev)
{
	int cnt = skb->data[0] ? 0 : 1;	/* there may be a pad present */
	__be16 ethertype;

	switch (skb->data[cnt]) {
	case NLPID_IP:		/* IP datagramm */
		ethertype = htons(ETH_P_IP);
		cnt += 1;
		break;

	case NLPID_SNAP:	/* SNAP encapsulation */
		if (memcmp(&skb->data[cnt + 1], wanrouter_oui_ether,
			   sizeof(wanrouter_oui_ether))){
			printk(KERN_INFO
				"%s: unsupported SNAP OUI %02X-%02X-%02X "
				"on interface %s!\n", wanrouter_modname,
				skb->data[cnt+1], skb->data[cnt+2],
				skb->data[cnt+3], dev->name);
			return 0;
		}
		ethertype = *((__be16*)&skb->data[cnt+4]);
		cnt += 6;
		break;

	/* add other protocols, e.g. CLNP, ESIS, ISIS, if needed */

	default:
		printk(KERN_INFO
			"%s: unsupported NLPID 0x%02X on interface %s!\n",
			wanrouter_modname, skb->data[cnt], dev->name);
		return 0;
	}
	skb->protocol = ethertype;
	skb->pkt_type = PACKET_HOST;	/*	Physically point to point */
	skb_pull(skb, cnt);
	skb_reset_mac_header(skb);
	return ethertype;
}

#endif  /*  0  */

/*
 *	WAN device IOCTL.
 *	o find WAN device associated with this node
 *	o execute requested action or pass command to the device driver
 */

long wanrouter_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct inode *inode = file->f_path.dentry->d_inode;
	int err = 0;
	struct proc_dir_entry *dent;
	struct wan_device *wandev;
	void __user *data = (void __user *)arg;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	if ((cmd >> 8) != ROUTER_IOCTL)
		return -EINVAL;

	dent = PDE(inode);
	if ((dent == NULL) || (dent->data == NULL))
		return -EINVAL;

	wandev = dent->data;
	if (wandev->magic != ROUTER_MAGIC)
		return -EINVAL;

	mutex_lock(&wanrouter_mutex);
	switch (cmd) {
	case ROUTER_SETUP:
		err = wanrouter_device_setup(wandev, data);
		break;

	case ROUTER_DOWN:
		err = wanrouter_device_shutdown(wandev);
		break;

	case ROUTER_STAT:
		err = wanrouter_device_stat(wandev, data);
		break;

	case ROUTER_IFNEW:
		err = wanrouter_device_new_if(wandev, data);
		break;

	case ROUTER_IFDEL:
		err = wanrouter_device_del_if(wandev, data);
		break;

	case ROUTER_IFSTAT:
		break;

	default:
		if ((cmd >= ROUTER_USER) &&
		    (cmd <= ROUTER_USER_MAX) &&
		    wandev->ioctl)
			err = wandev->ioctl(wandev, cmd, arg);
		else err = -EINVAL;
	}
	mutex_unlock(&wanrouter_mutex);
	return err;
}

/*
 *	WAN Driver IOCTL Handlers
 */

/*
 *	Setup WAN link device.
 *	o verify user address space
 *	o allocate kernel memory and copy configuration data to kernel space
 *	o if configuration data includes extension, copy it to kernel space too
 *	o call driver's setup() entry point
 */

static int wanrouter_device_setup(struct wan_device *wandev,
				  wandev_conf_t __user *u_conf)
{
	void *data = NULL;
	wandev_conf_t *conf;
	int err = -EINVAL;

	if (wandev->setup == NULL) {	/* Nothing to do ? */
		printk(KERN_INFO "%s: ERROR, No setup script: wandev->setup()\n",
				wandev->name);
		return 0;
	}

	conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL);
	if (conf == NULL){
		printk(KERN_INFO "%s: ERROR, Failed to allocate kernel memory !\n",
				wandev->name);
		return -ENOBUFS;
	}

	if (copy_from_user(conf, u_conf, sizeof(wandev_conf_t))) {
		printk(KERN_INFO "%s: Failed to copy user config data to kernel space!\n",
				wandev->name);
		kfree(conf);
		return -EFAULT;
	}

	if (conf->magic != ROUTER_MAGIC) {
		kfree(conf);
		printk(KERN_INFO "%s: ERROR, Invalid MAGIC Number\n",
				wandev->name);
		return -EINVAL;
	}

	if (conf->data_size && conf->data) {
		if (conf->data_size > 128000) {
			printk(KERN_INFO
			    "%s: ERROR, Invalid firmware data size %i !\n",
					wandev->name, conf->data_size);
			kfree(conf);
			return -EINVAL;
		}

		data = vmalloc(conf->data_size);
		if (!data) {
			printk(KERN_INFO
				"%s: ERROR, Faild allocate kernel memory !\n",
				wandev->name);
			kfree(conf);
			return -ENOBUFS;
		}
		if (!copy_from_user(data, conf->data, conf->data_size)) {
			conf->data = data;
			err = wandev->setup(wandev, conf);
		} else {
			printk(KERN_INFO
			     "%s: ERROR, Faild to copy from user data !\n",
			       wandev->name);
			err = -EFAULT;
		}
		vfree(data);
	} else {
		printk(KERN_INFO
		    "%s: ERROR, No firmware found ! Firmware size = %i !\n",
				wandev->name, conf->data_size);
	}

	kfree(conf);
	return err;
}

/*
 *	Shutdown WAN device.
 *	o delete all not opened logical channels for this device
 *	o call driver's shutdown() entry point
 */

static int wanrouter_device_shutdown(struct wan_device *wandev)
{
	struct net_device *dev;
	int err=0;

	if (wandev->state == WAN_UNCONFIGURED)
		return 0;

	printk(KERN_INFO "\n%s: Shutting Down!\n",wandev->name);

	for (dev = wandev->dev; dev;) {
		err = wanrouter_delete_interface(wandev, dev->name);
		if (err)
			return err;
		/* The above function deallocates the current dev
		 * structure. Therefore, we cannot use netdev_priv(dev)
		 * as the next element: wandev->dev points to the
		 * next element */
		dev = wandev->dev;
	}

	if (wandev->ndev)
		return -EBUSY;	/* there are opened interfaces  */

	if (wandev->shutdown)
		err=wandev->shutdown(wandev);

	return err;
}

/*
 *	Get WAN device status & statistics.
 */

static int wanrouter_device_stat(struct wan_device *wandev,
				 wandev_stat_t __user *u_stat)
{
	wandev_stat_t stat;

	memset(&stat, 0, sizeof(stat));

	/* Ask device driver to update device statistics */
	if ((wandev->state != WAN_UNCONFIGURED) && wandev->update)
		wandev->update(wandev);

	/* Fill out structure */
	stat.ndev  = wandev->ndev;
	stat.state = wandev->state;

	if (copy_to_user(u_stat, &stat, sizeof(stat)))
		return -EFAULT;

	return 0;
}

/*
 *	Create new WAN interface.
 *	o verify user address space
 *	o copy configuration data to kernel address space
 *	o allocate network interface data space
 *	o call driver's new_if() entry point
 *	o make sure there is no interface name conflict
 *	o register network interface
 */

static int wanrouter_device_new_if(struct wan_device *wandev,
				   wanif_conf_t __user *u_conf)
{
	wanif_conf_t *cnf;
	struct net_device *dev = NULL;
	int err;

	if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL))
		return -ENODEV;

	cnf = kmalloc(sizeof(wanif_conf_t), GFP_KERNEL);
	if (!cnf)
		return -ENOBUFS;

	err = -EFAULT;
	if (copy_from_user(cnf, u_conf, sizeof(wanif_conf_t)))
		goto out;

	err = -EINVAL;
	if (cnf->magic != ROUTER_MAGIC)
		goto out;

	if (cnf->config_id == WANCONFIG_MPPP) {
		printk(KERN_INFO "%s: Wanpipe Mulit-Port PPP support has not been compiled in!\n",
				wandev->name);
		err = -EPROTONOSUPPORT;
		goto out;
	} else {
		err = wandev->new_if(wandev, dev, cnf);
	}

	if (!err) {
		/* Register network interface. This will invoke init()
		 * function supplied by the driver.  If device registered
		 * successfully, add it to the interface list.
		 */

		if (dev->name == NULL) {
			err = -EINVAL;
		} else {

			#ifdef WANDEBUG
			printk(KERN_INFO "%s: registering interface %s...\n",
				wanrouter_modname, dev->name);
			#endif

			err = register_netdev(dev);
			if (!err) {
				struct net_device *slave = NULL;
				unsigned long smp_flags=0;

				lock_adapter_irq(&wandev->lock, &smp_flags);

				if (wandev->dev == NULL) {
					wandev->dev = dev;
				} else {
					for (slave=wandev->dev;
					     DEV_TO_SLAVE(slave);
					     slave = DEV_TO_SLAVE(slave))
						DEV_TO_SLAVE(slave) = dev;
				}
				++wandev->ndev;

				unlock_adapter_irq(&wandev->lock, &smp_flags);
				err = 0;	/* done !!! */
				goto out;
			}
		}
		if (wandev->del_if)
			wandev->del_if(wandev, dev);
		free_netdev(dev);
	}

out:
	kfree(cnf);
	return err;
}


/*
 *	Delete WAN logical channel.
 *	 o verify user address space
 *	 o copy configuration data to kernel address space
 */

static int wanrouter_device_del_if(struct wan_device *wandev, char __user *u_name)
{
	char name[WAN_IFNAME_SZ + 1];
	int err = 0;

	if (wandev->state == WAN_UNCONFIGURED)
		return -ENODEV;

	memset(name, 0, sizeof(name));

	if (copy_from_user(name, u_name, WAN_IFNAME_SZ))
		return -EFAULT;

	err = wanrouter_delete_interface(wandev, name);
	if (err)
		return err;

	/* If last interface being deleted, shutdown card
	 * This helps with administration at leaf nodes
	 * (You can tell if the person at the other end of the phone
	 * has an interface configured) and avoids DoS vulnerabilities
	 * in binary driver files - this fixes a problem with the current
	 * Sangoma driver going into strange states when all the network
	 * interfaces are deleted and the link irrecoverably disconnected.
	 */

	if (!wandev->ndev && wandev->shutdown)
		err = wandev->shutdown(wandev);

	return err;
}

/*
 *	Miscellaneous Functions
 */

/*
 *	Find WAN device by name.
 *	Return pointer to the WAN device data space or NULL if device not found.
 */

static struct wan_device *wanrouter_find_device(char *name)
{
	struct wan_device *wandev;

	for (wandev = wanrouter_router_devlist;
	     wandev && strcmp(wandev->name, name);
		wandev = wandev->next);
	return wandev;
}

/*
 *	Delete WAN logical channel identified by its name.
 *	o find logical channel by its name
 *	o call driver's del_if() entry point
 *	o unregister network interface
 *	o unlink channel data space from linked list of channels
 *	o release channel data space
 *
 *	Return:	0		success
 *		-ENODEV		channel not found.
 *		-EBUSY		interface is open
 *
 *	Note: If (force != 0), then device will be destroyed even if interface
 *	associated with it is open. It's caller's responsibility to make
 *	sure that opened interfaces are not removed!
 */

static int wanrouter_delete_interface(struct wan_device *wandev, char *name)
{
	struct net_device *dev = NULL, *prev = NULL;
	unsigned long smp_flags=0;

	lock_adapter_irq(&wandev->lock, &smp_flags);
	dev = wandev->dev;
	prev = NULL;
	while (dev && strcmp(name, dev->name)) {
		struct net_device **slave = netdev_priv(dev);
		prev = dev;
		dev = *slave;
	}
	unlock_adapter_irq(&wandev->lock, &smp_flags);

	if (dev == NULL)
		return -ENODEV;	/* interface not found */

	if (netif_running(dev))
		return -EBUSY;	/* interface in use */

	if (wandev->del_if)
		wandev->del_if(wandev, dev);

	lock_adapter_irq(&wandev->lock, &smp_flags);
	if (prev) {
		struct net_device **prev_slave = netdev_priv(prev);
		struct net_device **slave = netdev_priv(dev);

		*prev_slave = *slave;
	} else {
		struct net_device **slave = netdev_priv(dev);
		wandev->dev = *slave;
	}
	--wandev->ndev;
	unlock_adapter_irq(&wandev->lock, &smp_flags);

	printk(KERN_INFO "%s: unregistering '%s'\n", wandev->name, dev->name);

	unregister_netdev(dev);

	free_netdev(dev);

	return 0;
}

static void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
	__acquires(lock)
{
	spin_lock_irqsave(lock, *smp_flags);
}


static void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
	__releases(lock)
{
	spin_unlock_irqrestore(lock, *smp_flags);
}

EXPORT_SYMBOL(register_wan_device);
EXPORT_SYMBOL(unregister_wan_device);

MODULE_LICENSE("GPL");

/*
 *	End
 */