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