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