1*1802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21b2b03f8SKarsten Keil /* 31b2b03f8SKarsten Keil * Copyright 2008 by Karsten Keil <kkeil@novell.com> 41b2b03f8SKarsten Keil */ 51b2b03f8SKarsten Keil 65a0e3ad6STejun Heo #include <linux/slab.h> 71b2b03f8SKarsten Keil #include <linux/types.h> 81b2b03f8SKarsten Keil #include <linux/stddef.h> 91b2b03f8SKarsten Keil #include <linux/module.h> 101b2b03f8SKarsten Keil #include <linux/spinlock.h> 111b2b03f8SKarsten Keil #include <linux/mISDNif.h> 121b2b03f8SKarsten Keil #include "core.h" 131b2b03f8SKarsten Keil 141b2b03f8SKarsten Keil static u_int debug; 151b2b03f8SKarsten Keil 161b2b03f8SKarsten Keil MODULE_AUTHOR("Karsten Keil"); 171b2b03f8SKarsten Keil MODULE_LICENSE("GPL"); 181b2b03f8SKarsten Keil module_param(debug, uint, S_IRUGO | S_IWUSR); 191b2b03f8SKarsten Keil 201b2b03f8SKarsten Keil static u64 device_ids; 211b2b03f8SKarsten Keil #define MAX_DEVICE_ID 63 221b2b03f8SKarsten Keil 231b2b03f8SKarsten Keil static LIST_HEAD(Bprotocols); 245b834354SHannes Eder static DEFINE_RWLOCK(bp_lock); 251b2b03f8SKarsten Keil 26b36b654aSMatthias Urlichs static void mISDN_dev_release(struct device *dev) 27b36b654aSMatthias Urlichs { 28b36b654aSMatthias Urlichs /* nothing to do: the device is part of its parent's data structure */ 29b36b654aSMatthias Urlichs } 30b36b654aSMatthias Urlichs 31d98259cbSGreg Kroah-Hartman static ssize_t id_show(struct device *dev, 32b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 33b36b654aSMatthias Urlichs { 34b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 35b36b654aSMatthias Urlichs 36b36b654aSMatthias Urlichs if (!mdev) 37b36b654aSMatthias Urlichs return -ENODEV; 38b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->id); 39b36b654aSMatthias Urlichs } 40d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(id); 41b36b654aSMatthias Urlichs 42d98259cbSGreg Kroah-Hartman static ssize_t nrbchan_show(struct device *dev, 43b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 44b36b654aSMatthias Urlichs { 45b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 46b36b654aSMatthias Urlichs 47b36b654aSMatthias Urlichs if (!mdev) 48b36b654aSMatthias Urlichs return -ENODEV; 49b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->nrbchan); 50b36b654aSMatthias Urlichs } 51d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(nrbchan); 52b36b654aSMatthias Urlichs 53d98259cbSGreg Kroah-Hartman static ssize_t d_protocols_show(struct device *dev, 54b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 55b36b654aSMatthias Urlichs { 56b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 57b36b654aSMatthias Urlichs 58b36b654aSMatthias Urlichs if (!mdev) 59b36b654aSMatthias Urlichs return -ENODEV; 60b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->Dprotocols); 61b36b654aSMatthias Urlichs } 62d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(d_protocols); 63b36b654aSMatthias Urlichs 64d98259cbSGreg Kroah-Hartman static ssize_t b_protocols_show(struct device *dev, 65b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 66b36b654aSMatthias Urlichs { 67b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 68b36b654aSMatthias Urlichs 69b36b654aSMatthias Urlichs if (!mdev) 70b36b654aSMatthias Urlichs return -ENODEV; 71b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols()); 72b36b654aSMatthias Urlichs } 73d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(b_protocols); 74b36b654aSMatthias Urlichs 75d98259cbSGreg Kroah-Hartman static ssize_t protocol_show(struct device *dev, 76b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 77b36b654aSMatthias Urlichs { 78b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 79b36b654aSMatthias Urlichs 80b36b654aSMatthias Urlichs if (!mdev) 81b36b654aSMatthias Urlichs return -ENODEV; 82b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->D.protocol); 83b36b654aSMatthias Urlichs } 84d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(protocol); 85b36b654aSMatthias Urlichs 86d98259cbSGreg Kroah-Hartman static ssize_t name_show(struct device *dev, 87b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 88b36b654aSMatthias Urlichs { 89b36b654aSMatthias Urlichs strcpy(buf, dev_name(dev)); 90b36b654aSMatthias Urlichs return strlen(buf); 91b36b654aSMatthias Urlichs } 92d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(name); 93b36b654aSMatthias Urlichs 94b36b654aSMatthias Urlichs #if 0 /* hangs */ 95d98259cbSGreg Kroah-Hartman static ssize_t name_set(struct device *dev, struct device_attribute *attr, 96b36b654aSMatthias Urlichs const char *buf, size_t count) 97b36b654aSMatthias Urlichs { 98b36b654aSMatthias Urlichs int err = 0; 99b36b654aSMatthias Urlichs char *out = kmalloc(count + 1, GFP_KERNEL); 100b36b654aSMatthias Urlichs 101b36b654aSMatthias Urlichs if (!out) 102b36b654aSMatthias Urlichs return -ENOMEM; 103b36b654aSMatthias Urlichs 104b36b654aSMatthias Urlichs memcpy(out, buf, count); 105b36b654aSMatthias Urlichs if (count && out[count - 1] == '\n') 106b36b654aSMatthias Urlichs out[--count] = 0; 107b36b654aSMatthias Urlichs if (count) 108b36b654aSMatthias Urlichs err = device_rename(dev, out); 109b36b654aSMatthias Urlichs kfree(out); 110b36b654aSMatthias Urlichs 111b36b654aSMatthias Urlichs return (err < 0) ? err : count; 112b36b654aSMatthias Urlichs } 113d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RW(name); 114b36b654aSMatthias Urlichs #endif 115b36b654aSMatthias Urlichs 116d98259cbSGreg Kroah-Hartman static ssize_t channelmap_show(struct device *dev, 117b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 118b36b654aSMatthias Urlichs { 119b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 120b36b654aSMatthias Urlichs char *bp = buf; 121b36b654aSMatthias Urlichs int i; 122b36b654aSMatthias Urlichs 123b36b654aSMatthias Urlichs for (i = 0; i <= mdev->nrbchan; i++) 124b36b654aSMatthias Urlichs *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0'; 125b36b654aSMatthias Urlichs 126b36b654aSMatthias Urlichs return bp - buf; 127b36b654aSMatthias Urlichs } 128d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(channelmap); 129b36b654aSMatthias Urlichs 130d98259cbSGreg Kroah-Hartman static struct attribute *mISDN_attrs[] = { 131d98259cbSGreg Kroah-Hartman &dev_attr_id.attr, 132d98259cbSGreg Kroah-Hartman &dev_attr_d_protocols.attr, 133d98259cbSGreg Kroah-Hartman &dev_attr_b_protocols.attr, 134d98259cbSGreg Kroah-Hartman &dev_attr_protocol.attr, 135d98259cbSGreg Kroah-Hartman &dev_attr_channelmap.attr, 136d98259cbSGreg Kroah-Hartman &dev_attr_nrbchan.attr, 137d98259cbSGreg Kroah-Hartman &dev_attr_name.attr, 138d98259cbSGreg Kroah-Hartman NULL, 139b36b654aSMatthias Urlichs }; 140d98259cbSGreg Kroah-Hartman ATTRIBUTE_GROUPS(mISDN); 141b36b654aSMatthias Urlichs 142b36b654aSMatthias Urlichs static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env) 143b36b654aSMatthias Urlichs { 144b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 145b36b654aSMatthias Urlichs 146b36b654aSMatthias Urlichs if (!mdev) 147b36b654aSMatthias Urlichs return 0; 148b36b654aSMatthias Urlichs 149b36b654aSMatthias Urlichs if (add_uevent_var(env, "nchans=%d", mdev->nrbchan)) 150b36b654aSMatthias Urlichs return -ENOMEM; 151b36b654aSMatthias Urlichs 152b36b654aSMatthias Urlichs return 0; 153b36b654aSMatthias Urlichs } 154b36b654aSMatthias Urlichs 155b36b654aSMatthias Urlichs static void mISDN_class_release(struct class *cls) 156b36b654aSMatthias Urlichs { 157b36b654aSMatthias Urlichs /* do nothing, it's static */ 158b36b654aSMatthias Urlichs } 159b36b654aSMatthias Urlichs 160b36b654aSMatthias Urlichs static struct class mISDN_class = { 161b36b654aSMatthias Urlichs .name = "mISDN", 162b36b654aSMatthias Urlichs .owner = THIS_MODULE, 163b36b654aSMatthias Urlichs .dev_uevent = mISDN_uevent, 164d98259cbSGreg Kroah-Hartman .dev_groups = mISDN_groups, 165b36b654aSMatthias Urlichs .dev_release = mISDN_dev_release, 166b36b654aSMatthias Urlichs .class_release = mISDN_class_release, 167b36b654aSMatthias Urlichs }; 168b36b654aSMatthias Urlichs 169b36b654aSMatthias Urlichs static int 1709f3b795aSMichał Mirosław _get_mdevice(struct device *dev, const void *id) 171b36b654aSMatthias Urlichs { 172b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 173b36b654aSMatthias Urlichs 174b36b654aSMatthias Urlichs if (!mdev) 175b36b654aSMatthias Urlichs return 0; 1769f3b795aSMichał Mirosław if (mdev->id != *(const u_int *)id) 177b36b654aSMatthias Urlichs return 0; 178b36b654aSMatthias Urlichs return 1; 179b36b654aSMatthias Urlichs } 180b36b654aSMatthias Urlichs 1811b2b03f8SKarsten Keil struct mISDNdevice 1821b2b03f8SKarsten Keil *get_mdevice(u_int id) 1831b2b03f8SKarsten Keil { 184b36b654aSMatthias Urlichs return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id, 185b36b654aSMatthias Urlichs _get_mdevice)); 1861b2b03f8SKarsten Keil } 187b36b654aSMatthias Urlichs 188b36b654aSMatthias Urlichs static int 189b36b654aSMatthias Urlichs _get_mdevice_count(struct device *dev, void *cnt) 190b36b654aSMatthias Urlichs { 191b36b654aSMatthias Urlichs *(int *)cnt += 1; 192b36b654aSMatthias Urlichs return 0; 1931b2b03f8SKarsten Keil } 1941b2b03f8SKarsten Keil 1951b2b03f8SKarsten Keil int 1961b2b03f8SKarsten Keil get_mdevice_count(void) 1971b2b03f8SKarsten Keil { 1981b2b03f8SKarsten Keil int cnt = 0; 1991b2b03f8SKarsten Keil 200b36b654aSMatthias Urlichs class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count); 2011b2b03f8SKarsten Keil return cnt; 2021b2b03f8SKarsten Keil } 2031b2b03f8SKarsten Keil 2041b2b03f8SKarsten Keil static int 2051b2b03f8SKarsten Keil get_free_devid(void) 2061b2b03f8SKarsten Keil { 2071b2b03f8SKarsten Keil u_int i; 2081b2b03f8SKarsten Keil 2091b2b03f8SKarsten Keil for (i = 0; i <= MAX_DEVICE_ID; i++) 2101b2b03f8SKarsten Keil if (!test_and_set_bit(i, (u_long *)&device_ids)) 211b36b654aSMatthias Urlichs break; 212b36b654aSMatthias Urlichs if (i > MAX_DEVICE_ID) 213ddacd14eSRoel Kluin return -EBUSY; 214b36b654aSMatthias Urlichs return i; 2151b2b03f8SKarsten Keil } 2161b2b03f8SKarsten Keil 2171b2b03f8SKarsten Keil int 218b36b654aSMatthias Urlichs mISDN_register_device(struct mISDNdevice *dev, 219b36b654aSMatthias Urlichs struct device *parent, char *name) 2201b2b03f8SKarsten Keil { 2211b2b03f8SKarsten Keil int err; 2221b2b03f8SKarsten Keil 223ddacd14eSRoel Kluin err = get_free_devid(); 224ddacd14eSRoel Kluin if (err < 0) 225b36b654aSMatthias Urlichs goto error1; 226ddacd14eSRoel Kluin dev->id = err; 227b36b654aSMatthias Urlichs 228b36b654aSMatthias Urlichs device_initialize(&dev->dev); 2291b2b03f8SKarsten Keil if (name && name[0]) 230837468d1SMatthias Urlichs dev_set_name(&dev->dev, "%s", name); 2311b2b03f8SKarsten Keil else 232837468d1SMatthias Urlichs dev_set_name(&dev->dev, "mISDN%d", dev->id); 2331b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 2341b2b03f8SKarsten Keil printk(KERN_DEBUG "mISDN_register %s %d\n", 235837468d1SMatthias Urlichs dev_name(&dev->dev), dev->id); 2361b2b03f8SKarsten Keil err = create_stack(dev); 2371b2b03f8SKarsten Keil if (err) 238b36b654aSMatthias Urlichs goto error1; 239b36b654aSMatthias Urlichs 240b36b654aSMatthias Urlichs dev->dev.class = &mISDN_class; 241b36b654aSMatthias Urlichs dev->dev.platform_data = dev; 242b36b654aSMatthias Urlichs dev->dev.parent = parent; 243b36b654aSMatthias Urlichs dev_set_drvdata(&dev->dev, dev); 244b36b654aSMatthias Urlichs 245b36b654aSMatthias Urlichs err = device_add(&dev->dev); 246b36b654aSMatthias Urlichs if (err) 247b36b654aSMatthias Urlichs goto error3; 2481b2b03f8SKarsten Keil return 0; 249b36b654aSMatthias Urlichs 250b36b654aSMatthias Urlichs error3: 251b36b654aSMatthias Urlichs delete_stack(dev); 252b36b654aSMatthias Urlichs return err; 253b36b654aSMatthias Urlichs error1: 254b36b654aSMatthias Urlichs return err; 255b36b654aSMatthias Urlichs 2561b2b03f8SKarsten Keil } 2571b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_device); 2581b2b03f8SKarsten Keil 2591b2b03f8SKarsten Keil void 2601b2b03f8SKarsten Keil mISDN_unregister_device(struct mISDNdevice *dev) { 2611b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 2621b2b03f8SKarsten Keil printk(KERN_DEBUG "mISDN_unregister %s %d\n", 263837468d1SMatthias Urlichs dev_name(&dev->dev), dev->id); 264b36b654aSMatthias Urlichs /* sysfs_remove_link(&dev->dev.kobj, "device"); */ 265b36b654aSMatthias Urlichs device_del(&dev->dev); 266b36b654aSMatthias Urlichs dev_set_drvdata(&dev->dev, NULL); 267b36b654aSMatthias Urlichs 2681b2b03f8SKarsten Keil test_and_clear_bit(dev->id, (u_long *)&device_ids); 2691b2b03f8SKarsten Keil delete_stack(dev); 270b36b654aSMatthias Urlichs put_device(&dev->dev); 2711b2b03f8SKarsten Keil } 2721b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_device); 2731b2b03f8SKarsten Keil 2741b2b03f8SKarsten Keil u_int 2751b2b03f8SKarsten Keil get_all_Bprotocols(void) 2761b2b03f8SKarsten Keil { 2771b2b03f8SKarsten Keil struct Bprotocol *bp; 2781b2b03f8SKarsten Keil u_int m = 0; 2791b2b03f8SKarsten Keil 2801b2b03f8SKarsten Keil read_lock(&bp_lock); 2811b2b03f8SKarsten Keil list_for_each_entry(bp, &Bprotocols, list) 2821b2b03f8SKarsten Keil m |= bp->Bprotocols; 2831b2b03f8SKarsten Keil read_unlock(&bp_lock); 2841b2b03f8SKarsten Keil return m; 2851b2b03f8SKarsten Keil } 2861b2b03f8SKarsten Keil 2871b2b03f8SKarsten Keil struct Bprotocol * 2881b2b03f8SKarsten Keil get_Bprotocol4mask(u_int m) 2891b2b03f8SKarsten Keil { 2901b2b03f8SKarsten Keil struct Bprotocol *bp; 2911b2b03f8SKarsten Keil 2921b2b03f8SKarsten Keil read_lock(&bp_lock); 2931b2b03f8SKarsten Keil list_for_each_entry(bp, &Bprotocols, list) 2941b2b03f8SKarsten Keil if (bp->Bprotocols & m) { 2951b2b03f8SKarsten Keil read_unlock(&bp_lock); 2961b2b03f8SKarsten Keil return bp; 2971b2b03f8SKarsten Keil } 2981b2b03f8SKarsten Keil read_unlock(&bp_lock); 2991b2b03f8SKarsten Keil return NULL; 3001b2b03f8SKarsten Keil } 3011b2b03f8SKarsten Keil 3021b2b03f8SKarsten Keil struct Bprotocol * 3031b2b03f8SKarsten Keil get_Bprotocol4id(u_int id) 3041b2b03f8SKarsten Keil { 3051b2b03f8SKarsten Keil u_int m; 3061b2b03f8SKarsten Keil 3071b2b03f8SKarsten Keil if (id < ISDN_P_B_START || id > 63) { 3081b2b03f8SKarsten Keil printk(KERN_WARNING "%s id not in range %d\n", 3091b2b03f8SKarsten Keil __func__, id); 3101b2b03f8SKarsten Keil return NULL; 3111b2b03f8SKarsten Keil } 3121b2b03f8SKarsten Keil m = 1 << (id & ISDN_P_B_MASK); 3131b2b03f8SKarsten Keil return get_Bprotocol4mask(m); 3141b2b03f8SKarsten Keil } 3151b2b03f8SKarsten Keil 3161b2b03f8SKarsten Keil int 3171b2b03f8SKarsten Keil mISDN_register_Bprotocol(struct Bprotocol *bp) 3181b2b03f8SKarsten Keil { 3191b2b03f8SKarsten Keil u_long flags; 3201b2b03f8SKarsten Keil struct Bprotocol *old; 3211b2b03f8SKarsten Keil 3221b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 3231b2b03f8SKarsten Keil printk(KERN_DEBUG "%s: %s/%x\n", __func__, 3241b2b03f8SKarsten Keil bp->name, bp->Bprotocols); 3251b2b03f8SKarsten Keil old = get_Bprotocol4mask(bp->Bprotocols); 3261b2b03f8SKarsten Keil if (old) { 3271b2b03f8SKarsten Keil printk(KERN_WARNING 3281b2b03f8SKarsten Keil "register duplicate protocol old %s/%x new %s/%x\n", 3291b2b03f8SKarsten Keil old->name, old->Bprotocols, bp->name, bp->Bprotocols); 3301b2b03f8SKarsten Keil return -EBUSY; 3311b2b03f8SKarsten Keil } 3321b2b03f8SKarsten Keil write_lock_irqsave(&bp_lock, flags); 3331b2b03f8SKarsten Keil list_add_tail(&bp->list, &Bprotocols); 3341b2b03f8SKarsten Keil write_unlock_irqrestore(&bp_lock, flags); 3351b2b03f8SKarsten Keil return 0; 3361b2b03f8SKarsten Keil } 3371b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_Bprotocol); 3381b2b03f8SKarsten Keil 3391b2b03f8SKarsten Keil void 3401b2b03f8SKarsten Keil mISDN_unregister_Bprotocol(struct Bprotocol *bp) 3411b2b03f8SKarsten Keil { 3421b2b03f8SKarsten Keil u_long flags; 3431b2b03f8SKarsten Keil 3441b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 3451b2b03f8SKarsten Keil printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name, 3461b2b03f8SKarsten Keil bp->Bprotocols); 3471b2b03f8SKarsten Keil write_lock_irqsave(&bp_lock, flags); 3481b2b03f8SKarsten Keil list_del(&bp->list); 3491b2b03f8SKarsten Keil write_unlock_irqrestore(&bp_lock, flags); 3501b2b03f8SKarsten Keil } 3511b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_Bprotocol); 3521b2b03f8SKarsten Keil 353f45ebf3aSKarsten Keil static const char *msg_no_channel = "<no channel>"; 354f45ebf3aSKarsten Keil static const char *msg_no_stack = "<no stack>"; 355f45ebf3aSKarsten Keil static const char *msg_no_stackdev = "<no stack device>"; 356f45ebf3aSKarsten Keil 357f45ebf3aSKarsten Keil const char *mISDNDevName4ch(struct mISDNchannel *ch) 358f45ebf3aSKarsten Keil { 359f45ebf3aSKarsten Keil if (!ch) 360f45ebf3aSKarsten Keil return msg_no_channel; 361f45ebf3aSKarsten Keil if (!ch->st) 362f45ebf3aSKarsten Keil return msg_no_stack; 363f45ebf3aSKarsten Keil if (!ch->st->dev) 364f45ebf3aSKarsten Keil return msg_no_stackdev; 365f45ebf3aSKarsten Keil return dev_name(&ch->st->dev->dev); 366f45ebf3aSKarsten Keil }; 367f45ebf3aSKarsten Keil EXPORT_SYMBOL(mISDNDevName4ch); 368f45ebf3aSKarsten Keil 3695b834354SHannes Eder static int 3701b2b03f8SKarsten Keil mISDNInit(void) 3711b2b03f8SKarsten Keil { 3721b2b03f8SKarsten Keil int err; 3731b2b03f8SKarsten Keil 3741b2b03f8SKarsten Keil printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", 3751b2b03f8SKarsten Keil MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); 3763bd69ad1SAndreas Eversberg mISDN_init_clock(&debug); 3771b2b03f8SKarsten Keil mISDN_initstack(&debug); 378b36b654aSMatthias Urlichs err = class_register(&mISDN_class); 379b36b654aSMatthias Urlichs if (err) 380b36b654aSMatthias Urlichs goto error1; 3811b2b03f8SKarsten Keil err = mISDN_inittimer(&debug); 3821b2b03f8SKarsten Keil if (err) 383b36b654aSMatthias Urlichs goto error2; 3841b2b03f8SKarsten Keil err = l1_init(&debug); 385b36b654aSMatthias Urlichs if (err) 386b36b654aSMatthias Urlichs goto error3; 3871b2b03f8SKarsten Keil err = Isdnl2_Init(&debug); 388b36b654aSMatthias Urlichs if (err) 389b36b654aSMatthias Urlichs goto error4; 3901b2b03f8SKarsten Keil err = misdn_sock_init(&debug); 391b36b654aSMatthias Urlichs if (err) 392b36b654aSMatthias Urlichs goto error5; 393b36b654aSMatthias Urlichs return 0; 394b36b654aSMatthias Urlichs 395b36b654aSMatthias Urlichs error5: 3961b2b03f8SKarsten Keil Isdnl2_cleanup(); 397b36b654aSMatthias Urlichs error4: 398b36b654aSMatthias Urlichs l1_cleanup(); 399b36b654aSMatthias Urlichs error3: 400b36b654aSMatthias Urlichs mISDN_timer_cleanup(); 401b36b654aSMatthias Urlichs error2: 402b36b654aSMatthias Urlichs class_unregister(&mISDN_class); 403b36b654aSMatthias Urlichs error1: 4041b2b03f8SKarsten Keil return err; 4051b2b03f8SKarsten Keil } 4061b2b03f8SKarsten Keil 4075b834354SHannes Eder static void mISDN_cleanup(void) 4081b2b03f8SKarsten Keil { 4091b2b03f8SKarsten Keil misdn_sock_cleanup(); 4101b2b03f8SKarsten Keil Isdnl2_cleanup(); 411b36b654aSMatthias Urlichs l1_cleanup(); 412b36b654aSMatthias Urlichs mISDN_timer_cleanup(); 413b36b654aSMatthias Urlichs class_unregister(&mISDN_class); 4141b2b03f8SKarsten Keil 4151b2b03f8SKarsten Keil printk(KERN_DEBUG "mISDNcore unloaded\n"); 4161b2b03f8SKarsten Keil } 4171b2b03f8SKarsten Keil 4181b2b03f8SKarsten Keil module_init(mISDNInit); 4191b2b03f8SKarsten Keil module_exit(mISDN_cleanup); 420