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