xref: /linux/drivers/isdn/mISDN/core.c (revision ddacd14e8db9b9c4434d00545f6483fe01f02991)
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 
151b2b03f8SKarsten Keil #include <linux/types.h>
161b2b03f8SKarsten Keil #include <linux/stddef.h>
171b2b03f8SKarsten Keil #include <linux/module.h>
181b2b03f8SKarsten Keil #include <linux/spinlock.h>
191b2b03f8SKarsten Keil #include <linux/mISDNif.h>
201b2b03f8SKarsten Keil #include "core.h"
211b2b03f8SKarsten Keil 
221b2b03f8SKarsten Keil static u_int debug;
231b2b03f8SKarsten Keil 
241b2b03f8SKarsten Keil MODULE_AUTHOR("Karsten Keil");
251b2b03f8SKarsten Keil MODULE_LICENSE("GPL");
261b2b03f8SKarsten Keil module_param(debug, uint, S_IRUGO | S_IWUSR);
271b2b03f8SKarsten Keil 
281b2b03f8SKarsten Keil static u64		device_ids;
291b2b03f8SKarsten Keil #define MAX_DEVICE_ID	63
301b2b03f8SKarsten Keil 
311b2b03f8SKarsten Keil static LIST_HEAD(Bprotocols);
325b834354SHannes Eder static DEFINE_RWLOCK(bp_lock);
331b2b03f8SKarsten Keil 
34b36b654aSMatthias Urlichs static void mISDN_dev_release(struct device *dev)
35b36b654aSMatthias Urlichs {
36b36b654aSMatthias Urlichs 	/* nothing to do: the device is part of its parent's data structure */
37b36b654aSMatthias Urlichs }
38b36b654aSMatthias Urlichs 
39b36b654aSMatthias Urlichs static ssize_t _show_id(struct device *dev,
40b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
41b36b654aSMatthias Urlichs {
42b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
43b36b654aSMatthias Urlichs 
44b36b654aSMatthias Urlichs 	if (!mdev)
45b36b654aSMatthias Urlichs 		return -ENODEV;
46b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->id);
47b36b654aSMatthias Urlichs }
48b36b654aSMatthias Urlichs 
49b36b654aSMatthias Urlichs static ssize_t _show_nrbchan(struct device *dev,
50b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
51b36b654aSMatthias Urlichs {
52b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
53b36b654aSMatthias Urlichs 
54b36b654aSMatthias Urlichs 	if (!mdev)
55b36b654aSMatthias Urlichs 		return -ENODEV;
56b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->nrbchan);
57b36b654aSMatthias Urlichs }
58b36b654aSMatthias Urlichs 
59b36b654aSMatthias Urlichs static ssize_t _show_d_protocols(struct device *dev,
60b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
61b36b654aSMatthias Urlichs {
62b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
63b36b654aSMatthias Urlichs 
64b36b654aSMatthias Urlichs 	if (!mdev)
65b36b654aSMatthias Urlichs 		return -ENODEV;
66b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->Dprotocols);
67b36b654aSMatthias Urlichs }
68b36b654aSMatthias Urlichs 
69b36b654aSMatthias Urlichs static ssize_t _show_b_protocols(struct device *dev,
70b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
71b36b654aSMatthias Urlichs {
72b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
73b36b654aSMatthias Urlichs 
74b36b654aSMatthias Urlichs 	if (!mdev)
75b36b654aSMatthias Urlichs 		return -ENODEV;
76b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
77b36b654aSMatthias Urlichs }
78b36b654aSMatthias Urlichs 
79b36b654aSMatthias Urlichs static ssize_t _show_protocol(struct device *dev,
80b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
81b36b654aSMatthias Urlichs {
82b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
83b36b654aSMatthias Urlichs 
84b36b654aSMatthias Urlichs 	if (!mdev)
85b36b654aSMatthias Urlichs 		return -ENODEV;
86b36b654aSMatthias Urlichs 	return sprintf(buf, "%d\n", mdev->D.protocol);
87b36b654aSMatthias Urlichs }
88b36b654aSMatthias Urlichs 
89b36b654aSMatthias Urlichs static ssize_t _show_name(struct device *dev,
90b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
91b36b654aSMatthias Urlichs {
92b36b654aSMatthias Urlichs 	strcpy(buf, dev_name(dev));
93b36b654aSMatthias Urlichs 	return strlen(buf);
94b36b654aSMatthias Urlichs }
95b36b654aSMatthias Urlichs 
96b36b654aSMatthias Urlichs #if 0 /* hangs */
97b36b654aSMatthias Urlichs static ssize_t _set_name(struct device *dev, struct device_attribute *attr,
98b36b654aSMatthias Urlichs 				const char *buf, size_t count)
99b36b654aSMatthias Urlichs {
100b36b654aSMatthias Urlichs 	int err = 0;
101b36b654aSMatthias Urlichs 	char *out = kmalloc(count + 1, GFP_KERNEL);
102b36b654aSMatthias Urlichs 
103b36b654aSMatthias Urlichs 	if (!out)
104b36b654aSMatthias Urlichs 		return -ENOMEM;
105b36b654aSMatthias Urlichs 
106b36b654aSMatthias Urlichs 	memcpy(out, buf, count);
107b36b654aSMatthias Urlichs 	if (count && out[count - 1] == '\n')
108b36b654aSMatthias Urlichs 		out[--count] = 0;
109b36b654aSMatthias Urlichs 	if (count)
110b36b654aSMatthias Urlichs 		err = device_rename(dev, out);
111b36b654aSMatthias Urlichs 	kfree(out);
112b36b654aSMatthias Urlichs 
113b36b654aSMatthias Urlichs 	return (err < 0) ? err : count;
114b36b654aSMatthias Urlichs }
115b36b654aSMatthias Urlichs #endif
116b36b654aSMatthias Urlichs 
117b36b654aSMatthias Urlichs static ssize_t _show_channelmap(struct device *dev,
118b36b654aSMatthias Urlichs 				struct device_attribute *attr, char *buf)
119b36b654aSMatthias Urlichs {
120b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
121b36b654aSMatthias Urlichs 	char *bp = buf;
122b36b654aSMatthias Urlichs 	int i;
123b36b654aSMatthias Urlichs 
124b36b654aSMatthias Urlichs 	for (i = 0; i <= mdev->nrbchan; i++)
125b36b654aSMatthias Urlichs 		*bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
126b36b654aSMatthias Urlichs 
127b36b654aSMatthias Urlichs 	return bp - buf;
128b36b654aSMatthias Urlichs }
129b36b654aSMatthias Urlichs 
130b36b654aSMatthias Urlichs static struct device_attribute mISDN_dev_attrs[] = {
131b36b654aSMatthias Urlichs 	__ATTR(id,          S_IRUGO,         _show_id,          NULL),
132b36b654aSMatthias Urlichs 	__ATTR(d_protocols, S_IRUGO,         _show_d_protocols, NULL),
133b36b654aSMatthias Urlichs 	__ATTR(b_protocols, S_IRUGO,         _show_b_protocols, NULL),
134b36b654aSMatthias Urlichs 	__ATTR(protocol,    S_IRUGO,         _show_protocol,    NULL),
135b36b654aSMatthias Urlichs 	__ATTR(channelmap,  S_IRUGO,         _show_channelmap,  NULL),
136b36b654aSMatthias Urlichs 	__ATTR(nrbchan,     S_IRUGO,         _show_nrbchan,     NULL),
137b36b654aSMatthias Urlichs 	__ATTR(name,        S_IRUGO,         _show_name,        NULL),
138b36b654aSMatthias Urlichs /*	__ATTR(name,        S_IRUGO|S_IWUSR, _show_name,       _set_name), */
139b36b654aSMatthias Urlichs 	{}
140b36b654aSMatthias Urlichs };
141b36b654aSMatthias Urlichs 
142b36b654aSMatthias Urlichs #ifdef CONFIG_HOTPLUG
143b36b654aSMatthias Urlichs static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
144b36b654aSMatthias Urlichs {
145b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
146b36b654aSMatthias Urlichs 
147b36b654aSMatthias Urlichs 	if (!mdev)
148b36b654aSMatthias Urlichs 		return 0;
149b36b654aSMatthias Urlichs 
150b36b654aSMatthias Urlichs 	if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
151b36b654aSMatthias Urlichs 		return -ENOMEM;
152b36b654aSMatthias Urlichs 
153b36b654aSMatthias Urlichs 	return 0;
154b36b654aSMatthias Urlichs }
155b36b654aSMatthias Urlichs #endif
156b36b654aSMatthias Urlichs 
157b36b654aSMatthias Urlichs static void mISDN_class_release(struct class *cls)
158b36b654aSMatthias Urlichs {
159b36b654aSMatthias Urlichs 	/* do nothing, it's static */
160b36b654aSMatthias Urlichs }
161b36b654aSMatthias Urlichs 
162b36b654aSMatthias Urlichs static struct class mISDN_class = {
163b36b654aSMatthias Urlichs 	.name = "mISDN",
164b36b654aSMatthias Urlichs 	.owner = THIS_MODULE,
165b36b654aSMatthias Urlichs #ifdef CONFIG_HOTPLUG
166b36b654aSMatthias Urlichs 	.dev_uevent = mISDN_uevent,
167b36b654aSMatthias Urlichs #endif
168b36b654aSMatthias Urlichs 	.dev_attrs = mISDN_dev_attrs,
169b36b654aSMatthias Urlichs 	.dev_release = mISDN_dev_release,
170b36b654aSMatthias Urlichs 	.class_release = mISDN_class_release,
171b36b654aSMatthias Urlichs };
172b36b654aSMatthias Urlichs 
173b36b654aSMatthias Urlichs static int
174b36b654aSMatthias Urlichs _get_mdevice(struct device *dev, void *id)
175b36b654aSMatthias Urlichs {
176b36b654aSMatthias Urlichs 	struct mISDNdevice *mdev = dev_to_mISDN(dev);
177b36b654aSMatthias Urlichs 
178b36b654aSMatthias Urlichs 	if (!mdev)
179b36b654aSMatthias Urlichs 		return 0;
180b36b654aSMatthias Urlichs 	if (mdev->id != *(u_int *)id)
181b36b654aSMatthias Urlichs 		return 0;
182b36b654aSMatthias Urlichs 	return 1;
183b36b654aSMatthias Urlichs }
184b36b654aSMatthias Urlichs 
1851b2b03f8SKarsten Keil struct mISDNdevice
1861b2b03f8SKarsten Keil *get_mdevice(u_int id)
1871b2b03f8SKarsten Keil {
188b36b654aSMatthias Urlichs 	return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
189b36b654aSMatthias Urlichs 		_get_mdevice));
1901b2b03f8SKarsten Keil }
191b36b654aSMatthias Urlichs 
192b36b654aSMatthias Urlichs static int
193b36b654aSMatthias Urlichs _get_mdevice_count(struct device *dev, void *cnt)
194b36b654aSMatthias Urlichs {
195b36b654aSMatthias Urlichs 	*(int *)cnt += 1;
196b36b654aSMatthias Urlichs 	return 0;
1971b2b03f8SKarsten Keil }
1981b2b03f8SKarsten Keil 
1991b2b03f8SKarsten Keil int
2001b2b03f8SKarsten Keil get_mdevice_count(void)
2011b2b03f8SKarsten Keil {
2021b2b03f8SKarsten Keil 	int cnt = 0;
2031b2b03f8SKarsten Keil 
204b36b654aSMatthias Urlichs 	class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
2051b2b03f8SKarsten Keil 	return cnt;
2061b2b03f8SKarsten Keil }
2071b2b03f8SKarsten Keil 
2081b2b03f8SKarsten Keil static int
2091b2b03f8SKarsten Keil get_free_devid(void)
2101b2b03f8SKarsten Keil {
2111b2b03f8SKarsten Keil 	u_int	i;
2121b2b03f8SKarsten Keil 
2131b2b03f8SKarsten Keil 	for (i = 0; i <= MAX_DEVICE_ID; i++)
2141b2b03f8SKarsten Keil 		if (!test_and_set_bit(i, (u_long *)&device_ids))
215b36b654aSMatthias Urlichs 			break;
216b36b654aSMatthias Urlichs 	if (i > MAX_DEVICE_ID)
217*ddacd14eSRoel Kluin 		return -EBUSY;
218b36b654aSMatthias Urlichs 	return i;
2191b2b03f8SKarsten Keil }
2201b2b03f8SKarsten Keil 
2211b2b03f8SKarsten Keil int
222b36b654aSMatthias Urlichs mISDN_register_device(struct mISDNdevice *dev,
223b36b654aSMatthias Urlichs 			struct device *parent, char *name)
2241b2b03f8SKarsten Keil {
2251b2b03f8SKarsten Keil 	int	err;
2261b2b03f8SKarsten Keil 
227*ddacd14eSRoel Kluin 	err = get_free_devid();
228*ddacd14eSRoel Kluin 	if (err < 0)
229b36b654aSMatthias Urlichs 		goto error1;
230*ddacd14eSRoel Kluin 	dev->id = err;
231b36b654aSMatthias Urlichs 
232b36b654aSMatthias Urlichs 	device_initialize(&dev->dev);
2331b2b03f8SKarsten Keil 	if (name && name[0])
234837468d1SMatthias Urlichs 		dev_set_name(&dev->dev, "%s", name);
2351b2b03f8SKarsten Keil 	else
236837468d1SMatthias Urlichs 		dev_set_name(&dev->dev, "mISDN%d", dev->id);
2371b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
2381b2b03f8SKarsten Keil 		printk(KERN_DEBUG "mISDN_register %s %d\n",
239837468d1SMatthias Urlichs 			dev_name(&dev->dev), dev->id);
2401b2b03f8SKarsten Keil 	err = create_stack(dev);
2411b2b03f8SKarsten Keil 	if (err)
242b36b654aSMatthias Urlichs 		goto error1;
243b36b654aSMatthias Urlichs 
244b36b654aSMatthias Urlichs 	dev->dev.class = &mISDN_class;
245b36b654aSMatthias Urlichs 	dev->dev.platform_data = dev;
246b36b654aSMatthias Urlichs 	dev->dev.parent = parent;
247b36b654aSMatthias Urlichs 	dev_set_drvdata(&dev->dev, dev);
248b36b654aSMatthias Urlichs 
249b36b654aSMatthias Urlichs 	err = device_add(&dev->dev);
250b36b654aSMatthias Urlichs 	if (err)
251b36b654aSMatthias Urlichs 		goto error3;
2521b2b03f8SKarsten Keil 	return 0;
253b36b654aSMatthias Urlichs 
254b36b654aSMatthias Urlichs error3:
255b36b654aSMatthias Urlichs 	delete_stack(dev);
256b36b654aSMatthias Urlichs 	return err;
257b36b654aSMatthias Urlichs error1:
258b36b654aSMatthias Urlichs 	return err;
259b36b654aSMatthias Urlichs 
2601b2b03f8SKarsten Keil }
2611b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_device);
2621b2b03f8SKarsten Keil 
2631b2b03f8SKarsten Keil void
2641b2b03f8SKarsten Keil mISDN_unregister_device(struct mISDNdevice *dev) {
2651b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
2661b2b03f8SKarsten Keil 		printk(KERN_DEBUG "mISDN_unregister %s %d\n",
267837468d1SMatthias Urlichs 			dev_name(&dev->dev), dev->id);
268b36b654aSMatthias Urlichs 	/* sysfs_remove_link(&dev->dev.kobj, "device"); */
269b36b654aSMatthias Urlichs 	device_del(&dev->dev);
270b36b654aSMatthias Urlichs 	dev_set_drvdata(&dev->dev, NULL);
271b36b654aSMatthias Urlichs 
2721b2b03f8SKarsten Keil 	test_and_clear_bit(dev->id, (u_long *)&device_ids);
2731b2b03f8SKarsten Keil 	delete_stack(dev);
274b36b654aSMatthias Urlichs 	put_device(&dev->dev);
2751b2b03f8SKarsten Keil }
2761b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_device);
2771b2b03f8SKarsten Keil 
2781b2b03f8SKarsten Keil u_int
2791b2b03f8SKarsten Keil get_all_Bprotocols(void)
2801b2b03f8SKarsten Keil {
2811b2b03f8SKarsten Keil 	struct Bprotocol	*bp;
2821b2b03f8SKarsten Keil 	u_int	m = 0;
2831b2b03f8SKarsten Keil 
2841b2b03f8SKarsten Keil 	read_lock(&bp_lock);
2851b2b03f8SKarsten Keil 	list_for_each_entry(bp, &Bprotocols, list)
2861b2b03f8SKarsten Keil 		m |= bp->Bprotocols;
2871b2b03f8SKarsten Keil 	read_unlock(&bp_lock);
2881b2b03f8SKarsten Keil 	return m;
2891b2b03f8SKarsten Keil }
2901b2b03f8SKarsten Keil 
2911b2b03f8SKarsten Keil struct Bprotocol *
2921b2b03f8SKarsten Keil get_Bprotocol4mask(u_int m)
2931b2b03f8SKarsten Keil {
2941b2b03f8SKarsten Keil 	struct Bprotocol	*bp;
2951b2b03f8SKarsten Keil 
2961b2b03f8SKarsten Keil 	read_lock(&bp_lock);
2971b2b03f8SKarsten Keil 	list_for_each_entry(bp, &Bprotocols, list)
2981b2b03f8SKarsten Keil 		if (bp->Bprotocols & m) {
2991b2b03f8SKarsten Keil 			read_unlock(&bp_lock);
3001b2b03f8SKarsten Keil 			return bp;
3011b2b03f8SKarsten Keil 		}
3021b2b03f8SKarsten Keil 	read_unlock(&bp_lock);
3031b2b03f8SKarsten Keil 	return NULL;
3041b2b03f8SKarsten Keil }
3051b2b03f8SKarsten Keil 
3061b2b03f8SKarsten Keil struct Bprotocol *
3071b2b03f8SKarsten Keil get_Bprotocol4id(u_int id)
3081b2b03f8SKarsten Keil {
3091b2b03f8SKarsten Keil 	u_int	m;
3101b2b03f8SKarsten Keil 
3111b2b03f8SKarsten Keil 	if (id < ISDN_P_B_START || id > 63) {
3121b2b03f8SKarsten Keil 		printk(KERN_WARNING "%s id not in range  %d\n",
3131b2b03f8SKarsten Keil 		    __func__, id);
3141b2b03f8SKarsten Keil 		return NULL;
3151b2b03f8SKarsten Keil 	}
3161b2b03f8SKarsten Keil 	m = 1 << (id & ISDN_P_B_MASK);
3171b2b03f8SKarsten Keil 	return get_Bprotocol4mask(m);
3181b2b03f8SKarsten Keil }
3191b2b03f8SKarsten Keil 
3201b2b03f8SKarsten Keil int
3211b2b03f8SKarsten Keil mISDN_register_Bprotocol(struct Bprotocol *bp)
3221b2b03f8SKarsten Keil {
3231b2b03f8SKarsten Keil 	u_long			flags;
3241b2b03f8SKarsten Keil 	struct Bprotocol	*old;
3251b2b03f8SKarsten Keil 
3261b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
3271b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s: %s/%x\n", __func__,
3281b2b03f8SKarsten Keil 		    bp->name, bp->Bprotocols);
3291b2b03f8SKarsten Keil 	old = get_Bprotocol4mask(bp->Bprotocols);
3301b2b03f8SKarsten Keil 	if (old) {
3311b2b03f8SKarsten Keil 		printk(KERN_WARNING
3321b2b03f8SKarsten Keil 		    "register duplicate protocol old %s/%x new %s/%x\n",
3331b2b03f8SKarsten Keil 		    old->name, old->Bprotocols, bp->name, bp->Bprotocols);
3341b2b03f8SKarsten Keil 		return -EBUSY;
3351b2b03f8SKarsten Keil 	}
3361b2b03f8SKarsten Keil 	write_lock_irqsave(&bp_lock, flags);
3371b2b03f8SKarsten Keil 	list_add_tail(&bp->list, &Bprotocols);
3381b2b03f8SKarsten Keil 	write_unlock_irqrestore(&bp_lock, flags);
3391b2b03f8SKarsten Keil 	return 0;
3401b2b03f8SKarsten Keil }
3411b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_Bprotocol);
3421b2b03f8SKarsten Keil 
3431b2b03f8SKarsten Keil void
3441b2b03f8SKarsten Keil mISDN_unregister_Bprotocol(struct Bprotocol *bp)
3451b2b03f8SKarsten Keil {
3461b2b03f8SKarsten Keil 	u_long	flags;
3471b2b03f8SKarsten Keil 
3481b2b03f8SKarsten Keil 	if (debug & DEBUG_CORE)
3491b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
3501b2b03f8SKarsten Keil 			bp->Bprotocols);
3511b2b03f8SKarsten Keil 	write_lock_irqsave(&bp_lock, flags);
3521b2b03f8SKarsten Keil 	list_del(&bp->list);
3531b2b03f8SKarsten Keil 	write_unlock_irqrestore(&bp_lock, flags);
3541b2b03f8SKarsten Keil }
3551b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
3561b2b03f8SKarsten Keil 
3575b834354SHannes Eder static int
3581b2b03f8SKarsten Keil mISDNInit(void)
3591b2b03f8SKarsten Keil {
3601b2b03f8SKarsten Keil 	int	err;
3611b2b03f8SKarsten Keil 
3621b2b03f8SKarsten Keil 	printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
3631b2b03f8SKarsten Keil 		MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
3643bd69ad1SAndreas Eversberg 	mISDN_init_clock(&debug);
3651b2b03f8SKarsten Keil 	mISDN_initstack(&debug);
366b36b654aSMatthias Urlichs 	err = class_register(&mISDN_class);
367b36b654aSMatthias Urlichs 	if (err)
368b36b654aSMatthias Urlichs 		goto error1;
3691b2b03f8SKarsten Keil 	err = mISDN_inittimer(&debug);
3701b2b03f8SKarsten Keil 	if (err)
371b36b654aSMatthias Urlichs 		goto error2;
3721b2b03f8SKarsten Keil 	err = l1_init(&debug);
373b36b654aSMatthias Urlichs 	if (err)
374b36b654aSMatthias Urlichs 		goto error3;
3751b2b03f8SKarsten Keil 	err = Isdnl2_Init(&debug);
376b36b654aSMatthias Urlichs 	if (err)
377b36b654aSMatthias Urlichs 		goto error4;
3781b2b03f8SKarsten Keil 	err = misdn_sock_init(&debug);
379b36b654aSMatthias Urlichs 	if (err)
380b36b654aSMatthias Urlichs 		goto error5;
381b36b654aSMatthias Urlichs 	return 0;
382b36b654aSMatthias Urlichs 
383b36b654aSMatthias Urlichs error5:
3841b2b03f8SKarsten Keil 	Isdnl2_cleanup();
385b36b654aSMatthias Urlichs error4:
386b36b654aSMatthias Urlichs 	l1_cleanup();
387b36b654aSMatthias Urlichs error3:
388b36b654aSMatthias Urlichs 	mISDN_timer_cleanup();
389b36b654aSMatthias Urlichs error2:
390b36b654aSMatthias Urlichs 	class_unregister(&mISDN_class);
391b36b654aSMatthias Urlichs error1:
3921b2b03f8SKarsten Keil 	return err;
3931b2b03f8SKarsten Keil }
3941b2b03f8SKarsten Keil 
3955b834354SHannes Eder static void mISDN_cleanup(void)
3961b2b03f8SKarsten Keil {
3971b2b03f8SKarsten Keil 	misdn_sock_cleanup();
3981b2b03f8SKarsten Keil 	Isdnl2_cleanup();
399b36b654aSMatthias Urlichs 	l1_cleanup();
400b36b654aSMatthias Urlichs 	mISDN_timer_cleanup();
401b36b654aSMatthias Urlichs 	class_unregister(&mISDN_class);
4021b2b03f8SKarsten Keil 
4031b2b03f8SKarsten Keil 	printk(KERN_DEBUG "mISDNcore unloaded\n");
4041b2b03f8SKarsten Keil }
4051b2b03f8SKarsten Keil 
4061b2b03f8SKarsten Keil module_init(mISDNInit);
4071b2b03f8SKarsten Keil module_exit(mISDN_cleanup);
4081b2b03f8SKarsten Keil 
409