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