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 int
 359mISDNInit(void)
 360{
 361        int     err;
 362
 363        printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
 364                MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
 365        mISDN_init_clock(&debug);
 366        mISDN_initstack(&debug);
 367        err = class_register(&mISDN_class);
 368        if (err)
 369                goto error1;
 370        err = mISDN_inittimer(&debug);
 371        if (err)
 372                goto error2;
 373        err = l1_init(&debug);
 374        if (err)
 375                goto error3;
 376        err = Isdnl2_Init(&debug);
 377        if (err)
 378                goto error4;
 379        err = misdn_sock_init(&debug);
 380        if (err)
 381                goto error5;
 382        return 0;
 383
 384error5:
 385        Isdnl2_cleanup();
 386error4:
 387        l1_cleanup();
 388error3:
 389        mISDN_timer_cleanup();
 390error2:
 391        class_unregister(&mISDN_class);
 392error1:
 393        return err;
 394}
 395
 396static void mISDN_cleanup(void)
 397{
 398        misdn_sock_cleanup();
 399        Isdnl2_cleanup();
 400        l1_cleanup();
 401        mISDN_timer_cleanup();
 402        class_unregister(&mISDN_class);
 403
 404        printk(KERN_DEBUG "mISDNcore unloaded\n");
 405}
 406
 407module_init(mISDNInit);
 408module_exit(mISDN_cleanup);
 409
 410