2010-08-28 05:59:27 +08:00
|
|
|
/*
|
|
|
|
* Dynamic Span Interface for DAHDI (Local Interface)
|
|
|
|
*
|
|
|
|
* Written by Nicolas Bougues <nbougues@axialys.net>
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004, Axialys Interactive
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Note : a DAHDI timing source must exist prior to loading this driver
|
|
|
|
*
|
|
|
|
* Address syntax :
|
|
|
|
* <key>:<id>[:<monitor id>]
|
|
|
|
*
|
|
|
|
* As of now, keys and ids are single digit only
|
|
|
|
*
|
|
|
|
* One span may have up to one "normal" peer, and one "monitor" peer
|
|
|
|
*
|
|
|
|
* Example :
|
|
|
|
*
|
|
|
|
* Say you have two spans cross connected, a third one monitoring RX on the
|
|
|
|
* first one, a fourth one monitoring RX on the second one
|
|
|
|
*
|
|
|
|
* 1:0
|
|
|
|
* 1:1
|
|
|
|
* 1:2:0
|
|
|
|
* 1:3:1
|
|
|
|
*
|
|
|
|
* Contrary to TDMoE, no frame loss can occur.
|
|
|
|
*
|
|
|
|
* See bug #2021 for more details
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2 as published by the
|
|
|
|
* Free Software Foundation. See the LICENSE file included with
|
|
|
|
* this program for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/kmod.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/notifier.h>
|
|
|
|
|
|
|
|
#include <dahdi/kernel.h>
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
/**
|
|
|
|
* struct dahdi_dynamic_loc - For local dynamic spans
|
|
|
|
* @monitor_rx_peer: Indicates the peer span that monitors this span.
|
|
|
|
* @peer: Indicates the rw peer for this span.
|
|
|
|
*
|
|
|
|
*/
|
2011-01-04 02:24:52 +08:00
|
|
|
struct dahdi_dynamic_local {
|
2010-08-28 05:59:27 +08:00
|
|
|
unsigned short key;
|
|
|
|
unsigned short id;
|
2011-01-04 02:24:43 +08:00
|
|
|
struct dahdi_dynamic_local *monitor_rx_peer;
|
|
|
|
struct dahdi_dynamic_local *peer;
|
2010-08-28 05:59:27 +08:00
|
|
|
struct dahdi_span *span;
|
2011-01-04 02:24:52 +08:00
|
|
|
struct list_head node;
|
|
|
|
};
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(local_lock);
|
|
|
|
static LIST_HEAD(dynamic_local_list);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
static int
|
|
|
|
dahdi_dynamic_local_transmit(void *pvt, unsigned char *msg, int msglen)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2011-01-04 02:24:52 +08:00
|
|
|
struct dahdi_dynamic_local *const d = pvt;
|
2010-08-28 05:59:27 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
spin_lock_irqsave(&local_lock, flags);
|
2011-01-04 02:25:00 +08:00
|
|
|
if (d->peer && d->peer->span) {
|
|
|
|
if (test_bit(DAHDI_FLAGBIT_REGISTERED, &d->peer->span->flags))
|
|
|
|
dahdi_dynamic_receive(d->peer->span, msg, msglen);
|
|
|
|
}
|
|
|
|
if (d->monitor_rx_peer && d->monitor_rx_peer->span) {
|
|
|
|
if (test_bit(DAHDI_FLAGBIT_REGISTERED,
|
|
|
|
&d->monitor_rx_peer->span->flags)) {
|
|
|
|
dahdi_dynamic_receive(d->monitor_rx_peer->span,
|
|
|
|
msg, msglen);
|
|
|
|
}
|
|
|
|
}
|
2011-01-04 02:24:43 +08:00
|
|
|
spin_unlock_irqrestore(&local_lock, flags);
|
2010-08-28 05:59:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int digit2int(char d)
|
|
|
|
{
|
|
|
|
switch(d) {
|
|
|
|
case 'F':
|
|
|
|
case 'E':
|
|
|
|
case 'D':
|
|
|
|
case 'C':
|
|
|
|
case 'B':
|
|
|
|
case 'A':
|
|
|
|
return d - 'A' + 10;
|
|
|
|
case 'f':
|
|
|
|
case 'e':
|
|
|
|
case 'd':
|
|
|
|
case 'c':
|
|
|
|
case 'b':
|
|
|
|
case 'a':
|
|
|
|
return d - 'a' + 10;
|
|
|
|
case '9':
|
|
|
|
case '8':
|
|
|
|
case '7':
|
|
|
|
case '6':
|
|
|
|
case '5':
|
|
|
|
case '4':
|
|
|
|
case '3':
|
|
|
|
case '2':
|
|
|
|
case '1':
|
|
|
|
case '0':
|
|
|
|
return d - '0';
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
static void dahdi_dynamic_local_destroy(void *pvt)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2011-01-04 02:24:43 +08:00
|
|
|
struct dahdi_dynamic_local *d = pvt;
|
2010-08-28 05:59:27 +08:00
|
|
|
unsigned long flags;
|
2011-01-04 02:24:52 +08:00
|
|
|
struct dahdi_dynamic_local *cur;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
spin_lock_irqsave(&local_lock, flags);
|
2011-01-04 02:24:52 +08:00
|
|
|
list_for_each_entry(cur, &dynamic_local_list, node) {
|
2011-01-04 02:24:43 +08:00
|
|
|
if (cur->peer == d)
|
2010-08-28 05:59:27 +08:00
|
|
|
cur->peer = NULL;
|
2011-01-04 02:24:43 +08:00
|
|
|
if (cur->monitor_rx_peer == d)
|
2010-08-28 05:59:27 +08:00
|
|
|
cur->monitor_rx_peer = NULL;
|
|
|
|
}
|
2011-01-04 02:24:52 +08:00
|
|
|
list_del(&d->node);
|
2011-01-04 02:24:43 +08:00
|
|
|
spin_unlock_irqrestore(&local_lock, flags);
|
2011-01-04 02:24:52 +08:00
|
|
|
|
|
|
|
printk(KERN_INFO "TDMoL: Removed interface for %s, key %d "
|
|
|
|
"id %d\n", d->span->name, d->key, d->id);
|
|
|
|
module_put(THIS_MODULE);
|
|
|
|
kfree(d);
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
static void *dahdi_dynamic_local_create(struct dahdi_span *span, char *address)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2011-01-04 02:24:43 +08:00
|
|
|
struct dahdi_dynamic_local *d, *l;
|
2010-08-28 05:59:27 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int key = -1, id = -1, monitor = -1;
|
|
|
|
|
|
|
|
if (strlen(address) >= 3) {
|
|
|
|
if (address[1] != ':')
|
|
|
|
goto INVALID_ADDRESS;
|
|
|
|
key = digit2int(address[0]);
|
|
|
|
id = digit2int(address[2]);
|
|
|
|
}
|
|
|
|
if (strlen (address) == 5) {
|
|
|
|
if (address[3] != ':')
|
|
|
|
goto INVALID_ADDRESS;
|
|
|
|
monitor = digit2int(address[4]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key == -1 || id == -1)
|
|
|
|
goto INVALID_ADDRESS;
|
|
|
|
|
2011-01-04 02:24:48 +08:00
|
|
|
d = kzalloc(sizeof(*d), GFP_KERNEL);
|
2011-01-04 02:24:56 +08:00
|
|
|
if (!d)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
d->key = key;
|
|
|
|
d->id = id;
|
|
|
|
d->span = span;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&local_lock, flags);
|
|
|
|
/* Add this peer to any existing spans with same key
|
|
|
|
And add them as peers to this one */
|
|
|
|
list_for_each_entry(l, &dynamic_local_list, node) {
|
|
|
|
if (l->key != d->key)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (l->id == d->id) {
|
|
|
|
printk(KERN_DEBUG "TDMoL: Duplicate id (%d) for key "
|
|
|
|
"%d\n", d->id, d->key);
|
|
|
|
goto CLEAR_AND_DEL_FROM_PEERS;
|
|
|
|
}
|
|
|
|
if (monitor == -1) {
|
|
|
|
if (l->peer) {
|
|
|
|
printk(KERN_DEBUG "TDMoL: Span with key %d and "
|
|
|
|
"id %d already has a R/W peer\n",
|
|
|
|
d->key, d->id);
|
2011-01-04 02:24:52 +08:00
|
|
|
goto CLEAR_AND_DEL_FROM_PEERS;
|
2011-01-04 02:24:56 +08:00
|
|
|
} else {
|
|
|
|
l->peer = d;
|
|
|
|
d->peer = l;
|
2011-01-04 02:24:52 +08:00
|
|
|
}
|
2011-01-04 02:24:56 +08:00
|
|
|
}
|
|
|
|
if (monitor == l->id) {
|
|
|
|
if (l->monitor_rx_peer) {
|
|
|
|
printk(KERN_DEBUG "TDMoL: Span with key %d and "
|
|
|
|
"id %d already has a monitoring peer\n",
|
|
|
|
d->key, d->id);
|
|
|
|
goto CLEAR_AND_DEL_FROM_PEERS;
|
|
|
|
} else {
|
|
|
|
l->monitor_rx_peer = d;
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
2011-01-04 02:24:52 +08:00
|
|
|
}
|
2011-01-04 02:24:56 +08:00
|
|
|
}
|
|
|
|
list_add(&d->node, &dynamic_local_list);
|
|
|
|
spin_unlock_irqrestore(&local_lock, flags);
|
2011-01-04 02:24:52 +08:00
|
|
|
|
2011-01-04 02:24:56 +08:00
|
|
|
if (!try_module_get(THIS_MODULE))
|
|
|
|
printk(KERN_DEBUG "TDMoL: Unable to increment module use count\n");
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2011-01-04 02:24:56 +08:00
|
|
|
printk(KERN_INFO "TDMoL: Added new interface for %s, "
|
|
|
|
"key %d id %d\n", span->name, d->key, d->id);
|
2011-01-04 02:24:43 +08:00
|
|
|
return d;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
CLEAR_AND_DEL_FROM_PEERS:
|
2011-01-04 02:24:52 +08:00
|
|
|
list_for_each_entry(l, &dynamic_local_list, node) {
|
2011-01-04 02:24:43 +08:00
|
|
|
if (l->peer == d)
|
2010-08-28 05:59:27 +08:00
|
|
|
l->peer = NULL;
|
2011-01-04 02:24:43 +08:00
|
|
|
if (l->monitor_rx_peer == d)
|
2010-08-28 05:59:27 +08:00
|
|
|
l->monitor_rx_peer = NULL;
|
|
|
|
}
|
2011-01-04 02:24:43 +08:00
|
|
|
kfree(d);
|
|
|
|
spin_unlock_irqrestore(&local_lock, flags);
|
2010-08-28 05:59:27 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
INVALID_ADDRESS:
|
|
|
|
printk (KERN_NOTICE "TDMoL: Invalid address %s\n", address);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
static struct dahdi_dynamic_driver dahdi_dynamic_local = {
|
2010-08-28 05:59:27 +08:00
|
|
|
"loc",
|
|
|
|
"Local",
|
2011-01-04 02:24:43 +08:00
|
|
|
dahdi_dynamic_local_create,
|
|
|
|
dahdi_dynamic_local_destroy,
|
|
|
|
dahdi_dynamic_local_transmit,
|
2010-08-28 05:59:27 +08:00
|
|
|
NULL /* flush */
|
|
|
|
};
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
static int __init dahdi_dynamic_local_init(void)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2011-01-04 02:24:43 +08:00
|
|
|
dahdi_dynamic_register(&dahdi_dynamic_local);
|
2010-08-28 05:59:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
static void __exit dahdi_dynamic_local_exit(void)
|
2010-08-28 05:59:27 +08:00
|
|
|
{
|
2011-01-04 02:24:43 +08:00
|
|
|
dahdi_dynamic_unregister(&dahdi_dynamic_local);
|
2010-08-28 05:59:27 +08:00
|
|
|
}
|
|
|
|
|
2011-01-04 02:24:43 +08:00
|
|
|
module_init(dahdi_dynamic_local_init);
|
|
|
|
module_exit(dahdi_dynamic_local_exit);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL v2");
|