1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2008 by Karsten Keil <kkeil@novell.com>
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/slab.h>
7*4882a593Smuzhiyun #include <linux/types.h>
8*4882a593Smuzhiyun #include <linux/stddef.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/spinlock.h>
11*4882a593Smuzhiyun #include <linux/mISDNif.h>
12*4882a593Smuzhiyun #include "core.h"
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun static u_int debug;
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun MODULE_AUTHOR("Karsten Keil");
17*4882a593Smuzhiyun MODULE_LICENSE("GPL");
18*4882a593Smuzhiyun module_param(debug, uint, S_IRUGO | S_IWUSR);
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun static u64 device_ids;
21*4882a593Smuzhiyun #define MAX_DEVICE_ID 63
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun static LIST_HEAD(Bprotocols);
24*4882a593Smuzhiyun static DEFINE_RWLOCK(bp_lock);
25*4882a593Smuzhiyun
mISDN_dev_release(struct device * dev)26*4882a593Smuzhiyun static void mISDN_dev_release(struct device *dev)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun /* nothing to do: the device is part of its parent's data structure */
29*4882a593Smuzhiyun }
30*4882a593Smuzhiyun
id_show(struct device * dev,struct device_attribute * attr,char * buf)31*4882a593Smuzhiyun static ssize_t id_show(struct device *dev,
32*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun if (!mdev)
37*4882a593Smuzhiyun return -ENODEV;
38*4882a593Smuzhiyun return sprintf(buf, "%d\n", mdev->id);
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun static DEVICE_ATTR_RO(id);
41*4882a593Smuzhiyun
nrbchan_show(struct device * dev,struct device_attribute * attr,char * buf)42*4882a593Smuzhiyun static ssize_t nrbchan_show(struct device *dev,
43*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun if (!mdev)
48*4882a593Smuzhiyun return -ENODEV;
49*4882a593Smuzhiyun return sprintf(buf, "%d\n", mdev->nrbchan);
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun static DEVICE_ATTR_RO(nrbchan);
52*4882a593Smuzhiyun
d_protocols_show(struct device * dev,struct device_attribute * attr,char * buf)53*4882a593Smuzhiyun static ssize_t d_protocols_show(struct device *dev,
54*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun if (!mdev)
59*4882a593Smuzhiyun return -ENODEV;
60*4882a593Smuzhiyun return sprintf(buf, "%d\n", mdev->Dprotocols);
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun static DEVICE_ATTR_RO(d_protocols);
63*4882a593Smuzhiyun
b_protocols_show(struct device * dev,struct device_attribute * attr,char * buf)64*4882a593Smuzhiyun static ssize_t b_protocols_show(struct device *dev,
65*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun if (!mdev)
70*4882a593Smuzhiyun return -ENODEV;
71*4882a593Smuzhiyun return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun static DEVICE_ATTR_RO(b_protocols);
74*4882a593Smuzhiyun
protocol_show(struct device * dev,struct device_attribute * attr,char * buf)75*4882a593Smuzhiyun static ssize_t protocol_show(struct device *dev,
76*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun if (!mdev)
81*4882a593Smuzhiyun return -ENODEV;
82*4882a593Smuzhiyun return sprintf(buf, "%d\n", mdev->D.protocol);
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun static DEVICE_ATTR_RO(protocol);
85*4882a593Smuzhiyun
name_show(struct device * dev,struct device_attribute * attr,char * buf)86*4882a593Smuzhiyun static ssize_t name_show(struct device *dev,
87*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun strcpy(buf, dev_name(dev));
90*4882a593Smuzhiyun return strlen(buf);
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun static DEVICE_ATTR_RO(name);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun #if 0 /* hangs */
95*4882a593Smuzhiyun static ssize_t name_set(struct device *dev, struct device_attribute *attr,
96*4882a593Smuzhiyun const char *buf, size_t count)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun int err = 0;
99*4882a593Smuzhiyun char *out = kmalloc(count + 1, GFP_KERNEL);
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun if (!out)
102*4882a593Smuzhiyun return -ENOMEM;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun memcpy(out, buf, count);
105*4882a593Smuzhiyun if (count && out[count - 1] == '\n')
106*4882a593Smuzhiyun out[--count] = 0;
107*4882a593Smuzhiyun if (count)
108*4882a593Smuzhiyun err = device_rename(dev, out);
109*4882a593Smuzhiyun kfree(out);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun return (err < 0) ? err : count;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun static DEVICE_ATTR_RW(name);
114*4882a593Smuzhiyun #endif
115*4882a593Smuzhiyun
channelmap_show(struct device * dev,struct device_attribute * attr,char * buf)116*4882a593Smuzhiyun static ssize_t channelmap_show(struct device *dev,
117*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
120*4882a593Smuzhiyun char *bp = buf;
121*4882a593Smuzhiyun int i;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun for (i = 0; i <= mdev->nrbchan; i++)
124*4882a593Smuzhiyun *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun return bp - buf;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun static DEVICE_ATTR_RO(channelmap);
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun static struct attribute *mISDN_attrs[] = {
131*4882a593Smuzhiyun &dev_attr_id.attr,
132*4882a593Smuzhiyun &dev_attr_d_protocols.attr,
133*4882a593Smuzhiyun &dev_attr_b_protocols.attr,
134*4882a593Smuzhiyun &dev_attr_protocol.attr,
135*4882a593Smuzhiyun &dev_attr_channelmap.attr,
136*4882a593Smuzhiyun &dev_attr_nrbchan.attr,
137*4882a593Smuzhiyun &dev_attr_name.attr,
138*4882a593Smuzhiyun NULL,
139*4882a593Smuzhiyun };
140*4882a593Smuzhiyun ATTRIBUTE_GROUPS(mISDN);
141*4882a593Smuzhiyun
mISDN_uevent(struct device * dev,struct kobj_uevent_env * env)142*4882a593Smuzhiyun static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun if (!mdev)
147*4882a593Smuzhiyun return 0;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
150*4882a593Smuzhiyun return -ENOMEM;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun return 0;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
mISDN_class_release(struct class * cls)155*4882a593Smuzhiyun static void mISDN_class_release(struct class *cls)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun /* do nothing, it's static */
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun static struct class mISDN_class = {
161*4882a593Smuzhiyun .name = "mISDN",
162*4882a593Smuzhiyun .owner = THIS_MODULE,
163*4882a593Smuzhiyun .dev_uevent = mISDN_uevent,
164*4882a593Smuzhiyun .dev_groups = mISDN_groups,
165*4882a593Smuzhiyun .dev_release = mISDN_dev_release,
166*4882a593Smuzhiyun .class_release = mISDN_class_release,
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun static int
_get_mdevice(struct device * dev,const void * id)170*4882a593Smuzhiyun _get_mdevice(struct device *dev, const void *id)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun struct mISDNdevice *mdev = dev_to_mISDN(dev);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun if (!mdev)
175*4882a593Smuzhiyun return 0;
176*4882a593Smuzhiyun if (mdev->id != *(const u_int *)id)
177*4882a593Smuzhiyun return 0;
178*4882a593Smuzhiyun return 1;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun struct mISDNdevice
get_mdevice(u_int id)182*4882a593Smuzhiyun *get_mdevice(u_int id)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
185*4882a593Smuzhiyun _get_mdevice));
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun static int
_get_mdevice_count(struct device * dev,void * cnt)189*4882a593Smuzhiyun _get_mdevice_count(struct device *dev, void *cnt)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun *(int *)cnt += 1;
192*4882a593Smuzhiyun return 0;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun int
get_mdevice_count(void)196*4882a593Smuzhiyun get_mdevice_count(void)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun int cnt = 0;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
201*4882a593Smuzhiyun return cnt;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun static int
get_free_devid(void)205*4882a593Smuzhiyun get_free_devid(void)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun u_int i;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun for (i = 0; i <= MAX_DEVICE_ID; i++)
210*4882a593Smuzhiyun if (!test_and_set_bit(i, (u_long *)&device_ids))
211*4882a593Smuzhiyun break;
212*4882a593Smuzhiyun if (i > MAX_DEVICE_ID)
213*4882a593Smuzhiyun return -EBUSY;
214*4882a593Smuzhiyun return i;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun int
mISDN_register_device(struct mISDNdevice * dev,struct device * parent,char * name)218*4882a593Smuzhiyun mISDN_register_device(struct mISDNdevice *dev,
219*4882a593Smuzhiyun struct device *parent, char *name)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun int err;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun err = get_free_devid();
224*4882a593Smuzhiyun if (err < 0)
225*4882a593Smuzhiyun return err;
226*4882a593Smuzhiyun dev->id = err;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun device_initialize(&dev->dev);
229*4882a593Smuzhiyun if (name && name[0])
230*4882a593Smuzhiyun dev_set_name(&dev->dev, "%s", name);
231*4882a593Smuzhiyun else
232*4882a593Smuzhiyun dev_set_name(&dev->dev, "mISDN%d", dev->id);
233*4882a593Smuzhiyun if (debug & DEBUG_CORE)
234*4882a593Smuzhiyun printk(KERN_DEBUG "mISDN_register %s %d\n",
235*4882a593Smuzhiyun dev_name(&dev->dev), dev->id);
236*4882a593Smuzhiyun dev->dev.class = &mISDN_class;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun err = create_stack(dev);
239*4882a593Smuzhiyun if (err)
240*4882a593Smuzhiyun goto error1;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun dev->dev.platform_data = dev;
243*4882a593Smuzhiyun dev->dev.parent = parent;
244*4882a593Smuzhiyun dev_set_drvdata(&dev->dev, dev);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun err = device_add(&dev->dev);
247*4882a593Smuzhiyun if (err)
248*4882a593Smuzhiyun goto error3;
249*4882a593Smuzhiyun return 0;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun error3:
252*4882a593Smuzhiyun delete_stack(dev);
253*4882a593Smuzhiyun error1:
254*4882a593Smuzhiyun put_device(&dev->dev);
255*4882a593Smuzhiyun return err;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_register_device);
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun void
mISDN_unregister_device(struct mISDNdevice * dev)261*4882a593Smuzhiyun mISDN_unregister_device(struct mISDNdevice *dev) {
262*4882a593Smuzhiyun if (debug & DEBUG_CORE)
263*4882a593Smuzhiyun printk(KERN_DEBUG "mISDN_unregister %s %d\n",
264*4882a593Smuzhiyun dev_name(&dev->dev), dev->id);
265*4882a593Smuzhiyun /* sysfs_remove_link(&dev->dev.kobj, "device"); */
266*4882a593Smuzhiyun device_del(&dev->dev);
267*4882a593Smuzhiyun dev_set_drvdata(&dev->dev, NULL);
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun test_and_clear_bit(dev->id, (u_long *)&device_ids);
270*4882a593Smuzhiyun delete_stack(dev);
271*4882a593Smuzhiyun put_device(&dev->dev);
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_unregister_device);
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun u_int
get_all_Bprotocols(void)276*4882a593Smuzhiyun get_all_Bprotocols(void)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun struct Bprotocol *bp;
279*4882a593Smuzhiyun u_int m = 0;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun read_lock(&bp_lock);
282*4882a593Smuzhiyun list_for_each_entry(bp, &Bprotocols, list)
283*4882a593Smuzhiyun m |= bp->Bprotocols;
284*4882a593Smuzhiyun read_unlock(&bp_lock);
285*4882a593Smuzhiyun return m;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun struct Bprotocol *
get_Bprotocol4mask(u_int m)289*4882a593Smuzhiyun get_Bprotocol4mask(u_int m)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun struct Bprotocol *bp;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun read_lock(&bp_lock);
294*4882a593Smuzhiyun list_for_each_entry(bp, &Bprotocols, list)
295*4882a593Smuzhiyun if (bp->Bprotocols & m) {
296*4882a593Smuzhiyun read_unlock(&bp_lock);
297*4882a593Smuzhiyun return bp;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun read_unlock(&bp_lock);
300*4882a593Smuzhiyun return NULL;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun struct Bprotocol *
get_Bprotocol4id(u_int id)304*4882a593Smuzhiyun get_Bprotocol4id(u_int id)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun u_int m;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun if (id < ISDN_P_B_START || id > 63) {
309*4882a593Smuzhiyun printk(KERN_WARNING "%s id not in range %d\n",
310*4882a593Smuzhiyun __func__, id);
311*4882a593Smuzhiyun return NULL;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun m = 1 << (id & ISDN_P_B_MASK);
314*4882a593Smuzhiyun return get_Bprotocol4mask(m);
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun int
mISDN_register_Bprotocol(struct Bprotocol * bp)318*4882a593Smuzhiyun mISDN_register_Bprotocol(struct Bprotocol *bp)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun u_long flags;
321*4882a593Smuzhiyun struct Bprotocol *old;
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun if (debug & DEBUG_CORE)
324*4882a593Smuzhiyun printk(KERN_DEBUG "%s: %s/%x\n", __func__,
325*4882a593Smuzhiyun bp->name, bp->Bprotocols);
326*4882a593Smuzhiyun old = get_Bprotocol4mask(bp->Bprotocols);
327*4882a593Smuzhiyun if (old) {
328*4882a593Smuzhiyun printk(KERN_WARNING
329*4882a593Smuzhiyun "register duplicate protocol old %s/%x new %s/%x\n",
330*4882a593Smuzhiyun old->name, old->Bprotocols, bp->name, bp->Bprotocols);
331*4882a593Smuzhiyun return -EBUSY;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun write_lock_irqsave(&bp_lock, flags);
334*4882a593Smuzhiyun list_add_tail(&bp->list, &Bprotocols);
335*4882a593Smuzhiyun write_unlock_irqrestore(&bp_lock, flags);
336*4882a593Smuzhiyun return 0;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_register_Bprotocol);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun void
mISDN_unregister_Bprotocol(struct Bprotocol * bp)341*4882a593Smuzhiyun mISDN_unregister_Bprotocol(struct Bprotocol *bp)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun u_long flags;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun if (debug & DEBUG_CORE)
346*4882a593Smuzhiyun printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
347*4882a593Smuzhiyun bp->Bprotocols);
348*4882a593Smuzhiyun write_lock_irqsave(&bp_lock, flags);
349*4882a593Smuzhiyun list_del(&bp->list);
350*4882a593Smuzhiyun write_unlock_irqrestore(&bp_lock, flags);
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun static const char *msg_no_channel = "<no channel>";
355*4882a593Smuzhiyun static const char *msg_no_stack = "<no stack>";
356*4882a593Smuzhiyun static const char *msg_no_stackdev = "<no stack device>";
357*4882a593Smuzhiyun
mISDNDevName4ch(struct mISDNchannel * ch)358*4882a593Smuzhiyun const char *mISDNDevName4ch(struct mISDNchannel *ch)
359*4882a593Smuzhiyun {
360*4882a593Smuzhiyun if (!ch)
361*4882a593Smuzhiyun return msg_no_channel;
362*4882a593Smuzhiyun if (!ch->st)
363*4882a593Smuzhiyun return msg_no_stack;
364*4882a593Smuzhiyun if (!ch->st->dev)
365*4882a593Smuzhiyun return msg_no_stackdev;
366*4882a593Smuzhiyun return dev_name(&ch->st->dev->dev);
367*4882a593Smuzhiyun };
368*4882a593Smuzhiyun EXPORT_SYMBOL(mISDNDevName4ch);
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun static int
mISDNInit(void)371*4882a593Smuzhiyun mISDNInit(void)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun int err;
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
376*4882a593Smuzhiyun MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
377*4882a593Smuzhiyun mISDN_init_clock(&debug);
378*4882a593Smuzhiyun mISDN_initstack(&debug);
379*4882a593Smuzhiyun err = class_register(&mISDN_class);
380*4882a593Smuzhiyun if (err)
381*4882a593Smuzhiyun goto error1;
382*4882a593Smuzhiyun err = mISDN_inittimer(&debug);
383*4882a593Smuzhiyun if (err)
384*4882a593Smuzhiyun goto error2;
385*4882a593Smuzhiyun err = Isdnl1_Init(&debug);
386*4882a593Smuzhiyun if (err)
387*4882a593Smuzhiyun goto error3;
388*4882a593Smuzhiyun err = Isdnl2_Init(&debug);
389*4882a593Smuzhiyun if (err)
390*4882a593Smuzhiyun goto error4;
391*4882a593Smuzhiyun err = misdn_sock_init(&debug);
392*4882a593Smuzhiyun if (err)
393*4882a593Smuzhiyun goto error5;
394*4882a593Smuzhiyun return 0;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun error5:
397*4882a593Smuzhiyun Isdnl2_cleanup();
398*4882a593Smuzhiyun error4:
399*4882a593Smuzhiyun Isdnl1_cleanup();
400*4882a593Smuzhiyun error3:
401*4882a593Smuzhiyun mISDN_timer_cleanup();
402*4882a593Smuzhiyun error2:
403*4882a593Smuzhiyun class_unregister(&mISDN_class);
404*4882a593Smuzhiyun error1:
405*4882a593Smuzhiyun return err;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun
mISDN_cleanup(void)408*4882a593Smuzhiyun static void mISDN_cleanup(void)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun misdn_sock_cleanup();
411*4882a593Smuzhiyun Isdnl2_cleanup();
412*4882a593Smuzhiyun Isdnl1_cleanup();
413*4882a593Smuzhiyun mISDN_timer_cleanup();
414*4882a593Smuzhiyun class_unregister(&mISDN_class);
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun printk(KERN_DEBUG "mISDNcore unloaded\n");
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun module_init(mISDNInit);
420*4882a593Smuzhiyun module_exit(mISDN_cleanup);
421