brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · e34a7a4 Raw
415 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2008  by Karsten Keil <kkeil@novell.com>4 */5 6#include <linux/slab.h>7#include <linux/types.h>8#include <linux/stddef.h>9#include <linux/module.h>10#include <linux/spinlock.h>11#include <linux/mISDNif.h>12#include "core.h"13 14static u_int debug;15 16MODULE_AUTHOR("Karsten Keil");17MODULE_DESCRIPTION("Modular ISDN core driver");18MODULE_LICENSE("GPL");19module_param(debug, uint, S_IRUGO | S_IWUSR);20 21static u64		device_ids;22#define MAX_DEVICE_ID	6323 24static LIST_HEAD(Bprotocols);25static DEFINE_RWLOCK(bp_lock);26 27static void mISDN_dev_release(struct device *dev)28{29	/* nothing to do: the device is part of its parent's data structure */30}31 32static ssize_t id_show(struct device *dev,33		       struct device_attribute *attr, char *buf)34{35	struct mISDNdevice *mdev = dev_to_mISDN(dev);36 37	if (!mdev)38		return -ENODEV;39	return sprintf(buf, "%d\n", mdev->id);40}41static DEVICE_ATTR_RO(id);42 43static ssize_t nrbchan_show(struct device *dev,44			    struct device_attribute *attr, char *buf)45{46	struct mISDNdevice *mdev = dev_to_mISDN(dev);47 48	if (!mdev)49		return -ENODEV;50	return sprintf(buf, "%d\n", mdev->nrbchan);51}52static DEVICE_ATTR_RO(nrbchan);53 54static ssize_t d_protocols_show(struct device *dev,55				struct device_attribute *attr, char *buf)56{57	struct mISDNdevice *mdev = dev_to_mISDN(dev);58 59	if (!mdev)60		return -ENODEV;61	return sprintf(buf, "%d\n", mdev->Dprotocols);62}63static DEVICE_ATTR_RO(d_protocols);64 65static ssize_t b_protocols_show(struct device *dev,66				struct device_attribute *attr, char *buf)67{68	struct mISDNdevice *mdev = dev_to_mISDN(dev);69 70	if (!mdev)71		return -ENODEV;72	return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());73}74static DEVICE_ATTR_RO(b_protocols);75 76static ssize_t protocol_show(struct device *dev,77			     struct device_attribute *attr, char *buf)78{79	struct mISDNdevice *mdev = dev_to_mISDN(dev);80 81	if (!mdev)82		return -ENODEV;83	return sprintf(buf, "%d\n", mdev->D.protocol);84}85static DEVICE_ATTR_RO(protocol);86 87static ssize_t name_show(struct device *dev,88			 struct device_attribute *attr, char *buf)89{90	strcpy(buf, dev_name(dev));91	return strlen(buf);92}93static DEVICE_ATTR_RO(name);94 95#if 0 /* hangs */96static ssize_t name_set(struct device *dev, struct device_attribute *attr,97			const char *buf, size_t count)98{99	int err = 0;100	char *out = kmalloc(count + 1, GFP_KERNEL);101 102	if (!out)103		return -ENOMEM;104 105	memcpy(out, buf, count);106	if (count && out[count - 1] == '\n')107		out[--count] = 0;108	if (count)109		err = device_rename(dev, out);110	kfree(out);111 112	return (err < 0) ? err : count;113}114static DEVICE_ATTR_RW(name);115#endif116 117static ssize_t channelmap_show(struct device *dev,118			       struct device_attribute *attr, char *buf)119{120	struct mISDNdevice *mdev = dev_to_mISDN(dev);121	char *bp = buf;122	int i;123 124	for (i = 0; i <= mdev->nrbchan; i++)125		*bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';126 127	return bp - buf;128}129static DEVICE_ATTR_RO(channelmap);130 131static struct attribute *mISDN_attrs[] = {132	&dev_attr_id.attr,133	&dev_attr_d_protocols.attr,134	&dev_attr_b_protocols.attr,135	&dev_attr_protocol.attr,136	&dev_attr_channelmap.attr,137	&dev_attr_nrbchan.attr,138	&dev_attr_name.attr,139	NULL,140};141ATTRIBUTE_GROUPS(mISDN);142 143static int mISDN_uevent(const struct device *dev, struct kobj_uevent_env *env)144{145	const struct mISDNdevice *mdev = dev_to_mISDN(dev);146 147	if (!mdev)148		return 0;149 150	if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))151		return -ENOMEM;152 153	return 0;154}155 156static struct class mISDN_class = {157	.name = "mISDN",158	.dev_uevent = mISDN_uevent,159	.dev_groups = mISDN_groups,160	.dev_release = mISDN_dev_release,161};162 163static int164_get_mdevice(struct device *dev, const void *id)165{166	struct mISDNdevice *mdev = dev_to_mISDN(dev);167 168	if (!mdev)169		return 0;170	if (mdev->id != *(const u_int *)id)171		return 0;172	return 1;173}174 175struct mISDNdevice176*get_mdevice(u_int id)177{178	return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,179					      _get_mdevice));180}181 182static int183_get_mdevice_count(struct device *dev, void *cnt)184{185	*(int *)cnt += 1;186	return 0;187}188 189int190get_mdevice_count(void)191{192	int cnt = 0;193 194	class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);195	return cnt;196}197 198static int199get_free_devid(void)200{201	u_int	i;202 203	for (i = 0; i <= MAX_DEVICE_ID; i++)204		if (!test_and_set_bit(i, (u_long *)&device_ids))205			break;206	if (i > MAX_DEVICE_ID)207		return -EBUSY;208	return i;209}210 211int212mISDN_register_device(struct mISDNdevice *dev,213		      struct device *parent, char *name)214{215	int	err;216 217	err = get_free_devid();218	if (err < 0)219		return err;220	dev->id = err;221 222	device_initialize(&dev->dev);223	if (name && name[0])224		dev_set_name(&dev->dev, "%s", name);225	else226		dev_set_name(&dev->dev, "mISDN%d", dev->id);227	if (debug & DEBUG_CORE)228		printk(KERN_DEBUG "mISDN_register %s %d\n",229		       dev_name(&dev->dev), dev->id);230	dev->dev.class = &mISDN_class;231 232	err = create_stack(dev);233	if (err)234		goto error1;235 236	dev->dev.platform_data = dev;237	dev->dev.parent = parent;238	dev_set_drvdata(&dev->dev, dev);239 240	err = device_add(&dev->dev);241	if (err)242		goto error3;243	return 0;244 245error3:246	delete_stack(dev);247error1:248	put_device(&dev->dev);249	return err;250 251}252EXPORT_SYMBOL(mISDN_register_device);253 254void255mISDN_unregister_device(struct mISDNdevice *dev) {256	if (debug & DEBUG_CORE)257		printk(KERN_DEBUG "mISDN_unregister %s %d\n",258		       dev_name(&dev->dev), dev->id);259	/* sysfs_remove_link(&dev->dev.kobj, "device"); */260	device_del(&dev->dev);261	dev_set_drvdata(&dev->dev, NULL);262 263	test_and_clear_bit(dev->id, (u_long *)&device_ids);264	delete_stack(dev);265	put_device(&dev->dev);266}267EXPORT_SYMBOL(mISDN_unregister_device);268 269u_int270get_all_Bprotocols(void)271{272	struct Bprotocol	*bp;273	u_int	m = 0;274 275	read_lock(&bp_lock);276	list_for_each_entry(bp, &Bprotocols, list)277		m |= bp->Bprotocols;278	read_unlock(&bp_lock);279	return m;280}281 282struct Bprotocol *283get_Bprotocol4mask(u_int m)284{285	struct Bprotocol	*bp;286 287	read_lock(&bp_lock);288	list_for_each_entry(bp, &Bprotocols, list)289		if (bp->Bprotocols & m) {290			read_unlock(&bp_lock);291			return bp;292		}293	read_unlock(&bp_lock);294	return NULL;295}296 297struct Bprotocol *298get_Bprotocol4id(u_int id)299{300	u_int	m;301 302	if (id < ISDN_P_B_START || id > 63) {303		printk(KERN_WARNING "%s id not in range  %d\n",304		       __func__, id);305		return NULL;306	}307	m = 1 << (id & ISDN_P_B_MASK);308	return get_Bprotocol4mask(m);309}310 311int312mISDN_register_Bprotocol(struct Bprotocol *bp)313{314	u_long			flags;315	struct Bprotocol	*old;316 317	if (debug & DEBUG_CORE)318		printk(KERN_DEBUG "%s: %s/%x\n", __func__,319		       bp->name, bp->Bprotocols);320	old = get_Bprotocol4mask(bp->Bprotocols);321	if (old) {322		printk(KERN_WARNING323		       "register duplicate protocol old %s/%x new %s/%x\n",324		       old->name, old->Bprotocols, bp->name, bp->Bprotocols);325		return -EBUSY;326	}327	write_lock_irqsave(&bp_lock, flags);328	list_add_tail(&bp->list, &Bprotocols);329	write_unlock_irqrestore(&bp_lock, flags);330	return 0;331}332EXPORT_SYMBOL(mISDN_register_Bprotocol);333 334void335mISDN_unregister_Bprotocol(struct Bprotocol *bp)336{337	u_long	flags;338 339	if (debug & DEBUG_CORE)340		printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,341		       bp->Bprotocols);342	write_lock_irqsave(&bp_lock, flags);343	list_del(&bp->list);344	write_unlock_irqrestore(&bp_lock, flags);345}346EXPORT_SYMBOL(mISDN_unregister_Bprotocol);347 348static const char *msg_no_channel = "<no channel>";349static const char *msg_no_stack = "<no stack>";350static const char *msg_no_stackdev = "<no stack device>";351 352const char *mISDNDevName4ch(struct mISDNchannel *ch)353{354	if (!ch)355		return msg_no_channel;356	if (!ch->st)357		return msg_no_stack;358	if (!ch->st->dev)359		return msg_no_stackdev;360	return dev_name(&ch->st->dev->dev);361};362EXPORT_SYMBOL(mISDNDevName4ch);363 364static int365mISDNInit(void)366{367	int	err;368 369	printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",370	       MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);371	mISDN_init_clock(&debug);372	mISDN_initstack(&debug);373	err = class_register(&mISDN_class);374	if (err)375		goto error1;376	err = mISDN_inittimer(&debug);377	if (err)378		goto error2;379	err = Isdnl1_Init(&debug);380	if (err)381		goto error3;382	err = Isdnl2_Init(&debug);383	if (err)384		goto error4;385	err = misdn_sock_init(&debug);386	if (err)387		goto error5;388	return 0;389 390error5:391	Isdnl2_cleanup();392error4:393	Isdnl1_cleanup();394error3:395	mISDN_timer_cleanup();396error2:397	class_unregister(&mISDN_class);398error1:399	return err;400}401 402static void mISDN_cleanup(void)403{404	misdn_sock_cleanup();405	Isdnl2_cleanup();406	Isdnl1_cleanup();407	mISDN_timer_cleanup();408	class_unregister(&mISDN_class);409 410	printk(KERN_DEBUG "mISDNcore unloaded\n");411}412 413module_init(mISDNInit);414module_exit(mISDN_cleanup);415