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