11802d0beSThomas 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"); 17*2ebb87f4SJeff Johnson MODULE_DESCRIPTION("Modular ISDN core driver"); 181b2b03f8SKarsten Keil MODULE_LICENSE("GPL"); 191b2b03f8SKarsten Keil module_param(debug, uint, S_IRUGO | S_IWUSR); 201b2b03f8SKarsten Keil 211b2b03f8SKarsten Keil static u64 device_ids; 221b2b03f8SKarsten Keil #define MAX_DEVICE_ID 63 231b2b03f8SKarsten Keil 241b2b03f8SKarsten Keil static LIST_HEAD(Bprotocols); 255b834354SHannes Eder static DEFINE_RWLOCK(bp_lock); 261b2b03f8SKarsten Keil 27b36b654aSMatthias Urlichs static void mISDN_dev_release(struct device *dev) 28b36b654aSMatthias Urlichs { 29b36b654aSMatthias Urlichs /* nothing to do: the device is part of its parent's data structure */ 30b36b654aSMatthias Urlichs } 31b36b654aSMatthias Urlichs 32d98259cbSGreg Kroah-Hartman static ssize_t id_show(struct device *dev, 33b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 34b36b654aSMatthias Urlichs { 35b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 36b36b654aSMatthias Urlichs 37b36b654aSMatthias Urlichs if (!mdev) 38b36b654aSMatthias Urlichs return -ENODEV; 39b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->id); 40b36b654aSMatthias Urlichs } 41d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(id); 42b36b654aSMatthias Urlichs 43d98259cbSGreg Kroah-Hartman static ssize_t nrbchan_show(struct device *dev, 44b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 45b36b654aSMatthias Urlichs { 46b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 47b36b654aSMatthias Urlichs 48b36b654aSMatthias Urlichs if (!mdev) 49b36b654aSMatthias Urlichs return -ENODEV; 50b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->nrbchan); 51b36b654aSMatthias Urlichs } 52d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(nrbchan); 53b36b654aSMatthias Urlichs 54d98259cbSGreg Kroah-Hartman static ssize_t d_protocols_show(struct device *dev, 55b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 56b36b654aSMatthias Urlichs { 57b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 58b36b654aSMatthias Urlichs 59b36b654aSMatthias Urlichs if (!mdev) 60b36b654aSMatthias Urlichs return -ENODEV; 61b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->Dprotocols); 62b36b654aSMatthias Urlichs } 63d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(d_protocols); 64b36b654aSMatthias Urlichs 65d98259cbSGreg Kroah-Hartman static ssize_t b_protocols_show(struct device *dev, 66b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 67b36b654aSMatthias Urlichs { 68b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 69b36b654aSMatthias Urlichs 70b36b654aSMatthias Urlichs if (!mdev) 71b36b654aSMatthias Urlichs return -ENODEV; 72b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols()); 73b36b654aSMatthias Urlichs } 74d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(b_protocols); 75b36b654aSMatthias Urlichs 76d98259cbSGreg Kroah-Hartman static ssize_t protocol_show(struct device *dev, 77b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 78b36b654aSMatthias Urlichs { 79b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 80b36b654aSMatthias Urlichs 81b36b654aSMatthias Urlichs if (!mdev) 82b36b654aSMatthias Urlichs return -ENODEV; 83b36b654aSMatthias Urlichs return sprintf(buf, "%d\n", mdev->D.protocol); 84b36b654aSMatthias Urlichs } 85d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(protocol); 86b36b654aSMatthias Urlichs 87d98259cbSGreg Kroah-Hartman static ssize_t name_show(struct device *dev, 88b36b654aSMatthias Urlichs struct device_attribute *attr, char *buf) 89b36b654aSMatthias Urlichs { 90b36b654aSMatthias Urlichs strcpy(buf, dev_name(dev)); 91b36b654aSMatthias Urlichs return strlen(buf); 92b36b654aSMatthias Urlichs } 93d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(name); 94b36b654aSMatthias Urlichs 95b36b654aSMatthias Urlichs #if 0 /* hangs */ 96d98259cbSGreg Kroah-Hartman static ssize_t name_set(struct device *dev, struct device_attribute *attr, 97b36b654aSMatthias Urlichs const char *buf, size_t count) 98b36b654aSMatthias Urlichs { 99b36b654aSMatthias Urlichs int err = 0; 100b36b654aSMatthias Urlichs char *out = kmalloc(count + 1, GFP_KERNEL); 101b36b654aSMatthias Urlichs 102b36b654aSMatthias Urlichs if (!out) 103b36b654aSMatthias Urlichs return -ENOMEM; 104b36b654aSMatthias Urlichs 105b36b654aSMatthias Urlichs memcpy(out, buf, count); 106b36b654aSMatthias Urlichs if (count && out[count - 1] == '\n') 107b36b654aSMatthias Urlichs out[--count] = 0; 108b36b654aSMatthias Urlichs if (count) 109b36b654aSMatthias Urlichs err = device_rename(dev, out); 110b36b654aSMatthias Urlichs kfree(out); 111b36b654aSMatthias Urlichs 112b36b654aSMatthias Urlichs return (err < 0) ? err : count; 113b36b654aSMatthias Urlichs } 114d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RW(name); 115b36b654aSMatthias Urlichs #endif 116b36b654aSMatthias Urlichs 117d98259cbSGreg Kroah-Hartman static ssize_t channelmap_show(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 } 129d98259cbSGreg Kroah-Hartman static DEVICE_ATTR_RO(channelmap); 130b36b654aSMatthias Urlichs 131d98259cbSGreg Kroah-Hartman static struct attribute *mISDN_attrs[] = { 132d98259cbSGreg Kroah-Hartman &dev_attr_id.attr, 133d98259cbSGreg Kroah-Hartman &dev_attr_d_protocols.attr, 134d98259cbSGreg Kroah-Hartman &dev_attr_b_protocols.attr, 135d98259cbSGreg Kroah-Hartman &dev_attr_protocol.attr, 136d98259cbSGreg Kroah-Hartman &dev_attr_channelmap.attr, 137d98259cbSGreg Kroah-Hartman &dev_attr_nrbchan.attr, 138d98259cbSGreg Kroah-Hartman &dev_attr_name.attr, 139d98259cbSGreg Kroah-Hartman NULL, 140b36b654aSMatthias Urlichs }; 141d98259cbSGreg Kroah-Hartman ATTRIBUTE_GROUPS(mISDN); 142b36b654aSMatthias Urlichs 14323680f0bSGreg Kroah-Hartman static int mISDN_uevent(const struct device *dev, struct kobj_uevent_env *env) 144b36b654aSMatthias Urlichs { 14523680f0bSGreg Kroah-Hartman const 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 156b36b654aSMatthias Urlichs static struct class mISDN_class = { 157b36b654aSMatthias Urlichs .name = "mISDN", 158b36b654aSMatthias Urlichs .dev_uevent = mISDN_uevent, 159d98259cbSGreg Kroah-Hartman .dev_groups = mISDN_groups, 160b36b654aSMatthias Urlichs .dev_release = mISDN_dev_release, 161b36b654aSMatthias Urlichs }; 162b36b654aSMatthias Urlichs 163b36b654aSMatthias Urlichs static int 1649f3b795aSMichał Mirosław _get_mdevice(struct device *dev, const void *id) 165b36b654aSMatthias Urlichs { 166b36b654aSMatthias Urlichs struct mISDNdevice *mdev = dev_to_mISDN(dev); 167b36b654aSMatthias Urlichs 168b36b654aSMatthias Urlichs if (!mdev) 169b36b654aSMatthias Urlichs return 0; 1709f3b795aSMichał Mirosław if (mdev->id != *(const u_int *)id) 171b36b654aSMatthias Urlichs return 0; 172b36b654aSMatthias Urlichs return 1; 173b36b654aSMatthias Urlichs } 174b36b654aSMatthias Urlichs 1751b2b03f8SKarsten Keil struct mISDNdevice 1761b2b03f8SKarsten Keil *get_mdevice(u_int id) 1771b2b03f8SKarsten Keil { 178b36b654aSMatthias Urlichs return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id, 179b36b654aSMatthias Urlichs _get_mdevice)); 1801b2b03f8SKarsten Keil } 181b36b654aSMatthias Urlichs 182b36b654aSMatthias Urlichs static int 183b36b654aSMatthias Urlichs _get_mdevice_count(struct device *dev, void *cnt) 184b36b654aSMatthias Urlichs { 185b36b654aSMatthias Urlichs *(int *)cnt += 1; 186b36b654aSMatthias Urlichs return 0; 1871b2b03f8SKarsten Keil } 1881b2b03f8SKarsten Keil 1891b2b03f8SKarsten Keil int 1901b2b03f8SKarsten Keil get_mdevice_count(void) 1911b2b03f8SKarsten Keil { 1921b2b03f8SKarsten Keil int cnt = 0; 1931b2b03f8SKarsten Keil 194b36b654aSMatthias Urlichs class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count); 1951b2b03f8SKarsten Keil return cnt; 1961b2b03f8SKarsten Keil } 1971b2b03f8SKarsten Keil 1981b2b03f8SKarsten Keil static int 1991b2b03f8SKarsten Keil get_free_devid(void) 2001b2b03f8SKarsten Keil { 2011b2b03f8SKarsten Keil u_int i; 2021b2b03f8SKarsten Keil 2031b2b03f8SKarsten Keil for (i = 0; i <= MAX_DEVICE_ID; i++) 2041b2b03f8SKarsten Keil if (!test_and_set_bit(i, (u_long *)&device_ids)) 205b36b654aSMatthias Urlichs break; 206b36b654aSMatthias Urlichs if (i > MAX_DEVICE_ID) 207ddacd14eSRoel Kluin return -EBUSY; 208b36b654aSMatthias Urlichs return i; 2091b2b03f8SKarsten Keil } 2101b2b03f8SKarsten Keil 2111b2b03f8SKarsten Keil int 212b36b654aSMatthias Urlichs mISDN_register_device(struct mISDNdevice *dev, 213b36b654aSMatthias Urlichs struct device *parent, char *name) 2141b2b03f8SKarsten Keil { 2151b2b03f8SKarsten Keil int err; 2161b2b03f8SKarsten Keil 217ddacd14eSRoel Kluin err = get_free_devid(); 218ddacd14eSRoel Kluin if (err < 0) 2192d25107eSWang ShaoBo return err; 220ddacd14eSRoel Kluin dev->id = err; 221b36b654aSMatthias Urlichs 222b36b654aSMatthias Urlichs device_initialize(&dev->dev); 2231b2b03f8SKarsten Keil if (name && name[0]) 224837468d1SMatthias Urlichs dev_set_name(&dev->dev, "%s", name); 2251b2b03f8SKarsten Keil else 226837468d1SMatthias Urlichs dev_set_name(&dev->dev, "mISDN%d", dev->id); 2271b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 2281b2b03f8SKarsten Keil printk(KERN_DEBUG "mISDN_register %s %d\n", 229837468d1SMatthias Urlichs dev_name(&dev->dev), dev->id); 230e7d1d4d9SYang Yingliang dev->dev.class = &mISDN_class; 231e7d1d4d9SYang Yingliang 2321b2b03f8SKarsten Keil err = create_stack(dev); 2331b2b03f8SKarsten Keil if (err) 234b36b654aSMatthias Urlichs goto error1; 235b36b654aSMatthias Urlichs 236b36b654aSMatthias Urlichs dev->dev.platform_data = dev; 237b36b654aSMatthias Urlichs dev->dev.parent = parent; 238b36b654aSMatthias Urlichs dev_set_drvdata(&dev->dev, dev); 239b36b654aSMatthias Urlichs 240b36b654aSMatthias Urlichs err = device_add(&dev->dev); 241b36b654aSMatthias Urlichs if (err) 242b36b654aSMatthias Urlichs goto error3; 2431b2b03f8SKarsten Keil return 0; 244b36b654aSMatthias Urlichs 245b36b654aSMatthias Urlichs error3: 246b36b654aSMatthias Urlichs delete_stack(dev); 247b36b654aSMatthias Urlichs error1: 248e7d1d4d9SYang Yingliang put_device(&dev->dev); 249b36b654aSMatthias Urlichs return err; 250b36b654aSMatthias Urlichs 2511b2b03f8SKarsten Keil } 2521b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_device); 2531b2b03f8SKarsten Keil 2541b2b03f8SKarsten Keil void 2551b2b03f8SKarsten Keil mISDN_unregister_device(struct mISDNdevice *dev) { 2561b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 2571b2b03f8SKarsten Keil printk(KERN_DEBUG "mISDN_unregister %s %d\n", 258837468d1SMatthias Urlichs dev_name(&dev->dev), dev->id); 259b36b654aSMatthias Urlichs /* sysfs_remove_link(&dev->dev.kobj, "device"); */ 260b36b654aSMatthias Urlichs device_del(&dev->dev); 261b36b654aSMatthias Urlichs dev_set_drvdata(&dev->dev, NULL); 262b36b654aSMatthias Urlichs 2631b2b03f8SKarsten Keil test_and_clear_bit(dev->id, (u_long *)&device_ids); 2641b2b03f8SKarsten Keil delete_stack(dev); 265b36b654aSMatthias Urlichs put_device(&dev->dev); 2661b2b03f8SKarsten Keil } 2671b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_device); 2681b2b03f8SKarsten Keil 2691b2b03f8SKarsten Keil u_int 2701b2b03f8SKarsten Keil get_all_Bprotocols(void) 2711b2b03f8SKarsten Keil { 2721b2b03f8SKarsten Keil struct Bprotocol *bp; 2731b2b03f8SKarsten Keil u_int m = 0; 2741b2b03f8SKarsten Keil 2751b2b03f8SKarsten Keil read_lock(&bp_lock); 2761b2b03f8SKarsten Keil list_for_each_entry(bp, &Bprotocols, list) 2771b2b03f8SKarsten Keil m |= bp->Bprotocols; 2781b2b03f8SKarsten Keil read_unlock(&bp_lock); 2791b2b03f8SKarsten Keil return m; 2801b2b03f8SKarsten Keil } 2811b2b03f8SKarsten Keil 2821b2b03f8SKarsten Keil struct Bprotocol * 2831b2b03f8SKarsten Keil get_Bprotocol4mask(u_int m) 2841b2b03f8SKarsten Keil { 2851b2b03f8SKarsten Keil struct Bprotocol *bp; 2861b2b03f8SKarsten Keil 2871b2b03f8SKarsten Keil read_lock(&bp_lock); 2881b2b03f8SKarsten Keil list_for_each_entry(bp, &Bprotocols, list) 2891b2b03f8SKarsten Keil if (bp->Bprotocols & m) { 2901b2b03f8SKarsten Keil read_unlock(&bp_lock); 2911b2b03f8SKarsten Keil return bp; 2921b2b03f8SKarsten Keil } 2931b2b03f8SKarsten Keil read_unlock(&bp_lock); 2941b2b03f8SKarsten Keil return NULL; 2951b2b03f8SKarsten Keil } 2961b2b03f8SKarsten Keil 2971b2b03f8SKarsten Keil struct Bprotocol * 2981b2b03f8SKarsten Keil get_Bprotocol4id(u_int id) 2991b2b03f8SKarsten Keil { 3001b2b03f8SKarsten Keil u_int m; 3011b2b03f8SKarsten Keil 3021b2b03f8SKarsten Keil if (id < ISDN_P_B_START || id > 63) { 3031b2b03f8SKarsten Keil printk(KERN_WARNING "%s id not in range %d\n", 3041b2b03f8SKarsten Keil __func__, id); 3051b2b03f8SKarsten Keil return NULL; 3061b2b03f8SKarsten Keil } 3071b2b03f8SKarsten Keil m = 1 << (id & ISDN_P_B_MASK); 3081b2b03f8SKarsten Keil return get_Bprotocol4mask(m); 3091b2b03f8SKarsten Keil } 3101b2b03f8SKarsten Keil 3111b2b03f8SKarsten Keil int 3121b2b03f8SKarsten Keil mISDN_register_Bprotocol(struct Bprotocol *bp) 3131b2b03f8SKarsten Keil { 3141b2b03f8SKarsten Keil u_long flags; 3151b2b03f8SKarsten Keil struct Bprotocol *old; 3161b2b03f8SKarsten Keil 3171b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 3181b2b03f8SKarsten Keil printk(KERN_DEBUG "%s: %s/%x\n", __func__, 3191b2b03f8SKarsten Keil bp->name, bp->Bprotocols); 3201b2b03f8SKarsten Keil old = get_Bprotocol4mask(bp->Bprotocols); 3211b2b03f8SKarsten Keil if (old) { 3221b2b03f8SKarsten Keil printk(KERN_WARNING 3231b2b03f8SKarsten Keil "register duplicate protocol old %s/%x new %s/%x\n", 3241b2b03f8SKarsten Keil old->name, old->Bprotocols, bp->name, bp->Bprotocols); 3251b2b03f8SKarsten Keil return -EBUSY; 3261b2b03f8SKarsten Keil } 3271b2b03f8SKarsten Keil write_lock_irqsave(&bp_lock, flags); 3281b2b03f8SKarsten Keil list_add_tail(&bp->list, &Bprotocols); 3291b2b03f8SKarsten Keil write_unlock_irqrestore(&bp_lock, flags); 3301b2b03f8SKarsten Keil return 0; 3311b2b03f8SKarsten Keil } 3321b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_register_Bprotocol); 3331b2b03f8SKarsten Keil 3341b2b03f8SKarsten Keil void 3351b2b03f8SKarsten Keil mISDN_unregister_Bprotocol(struct Bprotocol *bp) 3361b2b03f8SKarsten Keil { 3371b2b03f8SKarsten Keil u_long flags; 3381b2b03f8SKarsten Keil 3391b2b03f8SKarsten Keil if (debug & DEBUG_CORE) 3401b2b03f8SKarsten Keil printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name, 3411b2b03f8SKarsten Keil bp->Bprotocols); 3421b2b03f8SKarsten Keil write_lock_irqsave(&bp_lock, flags); 3431b2b03f8SKarsten Keil list_del(&bp->list); 3441b2b03f8SKarsten Keil write_unlock_irqrestore(&bp_lock, flags); 3451b2b03f8SKarsten Keil } 3461b2b03f8SKarsten Keil EXPORT_SYMBOL(mISDN_unregister_Bprotocol); 3471b2b03f8SKarsten Keil 348f45ebf3aSKarsten Keil static const char *msg_no_channel = "<no channel>"; 349f45ebf3aSKarsten Keil static const char *msg_no_stack = "<no stack>"; 350f45ebf3aSKarsten Keil static const char *msg_no_stackdev = "<no stack device>"; 351f45ebf3aSKarsten Keil 352f45ebf3aSKarsten Keil const char *mISDNDevName4ch(struct mISDNchannel *ch) 353f45ebf3aSKarsten Keil { 354f45ebf3aSKarsten Keil if (!ch) 355f45ebf3aSKarsten Keil return msg_no_channel; 356f45ebf3aSKarsten Keil if (!ch->st) 357f45ebf3aSKarsten Keil return msg_no_stack; 358f45ebf3aSKarsten Keil if (!ch->st->dev) 359f45ebf3aSKarsten Keil return msg_no_stackdev; 360f45ebf3aSKarsten Keil return dev_name(&ch->st->dev->dev); 361f45ebf3aSKarsten Keil }; 362f45ebf3aSKarsten Keil EXPORT_SYMBOL(mISDNDevName4ch); 363f45ebf3aSKarsten Keil 3645b834354SHannes Eder static int 3651b2b03f8SKarsten Keil mISDNInit(void) 3661b2b03f8SKarsten Keil { 3671b2b03f8SKarsten Keil int err; 3681b2b03f8SKarsten Keil 3691b2b03f8SKarsten Keil printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", 3701b2b03f8SKarsten Keil MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); 3713bd69ad1SAndreas Eversberg mISDN_init_clock(&debug); 3721b2b03f8SKarsten Keil mISDN_initstack(&debug); 373b36b654aSMatthias Urlichs err = class_register(&mISDN_class); 374b36b654aSMatthias Urlichs if (err) 375b36b654aSMatthias Urlichs goto error1; 3761b2b03f8SKarsten Keil err = mISDN_inittimer(&debug); 3771b2b03f8SKarsten Keil if (err) 378b36b654aSMatthias Urlichs goto error2; 3798b5fdfc5Swolfgang huang err = Isdnl1_Init(&debug); 380b36b654aSMatthias Urlichs if (err) 381b36b654aSMatthias Urlichs goto error3; 3821b2b03f8SKarsten Keil err = Isdnl2_Init(&debug); 383b36b654aSMatthias Urlichs if (err) 384b36b654aSMatthias Urlichs goto error4; 3851b2b03f8SKarsten Keil err = misdn_sock_init(&debug); 386b36b654aSMatthias Urlichs if (err) 387b36b654aSMatthias Urlichs goto error5; 388b36b654aSMatthias Urlichs return 0; 389b36b654aSMatthias Urlichs 390b36b654aSMatthias Urlichs error5: 3911b2b03f8SKarsten Keil Isdnl2_cleanup(); 392b36b654aSMatthias Urlichs error4: 3938b5fdfc5Swolfgang huang Isdnl1_cleanup(); 394b36b654aSMatthias Urlichs error3: 395b36b654aSMatthias Urlichs mISDN_timer_cleanup(); 396b36b654aSMatthias Urlichs error2: 397b36b654aSMatthias Urlichs class_unregister(&mISDN_class); 398b36b654aSMatthias Urlichs error1: 3991b2b03f8SKarsten Keil return err; 4001b2b03f8SKarsten Keil } 4011b2b03f8SKarsten Keil 4025b834354SHannes Eder static void mISDN_cleanup(void) 4031b2b03f8SKarsten Keil { 4041b2b03f8SKarsten Keil misdn_sock_cleanup(); 4051b2b03f8SKarsten Keil Isdnl2_cleanup(); 4068b5fdfc5Swolfgang huang Isdnl1_cleanup(); 407b36b654aSMatthias Urlichs mISDN_timer_cleanup(); 408b36b654aSMatthias Urlichs class_unregister(&mISDN_class); 4091b2b03f8SKarsten Keil 4101b2b03f8SKarsten Keil printk(KERN_DEBUG "mISDNcore unloaded\n"); 4111b2b03f8SKarsten Keil } 4121b2b03f8SKarsten Keil 4131b2b03f8SKarsten Keil module_init(mISDNInit); 4141b2b03f8SKarsten Keil module_exit(mISDN_cleanup); 415