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 _show_id(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}
  49
  50static ssize_t _show_nrbchan(struct device *dev,
  51                             struct device_attribute *attr, char *buf)
  52{
  53        struct mISDNdevice *mdev = dev_to_mISDN(dev);
  54
  55        if (!mdev)
  56                return -ENODEV;
  57        return sprintf(buf, "%d\n", mdev->nrbchan);
  58}
  59
  60static ssize_t _show_d_protocols(struct device *dev,
  61                                 struct device_attribute *attr, char *buf)
  62{
  63        struct mISDNdevice *mdev = dev_to_mISDN(dev);
  64
  65        if (!mdev)
  66                return -ENODEV;
  67        return sprintf(buf, "%d\n", mdev->Dprotocols);
  68}
  69
  70static ssize_t _show_b_protocols(struct device *dev,
  71                                 struct device_attribute *attr, char *buf)
  72{
  73        struct mISDNdevice *mdev = dev_to_mISDN(dev);
  74
  75        if (!mdev)
  76                return -ENODEV;
  77        return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
  78}
  79
  80static ssize_t _show_protocol(struct device *dev,
  81                              struct device_attribute *attr, char *buf)
  82{
  83        struct mISDNdevice *mdev = dev_to_mISDN(dev);
  84
  85        if (!mdev)
  86                return -ENODEV;
  87        return sprintf(buf, "%d\n", mdev->D.protocol);
  88}
  89
  90static ssize_t _show_name(struct device *dev,
  91                          struct device_attribute *attr, char *buf)
  92{
  93        strcpy(buf, dev_name(dev));
  94        return strlen(buf);
  95}
  96
  97#if 0 /* hangs */
  98static ssize_t _set_name(struct device *dev, struct device_attribute *attr,
  99                         const char *buf, size_t count)
 100{
 101        int err = 0;
 102        char *out = kmalloc(count + 1, GFP_KERNEL);
 103
 104        if (!out)
 105                return -ENOMEM;
 106
 107        memcpy(out, buf, count);
 108        if (count && out[count - 1] == '\n')
 109                out[--count] = 0;
 110        if (count)
 111                err = device_rename(dev, out);
 112        kfree(out);
 113
 114        return (err < 0) ? err : count;
 115}
 116#endif
 117
 118static ssize_t _show_channelmap(struct device *dev,
 119                                struct device_attribute *attr, char *buf)
 120{
 121        struct mISDNdevice *mdev = dev_to_mISDN(dev);
 122        char *bp = buf;
 123        int i;
 124
 125        for (i = 0; i <= mdev->nrbchan; i++)
 126                *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
 127
 128        return bp - buf;
 129}
 130
 131static struct device_attribute mISDN_dev_attrs[] = {
 132        __ATTR(id,          S_IRUGO,         _show_id,          NULL),
 133        __ATTR(d_protocols, S_IRUGO,         _show_d_protocols, NULL),
 134        __ATTR(b_protocols, S_IRUGO,         _show_b_protocols, NULL),
 135        __ATTR(protocol,    S_IRUGO,         _show_protocol,    NULL),
 136        __ATTR(channelmap,  S_IRUGO,         _show_channelmap,  NULL),
 137        __ATTR(nrbchan,     S_IRUGO,         _show_nrbchan,     NULL),
 138        __ATTR(name,        S_IRUGO,         _show_name,        NULL),
 139/*      __ATTR(name,        S_IRUGO | S_IWUSR, _show_name,      _set_name), */
 140        {}
 141};
 142
 143#ifdef CONFIG_HOTPLUG
 144static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
 145{
 146        struct mISDNdevice *mdev = dev_to_mISDN(dev);
 147
 148        if (!mdev)
 149                return 0;
 150
 151        if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
 152                return -ENOMEM;
 153
 154        return 0;
 155}
 156#endif
 157
 158static void mISDN_class_release(struct class *cls)
 159{
 160        /* do nothing, it's static */
 161}
 162
 163static struct class mISDN_class = {
 164        .name = "mISDN",
 165        .owner = THIS_MODULE,
 166#ifdef CONFIG_HOTPLUG
 167        .dev_uevent = mISDN_uevent,
 168#endif
 169        .dev_attrs = mISDN_dev_attrs,
 170        .dev_release = mISDN_dev_release,
 171        .class_release = mISDN_class_release,
 172};
 173
 174static int
 175_get_mdevice(struct device *dev, void *id)
 176{
 177        struct mISDNdevice *mdev = dev_to_mISDN(dev);
 178
 179        if (!mdev)
 180                return 0;
 181        if (mdev->id != *(u_int *)id)
 182                return 0;
 183        return 1;
 184}
 185
 186struct mISDNdevice
 187*get_mdevice(u_int id)
 188{
 189        return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
 190                                              _get_mdevice));
 191}
 192
 193static int
 194_get_mdevice_count(struct device *dev, void *cnt)
 195{
 196        *(int *)cnt += 1;
 197        return 0;
 198}
 199
 200int
 201get_mdevice_count(void)
 202{
 203        int cnt = 0;
 204
 205        class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
 206        return cnt;
 207}
 208
 209static int
 210get_free_devid(void)
 211{
 212        u_int   i;
 213
 214        for (i = 0; i <= MAX_DEVICE_ID; i++)
 215                if (!test_and_set_bit(i, (u_long *)&device_ids))
 216                        break;
 217        if (i > MAX_DEVICE_ID)
 218                return -EBUSY;
 219        return i;
 220}
 221
 222int
 223mISDN_register_device(struct mISDNdevice *dev,
 224                      struct device *parent, char *name)
 225{
 226        int     err;
 227
 228        err = get_free_devid();
 229        if (err < 0)
 230                goto error1;
 231        dev->id = err;
 232
 233        device_initialize(&dev->dev);
 234        if (name && name[0])
 235                dev_set_name(&dev->dev, "%s", name);
 236        else
 237                dev_set_name(&dev->dev, "mISDN%d", dev->id);
 238        if (debug & DEBUG_CORE)
 239                printk(KERN_DEBUG "mISDN_register %s %d\n",
 240                       dev_name(&dev->dev), dev->id);
 241        err = create_stack(dev);
 242        if (err)
 243                goto error1;
 244
 245        dev->dev.class = &mISDN_class;
 246        dev->dev.platform_data = dev;
 247        dev->dev.parent = parent;
 248        dev_set_drvdata(&dev->dev, dev);
 249
 250        err = device_add(&dev->dev);
 251        if (err)
 252                goto error3;
 253        return 0;
 254
 255error3:
 256        delete_stack(dev);
 257        return err;
 258error1:
 259        return err;
 260
 261}
 262EXPORT_SYMBOL(mISDN_register_device);
 263
 264void
 265mISDN_unregister_device(struct mISDNdevice *dev) {
 266        if (debug & DEBUG_CORE)
 267                printk(KERN_DEBUG "mISDN_unregister %s %d\n",
 268                       dev_name(&dev->dev), dev->id);
 269        /* sysfs_remove_link(&dev->dev.kobj, "device"); */
 270        device_del(&dev->dev);
 271        dev_set_drvdata(&dev->dev, NULL);
 272
 273        test_and_clear_bit(dev->id, (u_long *)&device_ids);
 274        delete_stack(dev);
 275        put_device(&dev->dev);
 276}
 277EXPORT_SYMBOL(mISDN_unregister_device);
 278
 279u_int
 280get_all_Bprotocols(void)
 281{
 282        struct Bprotocol        *bp;
 283        u_int   m = 0;
 284
 285        read_lock(&bp_lock);
 286        list_for_each_entry(bp, &Bprotocols, list)
 287                m |= bp->Bprotocols;
 288        read_unlock(&bp_lock);
 289        return m;
 290}
 291
 292struct Bprotocol *
 293get_Bprotocol4mask(u_int m)
 294{
 295        struct Bprotocol        *bp;
 296
 297        read_lock(&bp_lock);
 298        list_for_each_entry(bp, &Bprotocols, list)
 299                if (bp->Bprotocols & m) {
 300                        read_unlock(&bp_lock);
 301                        return bp;
 302                }
 303        read_unlock(&bp_lock);
 304        return NULL;
 305}
 306
 307struct Bprotocol *
 308get_Bprotocol4id(u_int id)
 309{
 310        u_int   m;
 311
 312        if (id < ISDN_P_B_START || id > 63) {
 313                printk(KERN_WARNING "%s id not in range  %d\n",
 314                       __func__, id);
 315                return NULL;
 316        }
 317        m = 1 << (id & ISDN_P_B_MASK);
 318        return get_Bprotocol4mask(m);
 319}
 320
 321int
 322mISDN_register_Bprotocol(struct Bprotocol *bp)
 323{
 324        u_long                  flags;
 325        struct Bprotocol        *old;
 326
 327        if (debug & DEBUG_CORE)
 328                printk(KERN_DEBUG "%s: %s/%x\n", __func__,
 329                       bp->name, bp->Bprotocols);
 330        old = get_Bprotocol4mask(bp->Bprotocols);
 331        if (old) {
 332                printk(KERN_WARNING
 333                       "register duplicate protocol old %s/%x new %s/%x\n",
 334                       old->name, old->Bprotocols, bp->name, bp->Bprotocols);
 335                return -EBUSY;
 336        }
 337        write_lock_irqsave(&bp_lock, flags);
 338        list_add_tail(&bp->list, &Bprotocols);
 339        write_unlock_irqrestore(&bp_lock, flags);
 340        return 0;
 341}
 342EXPORT_SYMBOL(mISDN_register_Bprotocol);
 343
 344void
 345mISDN_unregister_Bprotocol(struct Bprotocol *bp)
 346{
 347        u_long  flags;
 348
 349        if (debug & DEBUG_CORE)
 350                printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
 351                       bp->Bprotocols);
 352        write_lock_irqsave(&bp_lock, flags);
 353        list_del(&bp->list);
 354        write_unlock_irqrestore(&bp_lock, flags);
 355}
 356EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
 357
 358static const char *msg_no_channel = "<no channel>";
 359static const char *msg_no_stack = "<no stack>";
 360static const char *msg_no_stackdev = "<no stack device>";
 361
 362const char *mISDNDevName4ch(struct mISDNchannel *ch)
 363{
 364        if (!ch)
 365                return msg_no_channel;
 366        if (!ch->st)
 367                return msg_no_stack;
 368        if (!ch->st->dev)
 369                return msg_no_stackdev;
 370        return dev_name(&ch->st->dev->dev);
 371};
 372EXPORT_SYMBOL(mISDNDevName4ch);
 373
 374static int
 375mISDNInit(void)
 376{
 377        int     err;
 378
 379        printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
 380               MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
 381        mISDN_init_clock(&debug);
 382        mISDN_initstack(&debug);
 383        err = class_register(&mISDN_class);
 384        if (err)
 385                goto error1;
 386        err = mISDN_inittimer(&debug);
 387        if (err)
 388                goto error2;
 389        err = l1_init(&debug);
 390        if (err)
 391                goto error3;
 392        err = Isdnl2_Init(&debug);
 393        if (err)
 394                goto error4;
 395        err = misdn_sock_init(&debug);
 396        if (err)
 397                goto error5;
 398        return 0;
 399
 400error5:
 401        Isdnl2_cleanup();
 402error4:
 403        l1_cleanup();
 404error3:
 405        mISDN_timer_cleanup();
 406error2:
 407        class_unregister(&mISDN_class);
 408error1:
 409        return err;
 410}
 411
 412static void mISDN_cleanup(void)
 413{
 414        misdn_sock_cleanup();
 415        Isdnl2_cleanup();
 416        l1_cleanup();
 417        mISDN_timer_cleanup();
 418        class_unregister(&mISDN_class);
 419
 420        printk(KERN_DEBUG "mISDNcore unloaded\n");
 421}
 422
 423module_init(mISDNInit);
 424module_exit(mISDN_cleanup);
 425