18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2008  by Karsten Keil <kkeil@novell.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/stddef.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
118c2ecf20Sopenharmony_ci#include <linux/mISDNif.h>
128c2ecf20Sopenharmony_ci#include "core.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic u_int debug;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Karsten Keil");
178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
188c2ecf20Sopenharmony_cimodule_param(debug, uint, S_IRUGO | S_IWUSR);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic u64		device_ids;
218c2ecf20Sopenharmony_ci#define MAX_DEVICE_ID	63
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic LIST_HEAD(Bprotocols);
248c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(bp_lock);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void mISDN_dev_release(struct device *dev)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	/* nothing to do: the device is part of its parent's data structure */
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic ssize_t id_show(struct device *dev,
328c2ecf20Sopenharmony_ci		       struct device_attribute *attr, char *buf)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (!mdev)
378c2ecf20Sopenharmony_ci		return -ENODEV;
388c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", mdev->id);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic ssize_t nrbchan_show(struct device *dev,
438c2ecf20Sopenharmony_ci			    struct device_attribute *attr, char *buf)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (!mdev)
488c2ecf20Sopenharmony_ci		return -ENODEV;
498c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", mdev->nrbchan);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(nrbchan);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic ssize_t d_protocols_show(struct device *dev,
548c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (!mdev)
598c2ecf20Sopenharmony_ci		return -ENODEV;
608c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", mdev->Dprotocols);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(d_protocols);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic ssize_t b_protocols_show(struct device *dev,
658c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!mdev)
708c2ecf20Sopenharmony_ci		return -ENODEV;
718c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(b_protocols);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic ssize_t protocol_show(struct device *dev,
768c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buf)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!mdev)
818c2ecf20Sopenharmony_ci		return -ENODEV;
828c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", mdev->D.protocol);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(protocol);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev,
878c2ecf20Sopenharmony_ci			 struct device_attribute *attr, char *buf)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	strcpy(buf, dev_name(dev));
908c2ecf20Sopenharmony_ci	return strlen(buf);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#if 0 /* hangs */
958c2ecf20Sopenharmony_cistatic ssize_t name_set(struct device *dev, struct device_attribute *attr,
968c2ecf20Sopenharmony_ci			const char *buf, size_t count)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	int err = 0;
998c2ecf20Sopenharmony_ci	char *out = kmalloc(count + 1, GFP_KERNEL);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (!out)
1028c2ecf20Sopenharmony_ci		return -ENOMEM;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	memcpy(out, buf, count);
1058c2ecf20Sopenharmony_ci	if (count && out[count - 1] == '\n')
1068c2ecf20Sopenharmony_ci		out[--count] = 0;
1078c2ecf20Sopenharmony_ci	if (count)
1088c2ecf20Sopenharmony_ci		err = device_rename(dev, out);
1098c2ecf20Sopenharmony_ci	kfree(out);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return (err < 0) ? err : count;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(name);
1148c2ecf20Sopenharmony_ci#endif
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic ssize_t channelmap_show(struct device *dev,
1178c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buf)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
1208c2ecf20Sopenharmony_ci	char *bp = buf;
1218c2ecf20Sopenharmony_ci	int i;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	for (i = 0; i <= mdev->nrbchan; i++)
1248c2ecf20Sopenharmony_ci		*bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return bp - buf;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(channelmap);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic struct attribute *mISDN_attrs[] = {
1318c2ecf20Sopenharmony_ci	&dev_attr_id.attr,
1328c2ecf20Sopenharmony_ci	&dev_attr_d_protocols.attr,
1338c2ecf20Sopenharmony_ci	&dev_attr_b_protocols.attr,
1348c2ecf20Sopenharmony_ci	&dev_attr_protocol.attr,
1358c2ecf20Sopenharmony_ci	&dev_attr_channelmap.attr,
1368c2ecf20Sopenharmony_ci	&dev_attr_nrbchan.attr,
1378c2ecf20Sopenharmony_ci	&dev_attr_name.attr,
1388c2ecf20Sopenharmony_ci	NULL,
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(mISDN);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (!mdev)
1478c2ecf20Sopenharmony_ci		return 0;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
1508c2ecf20Sopenharmony_ci		return -ENOMEM;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void mISDN_class_release(struct class *cls)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	/* do nothing, it's static */
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct class mISDN_class = {
1618c2ecf20Sopenharmony_ci	.name = "mISDN",
1628c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1638c2ecf20Sopenharmony_ci	.dev_uevent = mISDN_uevent,
1648c2ecf20Sopenharmony_ci	.dev_groups = mISDN_groups,
1658c2ecf20Sopenharmony_ci	.dev_release = mISDN_dev_release,
1668c2ecf20Sopenharmony_ci	.class_release = mISDN_class_release,
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int
1708c2ecf20Sopenharmony_ci_get_mdevice(struct device *dev, const void *id)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct mISDNdevice *mdev = dev_to_mISDN(dev);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (!mdev)
1758c2ecf20Sopenharmony_ci		return 0;
1768c2ecf20Sopenharmony_ci	if (mdev->id != *(const u_int *)id)
1778c2ecf20Sopenharmony_ci		return 0;
1788c2ecf20Sopenharmony_ci	return 1;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistruct mISDNdevice
1828c2ecf20Sopenharmony_ci*get_mdevice(u_int id)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
1858c2ecf20Sopenharmony_ci					      _get_mdevice));
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int
1898c2ecf20Sopenharmony_ci_get_mdevice_count(struct device *dev, void *cnt)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	*(int *)cnt += 1;
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ciint
1968c2ecf20Sopenharmony_ciget_mdevice_count(void)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	int cnt = 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
2018c2ecf20Sopenharmony_ci	return cnt;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int
2058c2ecf20Sopenharmony_ciget_free_devid(void)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	u_int	i;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	for (i = 0; i <= MAX_DEVICE_ID; i++)
2108c2ecf20Sopenharmony_ci		if (!test_and_set_bit(i, (u_long *)&device_ids))
2118c2ecf20Sopenharmony_ci			break;
2128c2ecf20Sopenharmony_ci	if (i > MAX_DEVICE_ID)
2138c2ecf20Sopenharmony_ci		return -EBUSY;
2148c2ecf20Sopenharmony_ci	return i;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciint
2188c2ecf20Sopenharmony_cimISDN_register_device(struct mISDNdevice *dev,
2198c2ecf20Sopenharmony_ci		      struct device *parent, char *name)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	int	err;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	err = get_free_devid();
2248c2ecf20Sopenharmony_ci	if (err < 0)
2258c2ecf20Sopenharmony_ci		return err;
2268c2ecf20Sopenharmony_ci	dev->id = err;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	device_initialize(&dev->dev);
2298c2ecf20Sopenharmony_ci	if (name && name[0])
2308c2ecf20Sopenharmony_ci		dev_set_name(&dev->dev, "%s", name);
2318c2ecf20Sopenharmony_ci	else
2328c2ecf20Sopenharmony_ci		dev_set_name(&dev->dev, "mISDN%d", dev->id);
2338c2ecf20Sopenharmony_ci	if (debug & DEBUG_CORE)
2348c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mISDN_register %s %d\n",
2358c2ecf20Sopenharmony_ci		       dev_name(&dev->dev), dev->id);
2368c2ecf20Sopenharmony_ci	dev->dev.class = &mISDN_class;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	err = create_stack(dev);
2398c2ecf20Sopenharmony_ci	if (err)
2408c2ecf20Sopenharmony_ci		goto error1;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	dev->dev.platform_data = dev;
2438c2ecf20Sopenharmony_ci	dev->dev.parent = parent;
2448c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, dev);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	err = device_add(&dev->dev);
2478c2ecf20Sopenharmony_ci	if (err)
2488c2ecf20Sopenharmony_ci		goto error3;
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cierror3:
2528c2ecf20Sopenharmony_ci	delete_stack(dev);
2538c2ecf20Sopenharmony_cierror1:
2548c2ecf20Sopenharmony_ci	put_device(&dev->dev);
2558c2ecf20Sopenharmony_ci	return err;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_register_device);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_civoid
2618c2ecf20Sopenharmony_cimISDN_unregister_device(struct mISDNdevice *dev) {
2628c2ecf20Sopenharmony_ci	if (debug & DEBUG_CORE)
2638c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mISDN_unregister %s %d\n",
2648c2ecf20Sopenharmony_ci		       dev_name(&dev->dev), dev->id);
2658c2ecf20Sopenharmony_ci	/* sysfs_remove_link(&dev->dev.kobj, "device"); */
2668c2ecf20Sopenharmony_ci	device_del(&dev->dev);
2678c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, NULL);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	test_and_clear_bit(dev->id, (u_long *)&device_ids);
2708c2ecf20Sopenharmony_ci	delete_stack(dev);
2718c2ecf20Sopenharmony_ci	put_device(&dev->dev);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_unregister_device);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ciu_int
2768c2ecf20Sopenharmony_ciget_all_Bprotocols(void)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct Bprotocol	*bp;
2798c2ecf20Sopenharmony_ci	u_int	m = 0;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	read_lock(&bp_lock);
2828c2ecf20Sopenharmony_ci	list_for_each_entry(bp, &Bprotocols, list)
2838c2ecf20Sopenharmony_ci		m |= bp->Bprotocols;
2848c2ecf20Sopenharmony_ci	read_unlock(&bp_lock);
2858c2ecf20Sopenharmony_ci	return m;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistruct Bprotocol *
2898c2ecf20Sopenharmony_ciget_Bprotocol4mask(u_int m)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct Bprotocol	*bp;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	read_lock(&bp_lock);
2948c2ecf20Sopenharmony_ci	list_for_each_entry(bp, &Bprotocols, list)
2958c2ecf20Sopenharmony_ci		if (bp->Bprotocols & m) {
2968c2ecf20Sopenharmony_ci			read_unlock(&bp_lock);
2978c2ecf20Sopenharmony_ci			return bp;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci	read_unlock(&bp_lock);
3008c2ecf20Sopenharmony_ci	return NULL;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistruct Bprotocol *
3048c2ecf20Sopenharmony_ciget_Bprotocol4id(u_int id)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	u_int	m;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (id < ISDN_P_B_START || id > 63) {
3098c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s id not in range  %d\n",
3108c2ecf20Sopenharmony_ci		       __func__, id);
3118c2ecf20Sopenharmony_ci		return NULL;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci	m = 1 << (id & ISDN_P_B_MASK);
3148c2ecf20Sopenharmony_ci	return get_Bprotocol4mask(m);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ciint
3188c2ecf20Sopenharmony_cimISDN_register_Bprotocol(struct Bprotocol *bp)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	u_long			flags;
3218c2ecf20Sopenharmony_ci	struct Bprotocol	*old;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (debug & DEBUG_CORE)
3248c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: %s/%x\n", __func__,
3258c2ecf20Sopenharmony_ci		       bp->name, bp->Bprotocols);
3268c2ecf20Sopenharmony_ci	old = get_Bprotocol4mask(bp->Bprotocols);
3278c2ecf20Sopenharmony_ci	if (old) {
3288c2ecf20Sopenharmony_ci		printk(KERN_WARNING
3298c2ecf20Sopenharmony_ci		       "register duplicate protocol old %s/%x new %s/%x\n",
3308c2ecf20Sopenharmony_ci		       old->name, old->Bprotocols, bp->name, bp->Bprotocols);
3318c2ecf20Sopenharmony_ci		return -EBUSY;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	write_lock_irqsave(&bp_lock, flags);
3348c2ecf20Sopenharmony_ci	list_add_tail(&bp->list, &Bprotocols);
3358c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&bp_lock, flags);
3368c2ecf20Sopenharmony_ci	return 0;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_register_Bprotocol);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_civoid
3418c2ecf20Sopenharmony_cimISDN_unregister_Bprotocol(struct Bprotocol *bp)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	u_long	flags;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (debug & DEBUG_CORE)
3468c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
3478c2ecf20Sopenharmony_ci		       bp->Bprotocols);
3488c2ecf20Sopenharmony_ci	write_lock_irqsave(&bp_lock, flags);
3498c2ecf20Sopenharmony_ci	list_del(&bp->list);
3508c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&bp_lock, flags);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_unregister_Bprotocol);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic const char *msg_no_channel = "<no channel>";
3558c2ecf20Sopenharmony_cistatic const char *msg_no_stack = "<no stack>";
3568c2ecf20Sopenharmony_cistatic const char *msg_no_stackdev = "<no stack device>";
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ciconst char *mISDNDevName4ch(struct mISDNchannel *ch)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	if (!ch)
3618c2ecf20Sopenharmony_ci		return msg_no_channel;
3628c2ecf20Sopenharmony_ci	if (!ch->st)
3638c2ecf20Sopenharmony_ci		return msg_no_stack;
3648c2ecf20Sopenharmony_ci	if (!ch->st->dev)
3658c2ecf20Sopenharmony_ci		return msg_no_stackdev;
3668c2ecf20Sopenharmony_ci	return dev_name(&ch->st->dev->dev);
3678c2ecf20Sopenharmony_ci};
3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDNDevName4ch);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int
3718c2ecf20Sopenharmony_cimISDNInit(void)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	int	err;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
3768c2ecf20Sopenharmony_ci	       MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
3778c2ecf20Sopenharmony_ci	mISDN_init_clock(&debug);
3788c2ecf20Sopenharmony_ci	mISDN_initstack(&debug);
3798c2ecf20Sopenharmony_ci	err = class_register(&mISDN_class);
3808c2ecf20Sopenharmony_ci	if (err)
3818c2ecf20Sopenharmony_ci		goto error1;
3828c2ecf20Sopenharmony_ci	err = mISDN_inittimer(&debug);
3838c2ecf20Sopenharmony_ci	if (err)
3848c2ecf20Sopenharmony_ci		goto error2;
3858c2ecf20Sopenharmony_ci	err = Isdnl1_Init(&debug);
3868c2ecf20Sopenharmony_ci	if (err)
3878c2ecf20Sopenharmony_ci		goto error3;
3888c2ecf20Sopenharmony_ci	err = Isdnl2_Init(&debug);
3898c2ecf20Sopenharmony_ci	if (err)
3908c2ecf20Sopenharmony_ci		goto error4;
3918c2ecf20Sopenharmony_ci	err = misdn_sock_init(&debug);
3928c2ecf20Sopenharmony_ci	if (err)
3938c2ecf20Sopenharmony_ci		goto error5;
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cierror5:
3978c2ecf20Sopenharmony_ci	Isdnl2_cleanup();
3988c2ecf20Sopenharmony_cierror4:
3998c2ecf20Sopenharmony_ci	Isdnl1_cleanup();
4008c2ecf20Sopenharmony_cierror3:
4018c2ecf20Sopenharmony_ci	mISDN_timer_cleanup();
4028c2ecf20Sopenharmony_cierror2:
4038c2ecf20Sopenharmony_ci	class_unregister(&mISDN_class);
4048c2ecf20Sopenharmony_cierror1:
4058c2ecf20Sopenharmony_ci	return err;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic void mISDN_cleanup(void)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	misdn_sock_cleanup();
4118c2ecf20Sopenharmony_ci	Isdnl2_cleanup();
4128c2ecf20Sopenharmony_ci	Isdnl1_cleanup();
4138c2ecf20Sopenharmony_ci	mISDN_timer_cleanup();
4148c2ecf20Sopenharmony_ci	class_unregister(&mISDN_class);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "mISDNcore unloaded\n");
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cimodule_init(mISDNInit);
4208c2ecf20Sopenharmony_cimodule_exit(mISDN_cleanup);
421