123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- /*
- * Copyright 2008 by Karsten Keil <kkeil@novell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <linux/slab.h>
- #include <linux/types.h>
- #include <linux/stddef.h>
- #include <linux/module.h>
- #include <linux/spinlock.h>
- #include <linux/mISDNif.h>
- #include "core.h"
- static u_int debug;
- MODULE_AUTHOR("Karsten Keil");
- MODULE_LICENSE("GPL");
- module_param(debug, uint, S_IRUGO | S_IWUSR);
- static u64 device_ids;
- #define MAX_DEVICE_ID 63
- static LIST_HEAD(Bprotocols);
- static DEFINE_RWLOCK(bp_lock);
- static void mISDN_dev_release(struct device *dev)
- {
- /* nothing to do: the device is part of its parent's data structure */
- }
- static ssize_t id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- if (!mdev)
- return -ENODEV;
- return sprintf(buf, "%d\n", mdev->id);
- }
- static DEVICE_ATTR_RO(id);
- static ssize_t nrbchan_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- if (!mdev)
- return -ENODEV;
- return sprintf(buf, "%d\n", mdev->nrbchan);
- }
- static DEVICE_ATTR_RO(nrbchan);
- static ssize_t d_protocols_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- if (!mdev)
- return -ENODEV;
- return sprintf(buf, "%d\n", mdev->Dprotocols);
- }
- static DEVICE_ATTR_RO(d_protocols);
- static ssize_t b_protocols_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- if (!mdev)
- return -ENODEV;
- return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
- }
- static DEVICE_ATTR_RO(b_protocols);
- static ssize_t protocol_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- if (!mdev)
- return -ENODEV;
- return sprintf(buf, "%d\n", mdev->D.protocol);
- }
- static DEVICE_ATTR_RO(protocol);
- static ssize_t name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- strcpy(buf, dev_name(dev));
- return strlen(buf);
- }
- static DEVICE_ATTR_RO(name);
- #if 0 /* hangs */
- static ssize_t name_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- int err = 0;
- char *out = kmalloc(count + 1, GFP_KERNEL);
- if (!out)
- return -ENOMEM;
- memcpy(out, buf, count);
- if (count && out[count - 1] == '\n')
- out[--count] = 0;
- if (count)
- err = device_rename(dev, out);
- kfree(out);
- return (err < 0) ? err : count;
- }
- static DEVICE_ATTR_RW(name);
- #endif
- static ssize_t channelmap_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- char *bp = buf;
- int i;
- for (i = 0; i <= mdev->nrbchan; i++)
- *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
- return bp - buf;
- }
- static DEVICE_ATTR_RO(channelmap);
- static struct attribute *mISDN_attrs[] = {
- &dev_attr_id.attr,
- &dev_attr_d_protocols.attr,
- &dev_attr_b_protocols.attr,
- &dev_attr_protocol.attr,
- &dev_attr_channelmap.attr,
- &dev_attr_nrbchan.attr,
- &dev_attr_name.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(mISDN);
- static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- if (!mdev)
- return 0;
- if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
- return -ENOMEM;
- return 0;
- }
- static void mISDN_class_release(struct class *cls)
- {
- /* do nothing, it's static */
- }
- static struct class mISDN_class = {
- .name = "mISDN",
- .owner = THIS_MODULE,
- .dev_uevent = mISDN_uevent,
- .dev_groups = mISDN_groups,
- .dev_release = mISDN_dev_release,
- .class_release = mISDN_class_release,
- };
- static int
- _get_mdevice(struct device *dev, const void *id)
- {
- struct mISDNdevice *mdev = dev_to_mISDN(dev);
- if (!mdev)
- return 0;
- if (mdev->id != *(const u_int *)id)
- return 0;
- return 1;
- }
- struct mISDNdevice
- *get_mdevice(u_int id)
- {
- return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
- _get_mdevice));
- }
- static int
- _get_mdevice_count(struct device *dev, void *cnt)
- {
- *(int *)cnt += 1;
- return 0;
- }
- int
- get_mdevice_count(void)
- {
- int cnt = 0;
- class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
- return cnt;
- }
- static int
- get_free_devid(void)
- {
- u_int i;
- for (i = 0; i <= MAX_DEVICE_ID; i++)
- if (!test_and_set_bit(i, (u_long *)&device_ids))
- break;
- if (i > MAX_DEVICE_ID)
- return -EBUSY;
- return i;
- }
- int
- mISDN_register_device(struct mISDNdevice *dev,
- struct device *parent, char *name)
- {
- int err;
- err = get_free_devid();
- if (err < 0)
- goto error1;
- dev->id = err;
- device_initialize(&dev->dev);
- if (name && name[0])
- dev_set_name(&dev->dev, "%s", name);
- else
- dev_set_name(&dev->dev, "mISDN%d", dev->id);
- if (debug & DEBUG_CORE)
- printk(KERN_DEBUG "mISDN_register %s %d\n",
- dev_name(&dev->dev), dev->id);
- err = create_stack(dev);
- if (err)
- goto error1;
- dev->dev.class = &mISDN_class;
- dev->dev.platform_data = dev;
- dev->dev.parent = parent;
- dev_set_drvdata(&dev->dev, dev);
- err = device_add(&dev->dev);
- if (err)
- goto error3;
- return 0;
- error3:
- delete_stack(dev);
- return err;
- error1:
- return err;
- }
- EXPORT_SYMBOL(mISDN_register_device);
- void
- mISDN_unregister_device(struct mISDNdevice *dev) {
- if (debug & DEBUG_CORE)
- printk(KERN_DEBUG "mISDN_unregister %s %d\n",
- dev_name(&dev->dev), dev->id);
- /* sysfs_remove_link(&dev->dev.kobj, "device"); */
- device_del(&dev->dev);
- dev_set_drvdata(&dev->dev, NULL);
- test_and_clear_bit(dev->id, (u_long *)&device_ids);
- delete_stack(dev);
- put_device(&dev->dev);
- }
- EXPORT_SYMBOL(mISDN_unregister_device);
- u_int
- get_all_Bprotocols(void)
- {
- struct Bprotocol *bp;
- u_int m = 0;
- read_lock(&bp_lock);
- list_for_each_entry(bp, &Bprotocols, list)
- m |= bp->Bprotocols;
- read_unlock(&bp_lock);
- return m;
- }
- struct Bprotocol *
- get_Bprotocol4mask(u_int m)
- {
- struct Bprotocol *bp;
- read_lock(&bp_lock);
- list_for_each_entry(bp, &Bprotocols, list)
- if (bp->Bprotocols & m) {
- read_unlock(&bp_lock);
- return bp;
- }
- read_unlock(&bp_lock);
- return NULL;
- }
- struct Bprotocol *
- get_Bprotocol4id(u_int id)
- {
- u_int m;
- if (id < ISDN_P_B_START || id > 63) {
- printk(KERN_WARNING "%s id not in range %d\n",
- __func__, id);
- return NULL;
- }
- m = 1 << (id & ISDN_P_B_MASK);
- return get_Bprotocol4mask(m);
- }
- int
- mISDN_register_Bprotocol(struct Bprotocol *bp)
- {
- u_long flags;
- struct Bprotocol *old;
- if (debug & DEBUG_CORE)
- printk(KERN_DEBUG "%s: %s/%x\n", __func__,
- bp->name, bp->Bprotocols);
- old = get_Bprotocol4mask(bp->Bprotocols);
- if (old) {
- printk(KERN_WARNING
- "register duplicate protocol old %s/%x new %s/%x\n",
- old->name, old->Bprotocols, bp->name, bp->Bprotocols);
- return -EBUSY;
- }
- write_lock_irqsave(&bp_lock, flags);
- list_add_tail(&bp->list, &Bprotocols);
- write_unlock_irqrestore(&bp_lock, flags);
- return 0;
- }
- EXPORT_SYMBOL(mISDN_register_Bprotocol);
- void
- mISDN_unregister_Bprotocol(struct Bprotocol *bp)
- {
- u_long flags;
- if (debug & DEBUG_CORE)
- printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
- bp->Bprotocols);
- write_lock_irqsave(&bp_lock, flags);
- list_del(&bp->list);
- write_unlock_irqrestore(&bp_lock, flags);
- }
- EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
- static const char *msg_no_channel = "<no channel>";
- static const char *msg_no_stack = "<no stack>";
- static const char *msg_no_stackdev = "<no stack device>";
- const char *mISDNDevName4ch(struct mISDNchannel *ch)
- {
- if (!ch)
- return msg_no_channel;
- if (!ch->st)
- return msg_no_stack;
- if (!ch->st->dev)
- return msg_no_stackdev;
- return dev_name(&ch->st->dev->dev);
- };
- EXPORT_SYMBOL(mISDNDevName4ch);
- static int
- mISDNInit(void)
- {
- int err;
- printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
- MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
- mISDN_init_clock(&debug);
- mISDN_initstack(&debug);
- err = class_register(&mISDN_class);
- if (err)
- goto error1;
- err = mISDN_inittimer(&debug);
- if (err)
- goto error2;
- err = l1_init(&debug);
- if (err)
- goto error3;
- err = Isdnl2_Init(&debug);
- if (err)
- goto error4;
- err = misdn_sock_init(&debug);
- if (err)
- goto error5;
- return 0;
- error5:
- Isdnl2_cleanup();
- error4:
- l1_cleanup();
- error3:
- mISDN_timer_cleanup();
- error2:
- class_unregister(&mISDN_class);
- error1:
- return err;
- }
- static void mISDN_cleanup(void)
- {
- misdn_sock_cleanup();
- Isdnl2_cleanup();
- l1_cleanup();
- mISDN_timer_cleanup();
- class_unregister(&mISDN_class);
- printk(KERN_DEBUG "mISDNcore unloaded\n");
- }
- module_init(mISDNInit);
- module_exit(mISDN_cleanup);
|