xref: /linux/drivers/isdn/mISDN/core.c (revision 5a0e3ad6af8660be21ca98a971cd00f331318c05)
11b2b03f8SKarsten Keil /*
21b2b03f8SKarsten Keil  * Copyright 2008  by Karsten Keil <kkeil@novell.com>
31b2b03f8SKarsten Keil  *
41b2b03f8SKarsten Keil  * This program is free software; you can redistribute it and/or modify
51b2b03f8SKarsten Keil  * it under the terms of the GNU General Public License version 2 as
61b2b03f8SKarsten Keil  * published by the Free Software Foundation.
71b2b03f8SKarsten Keil  *
81b2b03f8SKarsten Keil  * This program is distributed in the hope that it will be useful,
91b2b03f8SKarsten Keil  * but WITHOUT ANY WARRANTY; without even the implied warranty of
101b2b03f8SKarsten Keil  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
111b2b03f8SKarsten Keil  * GNU General Public License for more details.
121b2b03f8SKarsten Keil  *
131b2b03f8SKarsten Keil  */
141b2b03f8SKarsten Keil 
15*5a0e3ad6STejun Heo #include <linux/slab.h>
161b2b03f8SKarsten Keil #include <linux/types.h>
171b2b03f8SKarsten Keil #include <linux/stddef.h>
181b2b03f8SKarsten Keil #include <linux/module.h>
191b2b03f8SKarsten Keil #include <linux/spinlock.h>
201b2b03f8SKarsten Keil #include <linux/mISDNif.h>
211b2b03f8SKarsten Keil #include "core.h"
221b2b03f8SKarsten Keil 
231b2b03f8SKarsten Keil static u_int debug;
241b2b03f8SKarsten Keil 
251b2b03f8SKarsten Keil MODULE_AUTHOR("Karsten Keil");
261b2b03f8SKarsten Keil MODULE_LICENSE("GPL");
271b2b03f8SKarsten Keil module_param(debug, uint, S_IRUGO | S_IWUSR);
281b2b03f8SKarsten Keil 
291b2b03f8SKarsten Keil static u64		device_ids;
301b2b03f8SKarsten Keil #define MAX_DEVICE_ID	63
311b2b03f8SKarsten Keil 
321b2b03f8SKarsten Keil static LIST_HEAD(Bprotocols);
335b834354SHannes Eder static DEFINE_RWLOCK(bp_lock);
341b2b03f8SKarsten Keil 
35b36b654aSMatthias Urlichs static void mISDN_dev_release(struct device *dev)
36b36b654aSMatthias Urlichs {
37b36b654aSMatthias Urlichs 	/* nothing to do: the device is part of its parent's data structure */
38b36b654aSMatthias Urlichs }
39b36b654aSMatthias Urlichs 
40b36b654aSMatthias Urlichs static ssize_t _show_id(struct device *dev,
41b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
42b36b654aSMatthias Urlichs {
43b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
44b36b654aSMatthias Urlichs 
45b36b654aSMatthias Urlichs 	if (!mdev)
46b36b654aSMatthias Urlichs 		return -ENODEV;
47b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->id);
48b36b654aSMatthias Urlichs }
49b36b654aSMatthias Urlichs 
50b36b654aSMatthias Urlichs static ssize_t _show_nrbchan(struct device *dev,
51b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
52b36b654aSMatthias Urlichs {
53b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
54b36b654aSMatthias Urlichs 
55b36b654aSMatthias Urlichs 	if (!mdev)
56b36b654aSMatthias Urlichs 		return -ENODEV;
57b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->nrbchan);
58b36b654aSMatthias Urlichs }
59b36b654aSMatthias Urlichs 
60b36b654aSMatthias Urlichs static ssize_t _show_d_protocols(struct device *dev,
61b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
62b36b654aSMatthias Urlichs {
63b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
64b36b654aSMatthias Urlichs 
65b36b654aSMatthias Urlichs 	if (!mdev)
66b36b654aSMatthias Urlichs 		return -ENODEV;
67b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->Dprotocols);
68b36b654aSMatthias Urlichs }
69b36b654aSMatthias Urlichs 
70b36b654aSMatthias Urlichs static ssize_t _show_b_protocols(struct device *dev,
71b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
72b36b654aSMatthias Urlichs {
73b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
74b36b654aSMatthias Urlichs 
75b36b654aSMatthias Urlichs 	if (!mdev)
76b36b654aSMatthias Urlichs 		return -ENODEV;
77b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
78b36b654aSMatthias Urlichs }
79b36b654aSMatthias Urlichs 
80b36b654aSMatthias Urlichs static ssize_t _show_protocol(struct device *dev,
81b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
82b36b654aSMatthias Urlichs {
83b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
84b36b654aSMatthias Urlichs 
85b36b654aSMatthias Urlichs 	if (!mdev)
86b36b654aSMatthias Urlichs 		return -ENODEV;
87b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->D.protocol);
88b36b654aSMatthias Urlichs }
89b36b654aSMatthias Urlichs 
90b36b654aSMatthias Urlichs static ssize_t _show_name(struct device *dev,
91b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
92b36b654aSMatthias Urlichs {
93b36b654aSMatthias Urlichs 	strcpy(buf, dev_name(dev));
94b36b654aSMatthias Urlichs 	return strlen(buf);
95b36b654aSMatthias Urlichs }
96b36b654aSMatthias Urlichs 
97b36b654aSMatthias Urlichs #if 0 /* hangs */
98b36b654aSMatthias Urlichs static ssize_t _set_name(struct device *dev, struct device_attribute *attr,
99b36b654aSMatthias Urlichs 				const char *buf, size_t count)
100b36b654aSMatthias Urlichs {
101b36b654aSMatthias Urlichs 	int err = 0;
102b36b654aSMatthias Urlichs 	char *out = kmalloc(count + 1, GFP_KERNEL);
103b36b654aSMatthias Urlichs 
104b36b654aSMatthias Urlichs 	if (!out)
105b36b654aSMatthias Urlichs 		return -ENOMEM;
106b36b654aSMatthias Urlichs 
107b36b654aSMatthias Urlichs 	memcpy(out, buf, count);
108b36b654aSMatthias Urlichs 	if (count && out[count - 1] == '\n')
109b36b654aSMatthias Urlichs 		out[--count] = 0;
110b36b654aSMatthias Urlichs 	if (count)
111b36b654aSMatthias Urlichs 		err = device_rename(dev, out);
112b36b654aSMatthias Urlichs 	kfree(out);
113b36b654aSMatthias Urlichs 
114b36b654aSMatthias Urlichs 	return (err < 0) ? err : count;
115b36b654aSMatthias Urlichs }
116b36b654aSMatthias Urlichs #endif
117b36b654aSMatthias Urlichs 
118b36b654aSMatthias Urlichs static ssize_t _show_channelmap(struct device *dev,
119b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
120b36b654aSMatthias Urlichs {
121b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
122b36b654aSMatthias Urlichs 	char *bp = buf;
123b36b654aSMatthias Urlichs 	int i;
124b36b654aSMatthias Urlichs 
125b36b654aSMatthias Urlichs 	for (i = 0; i <= mdev->nrbchan; i++)
126b36b654aSMatthias Urlichs 		*bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
127b36b654aSMatthias Urlichs 
128b36b654aSMatthias Urlichs 	return bp - buf;
129b36b654aSMatthias Urlichs }
130b36b654aSMatthias Urlichs 
131b36b654aSMatthias Urlichs static struct device_attribute mISDN_dev_attrs[] = {
132b36b654aSMatthias Urlichs 	__ATTR(id,          S_IRUGO,         _show_id,          NULL),
133b36b654aSMatthias Urlichs 	__ATTR(d_protocols, S_IRUGO,         _show_d_protocols, NULL),
134b36b654aSMatthias Urlichs 	__ATTR(b_protocols, S_IRUGO,         _show_b_protocols, NULL),
135b36b654aSMatthias Urlichs 	__ATTR(protocol,    S_IRUGO,         _show_protocol,    NULL),
136b36b654aSMatthias Urlichs 	__ATTR(channelmap,  S_IRUGO,         _show_channelmap,  NULL),
137b36b654aSMatthias Urlichs 	__ATTR(nrbchan,     S_IRUGO,         _show_nrbchan,     NULL),
138b36b654aSMatthias Urlichs 	__ATTR(name,        S_IRUGO,         _show_name,        NULL),
139b36b654aSMatthias Urlichs /*	__ATTR(name,        S_IRUGO|S_IWUSR, _show_name,       _set_name), */
140b36b654aSMatthias Urlichs 	{}
141b36b654aSMatthias Urlichs };
142b36b654aSMatthias Urlichs 
143b36b654aSMatthias Urlichs #ifdef CONFIG_HOTPLUG
144b36b654aSMatthias Urlichs static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
145b36b654aSMatthias Urlichs {
146b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
147b36b654aSMatthias Urlichs 
148b36b654aSMatthias Urlichs 	if (!mdev)
149b36b654aSMatthias Urlichs 		return 0;
150b36b654aSMatthias Urlichs 
151b36b654aSMatthias Urlichs 	if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
152b36b654aSMatthias Urlichs 		return -ENOMEM;
153b36b654aSMatthias Urlichs 
154b36b654aSMatthias Urlichs 	return 0;
155b36b654aSMatthias Urlichs }
156b36b654aSMatthias Urlichs #endif
157b36b654aSMatthias Urlichs 
158b36b654aSMatthias Urlichs static void mISDN_class_release(struct class *cls)
159b36b654aSMatthias Urlichs {
160b36b654aSMatthias Urlichs 	/* do nothing, it's static */
161b36b654aSMatthias Urlichs }
162b36b654aSMatthias Urlichs 
163b36b654aSMatthias Urlichs static struct class mISDN_class = {
164b36b654aSMatthias Urlichs 	.name = "mISDN",
165b36b654aSMatthias Urlichs 	.owner = THIS_MODULE,
166b36b654aSMatthias Urlichs #ifdef CONFIG_HOTPLUG
167b36b654aSMatthias Urlichs 	.dev_uevent = mISDN_uevent,
168b36b654aSMatthias Urlichs #endif
169b36b654aSMatthias Urlichs 	.dev_attrs = mISDN_dev_attrs,
170b36b654aSMatthias Urlichs 	.dev_release = mISDN_dev_release,
171b36b654aSMatthias Urlichs 	.class_release = mISDN_class_release,
172b36b654aSMatthias Urlichs };
173b36b654aSMatthias Urlichs 
174b36b654aSMatthias Urlichs static int
175b36b654aSMatthias Urlichs _get_mdevice(struct device *dev, void *id)
176b36b654aSMatthias Urlichs {
177b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
178b36b654aSMatthias Urlichs 
179b36b654aSMatthias Urlichs 	if (!mdev)
180b36b654aSMatthias Urlichs 		return 0;
181b36b654aSMatthias Urlichs 	if (mdev->id != *(u_int *)id)
182b36b654aSMatthias Urlichs 		return 0;
183b36b654aSMatthias Urlichs 	return 1;
184b36b654aSMatthias Urlichs }
185b36b654aSMatthias Urlichs 
1861b2b03f8SKarsten Keil struct mISDNdevice
1871b2b03f8SKarsten Keil *get_mdevice(u_int id)
1881b2b03f8SKarsten Keil {
189b36b654aSMatthias Urlichs 	return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
190b36b654aSMatthias Urlichs 		_get_mdevice));
1911b2b03f8SKarsten Keil }
192b36b654aSMatthias Urlichs 
193b36b654aSMatthias Urlichs static int
194b36b654aSMatthias Urlichs _get_mdevice_count(struct device *dev, void *cnt)
195b36b654aSMatthias Urlichs {
196b36b654aSMatthias Urlichs 	*(int *)cnt += 1;
197b36b654aSMatthias Urlichs 	return 0;
1981b2b03f8SKarsten Keil }
1991b2b03f8SKarsten Keil 
2001b2b03f8SKarsten Keil int
2011b2b03f8SKarsten Keil get_mdevice_count(void)
2021b2b03f8SKarsten Keil {
2031b2b03f8SKarsten Keil 	int cnt = 0;
2041b2b03f8SKarsten Keil 
205b36b654aSMatthias Urlichs 	class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
2061b2b03f8SKarsten Keil 	return cnt;
2071b2b03f8SKarsten Keil }
2081b2b03f8SKarsten Keil 
2091b2b03f8SKarsten Keil static int
2101b2b03f8SKarsten Keil get_free_devid(void)
2111b2b03f8SKarsten Keil {
2121b2b03f8SKarsten Keil 	u_int	i;
2131b2b03f8SKarsten Keil 
2141b2b03f8SKarsten Keil 	for (i = 0; i <= MAX_DEVICE_ID; i++)
2151b2b03f8SKarsten Keil 		if (!test_and_set_bit(i, (u_long *)&device_ids))
216b36b654aSMatthias Urlichs 			break;
217b36b654aSMatthias Urlichs 	if (i > MAX_DEVICE_ID)
218ddacd14eSRoel Kluin 		return -EBUSY;
219b36b654aSMatthias Urlichs 	return i;
2201b2b03f8SKarsten Keil }
2211b2b03f8SKarsten Keil 
2221b2b03f8SKarsten Keil int
223b36b654aSMatthias Urlichs mISDN_register_device(struct mISDNdevice *dev,
224b36b654aSMatthias Urlichs 			struct device *parent, char *name)
2251b2b03f8SKarsten Keil {
2261b2b03f8SKarsten Keil 	int	err;
2271b2b03f8SKarsten Keil 
228ddacd14eSRoel Kluin 	err = get_free_devid();
229ddacd14eSRoel Kluin 	if (err < 0)
230b36b654aSMatthias Urlichs 		goto error1;
231ddacd14eSRoel Kluin 	dev->id = err;
232b36b654aSMatthias Urlichs 
233b36b654aSMatthias Urlichs 	device_initialize(&dev->dev);
2341b2b03f8SKarsten Keil 	if (name && name[0])
235837468d1SMatthias Urlichs 		dev_set_name(&dev->dev, "%s", name);
2361b2b03f8SKarsten Keil 	else
237837468d1SMatthias Urlichs 		dev_set_name(&dev->dev, "mISDN%d", dev->id);
2381b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
2391b2b03f8SKarsten Keil 		printk(KERN_DEBUG "mISDN_register %s %d\n",
240837468d1SMatthias Urlichs 			dev_name(&dev->dev), dev->id);
2411b2b03f8SKarsten Keil 	err = create_stack(dev);
2421b2b03f8SKarsten Keil 	if (err)
243b36b654aSMatthias Urlichs 		goto error1;
244b36b654aSMatthias Urlichs 
245b36b654aSMatthias Urlichs 	dev->dev.class = &mISDN_class;
246b36b654aSMatthias Urlichs 	dev->dev.platform_data = dev;
247b36b654aSMatthias Urlichs 	dev->dev.parent = parent;
248b36b654aSMatthias Urlichs 	dev_set_drvdata(&dev->dev, dev);
249b36b654aSMatthias Urlichs 
250b36b654aSMatthias Urlichs 	err = device_add(&dev->dev);
251b36b654aSMatthias Urlichs 	if (err)
252b36b654aSMatthias Urlichs 		goto error3;
2531b2b03f8SKarsten Keil 	return 0;
254b36b654aSMatthias Urlichs 
255b36b654aSMatthias Urlichs error3:
256b36b654aSMatthias Urlichs 	delete_stack(dev);
257b36b654aSMatthias Urlichs 	return err;
258b36b654aSMatthias Urlichs error1:
259b36b654aSMatthias Urlichs 	return err;
260b36b654aSMatthias Urlichs 
2611b2b03f8SKarsten Keil }
2621b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_device);
2631b2b03f8SKarsten Keil 
2641b2b03f8SKarsten Keil void
2651b2b03f8SKarsten Keil mISDN_unregister_device(struct mISDNdevice *dev) {
2661b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
2671b2b03f8SKarsten Keil 		printk(KERN_DEBUG "mISDN_unregister %s %d\n",
268837468d1SMatthias Urlichs 			dev_name(&dev->dev), dev->id);
269b36b654aSMatthias Urlichs 	/* sysfs_remove_link(&dev->dev.kobj, "device"); */
270b36b654aSMatthias Urlichs 	device_del(&dev->dev);
271b36b654aSMatthias Urlichs 	dev_set_drvdata(&dev->dev, NULL);
272b36b654aSMatthias Urlichs 
2731b2b03f8SKarsten Keil 	test_and_clear_bit(dev->id, (u_long *)&device_ids);
2741b2b03f8SKarsten Keil 	delete_stack(dev);
275b36b654aSMatthias Urlichs 	put_device(&dev->dev);
2761b2b03f8SKarsten Keil }
2771b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_device);
2781b2b03f8SKarsten Keil 
2791b2b03f8SKarsten Keil u_int
2801b2b03f8SKarsten Keil get_all_Bprotocols(void)
2811b2b03f8SKarsten Keil {
2821b2b03f8SKarsten Keil 	struct Bprotocol	*bp;
2831b2b03f8SKarsten Keil 	u_int	m = 0;
2841b2b03f8SKarsten Keil 
2851b2b03f8SKarsten Keil 	read_lock(&bp_lock);
2861b2b03f8SKarsten Keil 	list_for_each_entry(bp, &Bprotocols, list)
2871b2b03f8SKarsten Keil 		m |= bp->Bprotocols;
2881b2b03f8SKarsten Keil 	read_unlock(&bp_lock);
2891b2b03f8SKarsten Keil 	return m;
2901b2b03f8SKarsten Keil }
2911b2b03f8SKarsten Keil 
2921b2b03f8SKarsten Keil struct Bprotocol *
2931b2b03f8SKarsten Keil get_Bprotocol4mask(u_int m)
2941b2b03f8SKarsten Keil {
2951b2b03f8SKarsten Keil 	struct Bprotocol	*bp;
2961b2b03f8SKarsten Keil 
2971b2b03f8SKarsten Keil 	read_lock(&bp_lock);
2981b2b03f8SKarsten Keil 	list_for_each_entry(bp, &Bprotocols, list)
2991b2b03f8SKarsten Keil 		if (bp->Bprotocols & m) {
3001b2b03f8SKarsten Keil 			read_unlock(&bp_lock);
3011b2b03f8SKarsten Keil 			return bp;
3021b2b03f8SKarsten Keil 		}
3031b2b03f8SKarsten Keil 	read_unlock(&bp_lock);
3041b2b03f8SKarsten Keil 	return NULL;
3051b2b03f8SKarsten Keil }
3061b2b03f8SKarsten Keil 
3071b2b03f8SKarsten Keil struct Bprotocol *
3081b2b03f8SKarsten Keil get_Bprotocol4id(u_int id)
3091b2b03f8SKarsten Keil {
3101b2b03f8SKarsten Keil 	u_int	m;
3111b2b03f8SKarsten Keil 
3121b2b03f8SKarsten Keil 	if (id < ISDN_P_B_START || id > 63) {
3131b2b03f8SKarsten Keil 		printk(KERN_WARNING "%s id not in range  %d\n",
3141b2b03f8SKarsten Keil 		    __func__, id);
3151b2b03f8SKarsten Keil 		return NULL;
3161b2b03f8SKarsten Keil 	}
3171b2b03f8SKarsten Keil 	m = 1 << (id & ISDN_P_B_MASK);
3181b2b03f8SKarsten Keil 	return get_Bprotocol4mask(m);
3191b2b03f8SKarsten Keil }
3201b2b03f8SKarsten Keil 
3211b2b03f8SKarsten Keil int
3221b2b03f8SKarsten Keil mISDN_register_Bprotocol(struct Bprotocol *bp)
3231b2b03f8SKarsten Keil {
3241b2b03f8SKarsten Keil 	u_long			flags;
3251b2b03f8SKarsten Keil 	struct Bprotocol	*old;
3261b2b03f8SKarsten Keil 
3271b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
3281b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s: %s/%x\n", __func__,
3291b2b03f8SKarsten Keil 		    bp->name, bp->Bprotocols);
3301b2b03f8SKarsten Keil 	old = get_Bprotocol4mask(bp->Bprotocols);
3311b2b03f8SKarsten Keil 	if (old) {
3321b2b03f8SKarsten Keil 		printk(KERN_WARNING
3331b2b03f8SKarsten Keil 		    "register duplicate protocol old %s/%x new %s/%x\n",
3341b2b03f8SKarsten Keil 		    old->name, old->Bprotocols, bp->name, bp->Bprotocols);
3351b2b03f8SKarsten Keil 		return -EBUSY;
3361b2b03f8SKarsten Keil 	}
3371b2b03f8SKarsten Keil 	write_lock_irqsave(&bp_lock, flags);
3381b2b03f8SKarsten Keil 	list_add_tail(&bp->list, &Bprotocols);
3391b2b03f8SKarsten Keil 	write_unlock_irqrestore(&bp_lock, flags);
3401b2b03f8SKarsten Keil 	return 0;
3411b2b03f8SKarsten Keil }
3421b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_Bprotocol);
3431b2b03f8SKarsten Keil 
3441b2b03f8SKarsten Keil void
3451b2b03f8SKarsten Keil mISDN_unregister_Bprotocol(struct Bprotocol *bp)
3461b2b03f8SKarsten Keil {
3471b2b03f8SKarsten Keil 	u_long	flags;
3481b2b03f8SKarsten Keil 
3491b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
3501b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
3511b2b03f8SKarsten Keil 			bp->Bprotocols);
3521b2b03f8SKarsten Keil 	write_lock_irqsave(&bp_lock, flags);
3531b2b03f8SKarsten Keil 	list_del(&bp->list);
3541b2b03f8SKarsten Keil 	write_unlock_irqrestore(&bp_lock, flags);
3551b2b03f8SKarsten Keil }
3561b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
3571b2b03f8SKarsten Keil 
3585b834354SHannes Eder static int
3591b2b03f8SKarsten Keil mISDNInit(void)
3601b2b03f8SKarsten Keil {
3611b2b03f8SKarsten Keil 	int	err;
3621b2b03f8SKarsten Keil 
3631b2b03f8SKarsten Keil 	printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
3641b2b03f8SKarsten Keil 		MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
3653bd69ad1SAndreas Eversberg 	mISDN_init_clock(&debug);
3661b2b03f8SKarsten Keil 	mISDN_initstack(&debug);
367b36b654aSMatthias Urlichs 	err = class_register(&mISDN_class);
368b36b654aSMatthias Urlichs 	if (err)
369b36b654aSMatthias Urlichs 		goto error1;
3701b2b03f8SKarsten Keil 	err = mISDN_inittimer(&debug);
3711b2b03f8SKarsten Keil 	if (err)
372b36b654aSMatthias Urlichs 		goto error2;
3731b2b03f8SKarsten Keil 	err = l1_init(&debug);
374b36b654aSMatthias Urlichs 	if (err)
375b36b654aSMatthias Urlichs 		goto error3;
3761b2b03f8SKarsten Keil 	err = Isdnl2_Init(&debug);
377b36b654aSMatthias Urlichs 	if (err)
378b36b654aSMatthias Urlichs 		goto error4;
3791b2b03f8SKarsten Keil 	err = misdn_sock_init(&debug);
380b36b654aSMatthias Urlichs 	if (err)
381b36b654aSMatthias Urlichs 		goto error5;
382b36b654aSMatthias Urlichs 	return 0;
383b36b654aSMatthias Urlichs 
384b36b654aSMatthias Urlichs error5:
3851b2b03f8SKarsten Keil 	Isdnl2_cleanup();
386b36b654aSMatthias Urlichs error4:
387b36b654aSMatthias Urlichs 	l1_cleanup();
388b36b654aSMatthias Urlichs error3:
389b36b654aSMatthias Urlichs 	mISDN_timer_cleanup();
390b36b654aSMatthias Urlichs error2:
391b36b654aSMatthias Urlichs 	class_unregister(&mISDN_class);
392b36b654aSMatthias Urlichs error1:
3931b2b03f8SKarsten Keil 	return err;
3941b2b03f8SKarsten Keil }
3951b2b03f8SKarsten Keil 
3965b834354SHannes Eder static void mISDN_cleanup(void)
3971b2b03f8SKarsten Keil {
3981b2b03f8SKarsten Keil 	misdn_sock_cleanup();
3991b2b03f8SKarsten Keil 	Isdnl2_cleanup();
400b36b654aSMatthias Urlichs 	l1_cleanup();
401b36b654aSMatthias Urlichs 	mISDN_timer_cleanup();
402b36b654aSMatthias Urlichs 	class_unregister(&mISDN_class);
4031b2b03f8SKarsten Keil 
4041b2b03f8SKarsten Keil 	printk(KERN_DEBUG "mISDNcore unloaded\n");
4051b2b03f8SKarsten Keil }
4061b2b03f8SKarsten Keil 
4071b2b03f8SKarsten Keil module_init(mISDNInit);
4081b2b03f8SKarsten Keil module_exit(mISDN_cleanup);
4091b2b03f8SKarsten Keil 
410