1149 lines
28 KiB
Diff
1149 lines
28 KiB
Diff
Index: linux-2.6.39/drivers/isdn/hardware/mISDN/Kconfig
|
|
===================================================================
|
|
--- linux-2.6.39.orig/drivers/isdn/hardware/mISDN/Kconfig 2011-10-13 01:29:11.635639545 +0800
|
|
+++ linux-2.6.39/drivers/isdn/hardware/mISDN/Kconfig 2011-10-13 01:29:29.579638931 +0800
|
|
@@ -91,3 +91,8 @@
|
|
tristate
|
|
depends on MISDN
|
|
|
|
+config MISDN_L1LOOP
|
|
+ tristate "Support for virtual ISDN loop devices"
|
|
+ help
|
|
+ Enable support for mISDN_l1loop to use virtual ISDN devices connected
|
|
+ to virtual bus system
|
|
Index: linux-2.6.39/drivers/isdn/hardware/mISDN/Makefile
|
|
===================================================================
|
|
--- linux-2.6.39.orig/drivers/isdn/hardware/mISDN/Makefile 2011-10-13 01:29:11.587639549 +0800
|
|
+++ linux-2.6.39/drivers/isdn/hardware/mISDN/Makefile 2011-10-13 01:29:29.579638931 +0800
|
|
@@ -11,6 +11,10 @@
|
|
obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o
|
|
obj-$(CONFIG_MISDN_W6692) += w6692.o
|
|
obj-$(CONFIG_MISDN_NETJET) += netjet.o
|
|
+obj-$(CONFIG_MISDN_L1LOOP) += mISDN_l1loop.o
|
|
# chip modules
|
|
obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
|
|
obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o
|
|
+
|
|
+# rename
|
|
+mISDN_l1loop-objs := l1loop.o
|
|
Index: linux-2.6.39/drivers/isdn/hardware/mISDN/l1loop.c
|
|
===================================================================
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ linux-2.6.39/drivers/isdn/hardware/mISDN/l1loop.c 2011-10-13 01:29:45.607638544 +0800
|
|
@@ -0,0 +1,1046 @@
|
|
+/* l1loop.c
|
|
+ * virtual mISDN layer1 driver
|
|
+ *
|
|
+ * Copyright 2008 by Martin Bachem (info@bachem-it.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ *
|
|
+ *
|
|
+ * module params:
|
|
+ *
|
|
+ * - interfaces=<n>, n=[1..64] default 2
|
|
+ * - vline=<n>, n=[0,1,2] default 1
|
|
+ * 0 : NONE (no virtual line at all, all interfaces behave like
|
|
+ * ISDN TAs without any bus connection)
|
|
+ * 1 : VBUS (all interfaces are connected to one virtual
|
|
+ * ISDN bus. the first interface opened as NT interface
|
|
+ * will configure all others as TE interface)
|
|
+ * 2 : VLOOP (each interface is virtually equipped with a cross
|
|
+ * connector, so all data is looped internally)
|
|
+ * 3 : VLINK (an even number of interfaces must be given.
|
|
+ * every pair of these interfaces is interlinked)
|
|
+ * - nchannel=<n>, n=[2..126] default 2
|
|
+ * number of bchannels each interface will consist of
|
|
+ * if vline==VLINK, multiple nchannel values may be given to
|
|
+ * define the number of channels for each pair of interfaces.
|
|
+ * - pri=<n>, n=[0,1] default 0
|
|
+ * 0: register all interfaces as BRI
|
|
+ * 1: register all interfaces as PRI
|
|
+ * - debug=<n>, default=0, with n=0xHHHHGGGG
|
|
+ * H - l1 driver flags described in hfcs_usb.h
|
|
+ * G - common mISDN debug flags described at mISDNhw.h
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mISDNhw.h>
|
|
+#include "l1loop.h"
|
|
+
|
|
+const char *l1loop_rev = "Revision: 0.1.5 (socket), 2008-11-04";
|
|
+
|
|
+
|
|
+static int l1loop_cnt;
|
|
+static LIST_HEAD(l1loop_list);
|
|
+static DEFINE_RWLOCK(l1loop_lock);
|
|
+struct l1loop *hw;
|
|
+struct port *vbusnt; /* NT of virtual S0/E1 bus when using vline=1 */
|
|
+
|
|
+/* module params */
|
|
+static unsigned int interfaces = 2;
|
|
+static unsigned int vline = 1;
|
|
+static unsigned int nchannel[32] = {2};
|
|
+static unsigned int pri;
|
|
+static unsigned int debug;
|
|
+
|
|
+MODULE_AUTHOR("Martin Bachem");
|
|
+MODULE_LICENSE("GPL");
|
|
+module_param(interfaces, uint, S_IRUGO | S_IWUSR);
|
|
+module_param(vline, uint, S_IRUGO | S_IWUSR);
|
|
+module_param_array(nchannel, uint, NULL, S_IRUGO | S_IWUSR);
|
|
+module_param(pri, uint, S_IRUGO | S_IWUSR);
|
|
+module_param(debug, uint, S_IRUGO | S_IWUSR);
|
|
+
|
|
+/*
|
|
+ * send full D/B channel status information
|
|
+ * as MPH_INFORMATION_IND
|
|
+ */
|
|
+static void l1loop_ph_info(struct port *p)
|
|
+{
|
|
+ struct ph_info *phi;
|
|
+ struct dchannel *dch = &p->dch;
|
|
+ int i;
|
|
+
|
|
+ phi = kzalloc(sizeof(struct ph_info) +
|
|
+ dch->dev.nrbchan * sizeof(struct ph_info_ch),
|
|
+ GFP_ATOMIC);
|
|
+
|
|
+ phi->dch.ch.protocol = p->protocol;
|
|
+ phi->dch.ch.Flags = dch->Flags;
|
|
+ phi->dch.state = dch->state;
|
|
+ phi->dch.num_bch = dch->dev.nrbchan;
|
|
+ for (i = 0; i < dch->dev.nrbchan; i++) {
|
|
+ phi->bch[i].protocol = p->bch[i].ch.protocol;
|
|
+ phi->bch[i].Flags = p->bch[i].Flags;
|
|
+ }
|
|
+ _queue_data(&dch->dev.D,
|
|
+ MPH_INFORMATION_IND, MISDN_ID_ANY,
|
|
+ sizeof(struct ph_info_dch) +
|
|
+ dch->dev.nrbchan * sizeof(struct ph_info_ch),
|
|
+ phi, GFP_ATOMIC);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * disable/enable BChannel for desired protocoll
|
|
+ */
|
|
+static int
|
|
+l1loop_setup_bch(struct bchannel *bch, int protocol)
|
|
+{
|
|
+ struct port *p = bch->hw;
|
|
+
|
|
+ if (debug)
|
|
+ printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n",
|
|
+ p->name, __func__, bch->state, protocol,
|
|
+ bch->nr);
|
|
+
|
|
+ switch (protocol) {
|
|
+ case (-1): /* used for init */
|
|
+ bch->state = -1;
|
|
+ /* fall trough */
|
|
+ case (ISDN_P_NONE):
|
|
+ if (bch->state == ISDN_P_NONE)
|
|
+ return 0; /* already in idle state */
|
|
+ bch->state = ISDN_P_NONE;
|
|
+ clear_bit(FLG_HDLC, &bch->Flags);
|
|
+ clear_bit(FLG_TRANSPARENT, &bch->Flags);
|
|
+ break;
|
|
+ case (ISDN_P_B_RAW):
|
|
+ bch->state = protocol;
|
|
+ set_bit(FLG_TRANSPARENT, &bch->Flags);
|
|
+ break;
|
|
+ case (ISDN_P_B_HDLC):
|
|
+ bch->state = protocol;
|
|
+ set_bit(FLG_HDLC, &bch->Flags);
|
|
+ break;
|
|
+ default:
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s: prot not known %x\n",
|
|
+ p->name, __func__, protocol);
|
|
+ return -ENOPROTOOPT;
|
|
+ }
|
|
+ l1loop_ph_info(p);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+deactivate_bchannel(struct bchannel *bch)
|
|
+{
|
|
+ struct port *p = bch->hw;
|
|
+
|
|
+ if (bch->debug)
|
|
+ printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n",
|
|
+ p->name, __func__, bch->nr);
|
|
+
|
|
+ spin_lock(&p->lock);
|
|
+ if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
|
|
+ dev_kfree_skb(bch->next_skb);
|
|
+ bch->next_skb = NULL;
|
|
+ }
|
|
+ if (bch->tx_skb) {
|
|
+ dev_kfree_skb(bch->tx_skb);
|
|
+ bch->tx_skb = NULL;
|
|
+ }
|
|
+ bch->tx_idx = 0;
|
|
+ if (bch->rx_skb) {
|
|
+ dev_kfree_skb(bch->rx_skb);
|
|
+ bch->rx_skb = NULL;
|
|
+ }
|
|
+ clear_bit(FLG_ACTIVE, &bch->Flags);
|
|
+ clear_bit(FLG_TX_BUSY, &bch->Flags);
|
|
+ spin_unlock(&p->lock);
|
|
+
|
|
+ l1loop_setup_bch(bch, ISDN_P_NONE);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bch layer1 loop (vline=2): immediatly loop back every B-channel data
|
|
+ */
|
|
+static void bch_vline_loop(struct bchannel *bch, struct sk_buff *skb)
|
|
+{
|
|
+ struct port *p = bch->hw;
|
|
+
|
|
+ bch->rx_skb = skb_copy(skb, GFP_KERNEL);
|
|
+ if (bch->rx_skb)
|
|
+ recv_Bchannel(bch, MISDN_ID_ANY);
|
|
+ else
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_ERR "%s: %s: mI_alloc_skb failed \n",
|
|
+ p->name, __func__);
|
|
+ dev_kfree_skb(skb);
|
|
+ get_next_bframe(bch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bch layer1 bus (vline=1): copy frame to each party with bch(x) FLAG_ACTIVE
|
|
+ */
|
|
+static void bch_vbus(struct bchannel *bch, struct sk_buff *skb)
|
|
+{
|
|
+ struct port *me = bch->hw;
|
|
+ struct port *party;
|
|
+ struct bchannel *target = NULL;
|
|
+ int i, b;
|
|
+
|
|
+ b = bch->nr - 1 - (bch->nr > 16);
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ party = hw->ports + i;
|
|
+ target = &party->bch[b];
|
|
+ if ((me != party) && test_bit(FLG_ACTIVE, &target->Flags)) {
|
|
+ /* immediately queue data to bch's RX queue */
|
|
+ target->rx_skb = skb_copy(skb, GFP_KERNEL);
|
|
+ if (target->rx_skb)
|
|
+ recv_Bchannel(target, MISDN_ID_ANY);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev_kfree_skb(skb);
|
|
+ get_next_bframe(bch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bch layer1 link (vline=3): copy frame to ohter party, if bch(x) FLAG_ACTIVE
|
|
+ */
|
|
+static void bch_vlink(struct bchannel *bch, struct sk_buff *skb)
|
|
+{
|
|
+ struct port *me = bch->hw;
|
|
+ struct port *party = &hw->ports[me->instance ^ 1];
|
|
+ struct bchannel *target = NULL;
|
|
+ int b;
|
|
+
|
|
+ b = bch->nr - 1 - (bch->nr > 16);
|
|
+ target = &party->bch[b];
|
|
+ if (test_bit(FLG_ACTIVE, &target->Flags)) {
|
|
+ /* immediately queue data to bch's RX queue */
|
|
+ target->rx_skb = skb_copy(skb, GFP_KERNEL);
|
|
+ if (target->rx_skb)
|
|
+ recv_Bchannel(target, MISDN_ID_ANY);
|
|
+ }
|
|
+
|
|
+ dev_kfree_skb(skb);
|
|
+ get_next_bframe(bch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * layer2 -> layer1 callback B-channel
|
|
+ */
|
|
+static int
|
|
+l1loop_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) {
|
|
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
|
|
+ struct port *p = bch->hw;
|
|
+ int ret = -EINVAL;
|
|
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
|
|
+
|
|
+ switch (hh->prim) {
|
|
+ case PH_DATA_REQ:
|
|
+ spin_lock(&p->lock);
|
|
+ ret = bchannel_senddata(bch, skb);
|
|
+ spin_unlock(&p->lock);
|
|
+ if (ret > 0) {
|
|
+ ret = 0;
|
|
+ queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL);
|
|
+ switch (vline) {
|
|
+ case VLINE_BUS:
|
|
+ bch_vbus(bch, skb);
|
|
+ break;
|
|
+ case VLINE_LOOP:
|
|
+ bch_vline_loop(bch, skb);
|
|
+ break;
|
|
+ case VLINE_LINK:
|
|
+ bch_vlink(bch, skb);
|
|
+ break;
|
|
+ case VLINE_NONE:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+ case PH_ACTIVATE_REQ:
|
|
+ if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
|
|
+ ret = l1loop_setup_bch(bch, ch->protocol);
|
|
+ else
|
|
+ ret = 0;
|
|
+ if (!ret)
|
|
+ _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
|
|
+ 0, NULL, GFP_KERNEL);
|
|
+ break;
|
|
+ case PH_DEACTIVATE_REQ:
|
|
+ deactivate_bchannel(bch);
|
|
+ _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY,
|
|
+ 0, NULL, GFP_KERNEL);
|
|
+ ret = 0;
|
|
+ break;
|
|
+ }
|
|
+ if (!ret)
|
|
+ dev_kfree_skb(skb);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 'physical' S0/E1 bus state change handler
|
|
+ */
|
|
+static void
|
|
+ph_state(struct dchannel *dch)
|
|
+{
|
|
+ struct port *p = dch->hw;
|
|
+ char *ptext;
|
|
+
|
|
+ if (p->protocol <= ISDN_P_MAX)
|
|
+ ptext = ISDN_P_TEXT[p->protocol];
|
|
+ else
|
|
+ ptext = ISDN_P_TEXT[ISDN_P_MAX+1];
|
|
+
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s: %s %s (%i)\n",
|
|
+ p->name, __func__, ptext,
|
|
+ (dch->state) ? "ACTIVE" : "INACTIVE",
|
|
+ test_bit(FLG_ACTIVE, &dch->Flags));
|
|
+
|
|
+ switch (dch->state) {
|
|
+ case VBUS_INACTIVE:
|
|
+ clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
|
|
+ clear_bit(FLG_ACTIVE, &dch->Flags);
|
|
+ _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
|
|
+ MISDN_ID_ANY, 0, NULL, GFP_KERNEL);
|
|
+ break;
|
|
+ case VBUS_ACTIVE:
|
|
+ if (!(test_and_set_bit(FLG_ACTIVE, &dch->Flags)))
|
|
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
|
|
+ MISDN_ID_ANY, 0, NULL, GFP_KERNEL);
|
|
+ else
|
|
+ _queue_data(&dch->dev.D, PH_ACTIVATE_CNF,
|
|
+ MISDN_ID_ANY, 0, NULL, GFP_KERNEL);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ l1loop_ph_info(p);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * every activation (NT and TE) causes every listening party to activate
|
|
+ */
|
|
+static void
|
|
+vbus_activate(struct port *p)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (vbusnt) {
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ p = hw->ports + i;
|
|
+ p->dch.state = VBUS_ACTIVE;
|
|
+ if (test_bit(FLG_OPEN, &p->dch.Flags))
|
|
+ ph_state(&p->dch);
|
|
+ }
|
|
+ } else {
|
|
+ /* no interfaces opened as NT yet */
|
|
+ p->dch.state = VBUS_INACTIVE;
|
|
+ ph_state(&p->dch);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * set every party member to state VBUS_ACTIVE
|
|
+ */
|
|
+static void
|
|
+vbus_deactivate(struct port *p)
|
|
+{
|
|
+ struct dchannel *dch;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ p = hw->ports + i;
|
|
+ dch = &p->dch;
|
|
+ dch->state = VBUS_INACTIVE;
|
|
+ if (test_bit(FLG_OPEN, &p->dch.Flags))
|
|
+ ph_state(dch);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * every activation (NT and TE) causes the ohter listening party to activate
|
|
+ */
|
|
+static void
|
|
+vlink_activate(struct port *p)
|
|
+{
|
|
+ struct port *party = &hw->ports[p->instance ^ 1];
|
|
+
|
|
+ if (test_bit(FLG_OPEN, &party->dch.Flags)) {
|
|
+ party->dch.state = VBUS_ACTIVE;
|
|
+ ph_state(&party->dch);
|
|
+ } else {
|
|
+ p->dch.state = VBUS_INACTIVE;
|
|
+ ph_state(&p->dch);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * set every party member to state VLINK_ACTIVE
|
|
+ */
|
|
+static void
|
|
+vlink_deactivate(struct port *p)
|
|
+{
|
|
+ struct port *party = &hw->ports[p->instance ^ 1];
|
|
+
|
|
+ p->dch.state = VBUS_INACTIVE;
|
|
+ ph_state(&p->dch);
|
|
+ if (test_bit(FLG_OPEN, &party->dch.Flags)) {
|
|
+ party->dch.state = VBUS_INACTIVE;
|
|
+ ph_state(&party->dch);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * apply 'physical' bus commands:
|
|
+ * L1_ACTIVATE_TE, L1_ACTIVATE_NT, L1_DEACTIVATE_NT
|
|
+ */
|
|
+static void
|
|
+ph_command(struct port *p, u_char command)
|
|
+{
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s: %x\n",
|
|
+ p->name, __func__, command);
|
|
+ switch (command) {
|
|
+ case L1_ACTIVATE_TE:
|
|
+ switch (vline) {
|
|
+ case VLINE_BUS:
|
|
+ vbus_activate(p);
|
|
+ break;
|
|
+ case VLINE_LOOP:
|
|
+ p->dch.state = VBUS_ACTIVE;
|
|
+ schedule_event(&p->dch, FLG_PHCHANGE);
|
|
+ break;
|
|
+ case VLINE_LINK:
|
|
+ vlink_activate(p);
|
|
+ break;
|
|
+ case VLINE_NONE:
|
|
+ default:
|
|
+ p->dch.state = VBUS_INACTIVE;
|
|
+ schedule_event(&p->dch, FLG_PHCHANGE);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case L1_ACTIVATE_NT:
|
|
+ switch (vline) {
|
|
+ case VLINE_BUS:
|
|
+ vbus_activate(p);
|
|
+ break;
|
|
+ case VLINE_LOOP:
|
|
+ p->dch.state = VBUS_ACTIVE;
|
|
+ schedule_event(&p->dch, FLG_PHCHANGE);
|
|
+ break;
|
|
+ case VLINE_LINK:
|
|
+ vlink_activate(p);
|
|
+ break;
|
|
+ case VLINE_NONE:
|
|
+ default:
|
|
+ p->dch.state = VBUS_INACTIVE;
|
|
+ schedule_event(&p->dch, FLG_PHCHANGE);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case L1_DEACTIVATE_NT:
|
|
+ switch (vline) {
|
|
+ case VLINE_BUS:
|
|
+ vbus_deactivate(p);
|
|
+ break;
|
|
+ case VLINE_LOOP:
|
|
+ p->dch.state = VBUS_INACTIVE;
|
|
+ schedule_event(&p->dch, FLG_PHCHANGE);
|
|
+ case VLINE_LINK:
|
|
+ vlink_deactivate(p);
|
|
+ break;
|
|
+ case VLINE_NONE:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * dch layer1 loop (vline=2): immediatly loop back every D-channel data
|
|
+ */
|
|
+static void dch_vline_loop(struct dchannel *dch, struct sk_buff *skb)
|
|
+{
|
|
+ struct port *p = dch->hw;
|
|
+
|
|
+ dch->rx_skb = skb_copy(skb, GFP_KERNEL);
|
|
+ if (dch->rx_skb)
|
|
+ recv_Dchannel(dch);
|
|
+ else
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_ERR "%s: %s: mI_alloc_skb failed \n",
|
|
+ p->name, __func__);
|
|
+ dev_kfree_skb(skb);
|
|
+ get_next_dframe(dch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * dch layer1 link (vline=3): connect each pair of D-channel data
|
|
+ */
|
|
+static void dch_vline_link(struct dchannel *dch, struct sk_buff *skb)
|
|
+{
|
|
+ struct port *me = dch->hw;
|
|
+ struct port *party = &hw->ports[me->instance ^ 1];
|
|
+ struct dchannel *party_dch = &party->dch;
|
|
+
|
|
+ if (test_bit(FLG_ACTIVE, &party_dch->Flags)) {
|
|
+ party_dch->rx_skb = skb_copy(skb, GFP_KERNEL);
|
|
+ if (party_dch->rx_skb)
|
|
+ recv_Dchannel(party_dch);
|
|
+ }
|
|
+ dev_kfree_skb(skb);
|
|
+ get_next_dframe(dch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * dch layer1 bus (vline=1) on S0 bus
|
|
+ */
|
|
+static void dch_vbus_S0(struct dchannel *dch, struct sk_buff *skb)
|
|
+{
|
|
+ struct port *me = dch->hw;
|
|
+ struct port *party;
|
|
+ struct dchannel *party_dch;
|
|
+ int i;
|
|
+
|
|
+ if (vbusnt) {
|
|
+ if (me != vbusnt) {
|
|
+ /* TE -> NT */
|
|
+ vbusnt->dch.rx_skb = skb_copy(skb, GFP_KERNEL);
|
|
+ if (vbusnt->dch.rx_skb)
|
|
+ recv_Dchannel(&vbusnt->dch);
|
|
+ /* virtual E-channel ECHO */
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ party = hw->ports + i;
|
|
+ if ((party != vbusnt) && test_bit(FLG_ACTIVE,
|
|
+ &party->dch.Flags)) {
|
|
+ party_dch = &party->dch;
|
|
+ party_dch->rx_skb = skb_copy(skb,
|
|
+ GFP_KERNEL);
|
|
+ if (party_dch->rx_skb)
|
|
+ recv_Echannel(party_dch,
|
|
+ party_dch);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ /* NT -> all TEs */
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ party = hw->ports + i;
|
|
+ if ((me != party) && test_bit(FLG_ACTIVE,
|
|
+ &party->dch.Flags)) {
|
|
+ party_dch = &party->dch;
|
|
+ party_dch->rx_skb = skb_copy(skb,
|
|
+ GFP_KERNEL);
|
|
+ if (party_dch->rx_skb)
|
|
+ recv_Dchannel(party_dch);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ dev_kfree_skb(skb);
|
|
+ get_next_dframe(dch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * dch layer1 bus (vline=1) on E1 bus
|
|
+ */
|
|
+static void dch_vbus_E1(struct dchannel *dch, struct sk_buff *skb)
|
|
+{
|
|
+ struct port *me = dch->hw;
|
|
+ struct port *party;
|
|
+ struct dchannel *party_dch;
|
|
+ int i;
|
|
+
|
|
+ /* NT->TE / TE->NT */
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ party = hw->ports + i;
|
|
+ if ((me != party) && test_bit(FLG_ACTIVE, &party->dch.Flags)) {
|
|
+ party_dch = &party->dch;
|
|
+ party_dch->rx_skb = skb_copy(skb, GFP_KERNEL);
|
|
+ if (party_dch->rx_skb)
|
|
+ recv_Dchannel(party_dch);
|
|
+ }
|
|
+ }
|
|
+ dev_kfree_skb(skb);
|
|
+ get_next_dframe(dch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * layer2 -> layer1 callback D-channel
|
|
+ */
|
|
+static int
|
|
+l1loop_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) {
|
|
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
|
|
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
|
|
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
|
|
+ struct port *p = dch->hw;
|
|
+ int ret = -EINVAL;
|
|
+ char *ptext;
|
|
+
|
|
+ if (p->protocol <= ISDN_P_MAX)
|
|
+ ptext = ISDN_P_TEXT[p->protocol];
|
|
+ else
|
|
+ ptext = ISDN_P_TEXT[ISDN_P_MAX+1];
|
|
+
|
|
+ switch (hh->prim) {
|
|
+ case PH_DATA_REQ:
|
|
+ spin_lock(&p->lock);
|
|
+ ret = dchannel_senddata(dch, skb);
|
|
+ spin_unlock(&p->lock);
|
|
+ if (ret > 0) {
|
|
+ ret = 0;
|
|
+ queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL);
|
|
+ switch (vline) {
|
|
+ case VLINE_BUS:
|
|
+ if (IS_ISDN_P_S0(p->protocol))
|
|
+ dch_vbus_S0(dch, skb);
|
|
+ else
|
|
+ dch_vbus_E1(dch, skb);
|
|
+ break;
|
|
+ case VLINE_LOOP:
|
|
+ dch_vline_loop(dch, skb);
|
|
+ break;
|
|
+ case VLINE_LINK:
|
|
+ dch_vline_link(dch, skb);
|
|
+ case VLINE_NONE:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+ case PH_ACTIVATE_REQ:
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n",
|
|
+ p->name, __func__, ptext);
|
|
+ ret = 0;
|
|
+ if (IS_ISDN_P_NT(p->protocol)) {
|
|
+ if (test_bit(FLG_ACTIVE, &dch->Flags))
|
|
+ _queue_data(&dch->dev.D, PH_ACTIVATE_CNF,
|
|
+ MISDN_ID_ANY, 0, NULL, GFP_KERNEL);
|
|
+ else {
|
|
+ ph_command(p, L1_ACTIVATE_NT);
|
|
+ test_and_set_bit(FLG_L2_ACTIVATED,
|
|
+ &dch->Flags);
|
|
+ }
|
|
+ } else {
|
|
+ if (test_bit(FLG_ACTIVE, &dch->Flags))
|
|
+ _queue_data(&dch->dev.D, PH_ACTIVATE_CNF,
|
|
+ MISDN_ID_ANY, 0, NULL, GFP_KERNEL);
|
|
+ else
|
|
+ ph_command(p, L1_ACTIVATE_TE);
|
|
+ }
|
|
+ break;
|
|
+ case PH_DEACTIVATE_REQ:
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ %s\n",
|
|
+ p->name, __func__, ptext);
|
|
+ test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
|
|
+
|
|
+ if (IS_ISDN_P_NT(p->protocol))
|
|
+ ph_command(p, L1_DEACTIVATE_NT);
|
|
+
|
|
+ spin_lock(&p->lock);
|
|
+ skb_queue_purge(&dch->squeue);
|
|
+ if (dch->tx_skb) {
|
|
+ dev_kfree_skb(dch->tx_skb);
|
|
+ dch->tx_skb = NULL;
|
|
+ }
|
|
+ dch->tx_idx = 0;
|
|
+ if (dch->rx_skb) {
|
|
+ dev_kfree_skb(dch->rx_skb);
|
|
+ dch->rx_skb = NULL;
|
|
+ }
|
|
+ spin_unlock(&p->lock);
|
|
+ ret = 0;
|
|
+ break;
|
|
+ case MPH_INFORMATION_REQ:
|
|
+ l1loop_ph_info(p);
|
|
+ ret = 0;
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * layer 1 B-channel 'hardware' access
|
|
+ */
|
|
+static int
|
|
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ switch (cq->op) {
|
|
+ case MISDN_CTRL_GETOP:
|
|
+ cq->op = MISDN_CTRL_FILL_EMPTY;
|
|
+ break;
|
|
+ case MISDN_CTRL_FILL_EMPTY:
|
|
+ test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
|
|
+ if (debug & DEBUG_HW_OPEN)
|
|
+ printk(KERN_DEBUG
|
|
+ "%s: FILL_EMPTY request (nr=%d off=%d)\n",
|
|
+ __func__, bch->nr, !!cq->p1);
|
|
+ break;
|
|
+ default:
|
|
+ printk(KERN_WARNING "%s: unknown Op %x\n",
|
|
+ __func__, cq->op);
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * layer1 B-channel 'hardware' access
|
|
+ */
|
|
+static int
|
|
+l1loop_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
|
|
+{
|
|
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
|
|
+ int ret = -EINVAL;
|
|
+
|
|
+ if (bch->debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg);
|
|
+
|
|
+ switch (cmd) {
|
|
+ case CLOSE_CHANNEL:
|
|
+ test_and_clear_bit(FLG_OPEN, &bch->Flags);
|
|
+ if (test_bit(FLG_ACTIVE, &bch->Flags))
|
|
+ deactivate_bchannel(bch);
|
|
+ ch->protocol = ISDN_P_NONE;
|
|
+ ch->peer = NULL;
|
|
+ module_put(THIS_MODULE);
|
|
+ ret = 0;
|
|
+ break;
|
|
+ case CONTROL_CHANNEL:
|
|
+ ret = channel_bctrl(bch, arg);
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ printk(KERN_WARNING "%s: unknown prim(%x)\n",
|
|
+ __func__, cmd);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * open interface due to user process:
|
|
+ * intially register NT interface when using vline=VLINE_BUS (default)
|
|
+ */
|
|
+static int
|
|
+open_dchannel(struct port *p, struct mISDNchannel *ch, struct channel_req *rq)
|
|
+{
|
|
+ if (debug & DEBUG_HW_OPEN)
|
|
+ printk(KERN_DEBUG "%s: %s: dev(%d) open from %p\n",
|
|
+ p->name, __func__, p->dch.dev.id,
|
|
+ __builtin_return_address(0));
|
|
+ if (rq->protocol == ISDN_P_NONE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!p->initdone) {
|
|
+ if ((vline == VLINE_BUS) && (IS_ISDN_P_NT(rq->protocol))
|
|
+ && vbusnt)
|
|
+ return -EPROTONOSUPPORT;
|
|
+
|
|
+ /* set VBUS NT interface */
|
|
+ if ((vline == VLINE_BUS) && (IS_ISDN_P_NT(rq->protocol)))
|
|
+ vbusnt = p;
|
|
+
|
|
+ p->initdone = 1;
|
|
+ p->protocol = rq->protocol;
|
|
+ ch->protocol = rq->protocol;
|
|
+
|
|
+ } else {
|
|
+ if (rq->protocol != ch->protocol)
|
|
+ return -EPROTONOSUPPORT;
|
|
+ }
|
|
+
|
|
+ set_bit(FLG_OPEN, &p->dch.Flags);
|
|
+ if (vbusnt) {
|
|
+ if (p != vbusnt)
|
|
+ p->dch.state = vbusnt->dch.state;
|
|
+ } else {
|
|
+ if (debug & DEBUG_HW_OPEN)
|
|
+ printk(KERN_DEBUG "%s: %s: dev(%d) no NT found\n",
|
|
+ p->name, __func__, p->dch.dev.id);
|
|
+ p->dch.state = VBUS_INACTIVE;
|
|
+ }
|
|
+ ph_state(&p->dch);
|
|
+
|
|
+ rq->ch = ch;
|
|
+ if (!try_module_get(THIS_MODULE))
|
|
+ printk(KERN_WARNING "%s: %s: cannot get module\n",
|
|
+ p->name, __func__);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+open_bchannel(struct port *p, struct dchannel *dch, struct channel_req *rq)
|
|
+{
|
|
+ struct bchannel *bch;
|
|
+
|
|
+ if (!test_channelmap(rq->adr.channel, dch->dev.channelmap))
|
|
+ return -EINVAL;
|
|
+ if (rq->protocol == ISDN_P_NONE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s B%i\n",
|
|
+ p->name, __func__, rq->adr.channel);
|
|
+
|
|
+ bch = &p->bch[rq->adr.channel - 1 - (rq->adr.channel > 16)];
|
|
+ if (test_and_set_bit(FLG_OPEN, &bch->Flags))
|
|
+ return -EBUSY; /* b-channel can be only open once */
|
|
+ test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
|
|
+ bch->ch.protocol = rq->protocol;
|
|
+ rq->ch = &bch->ch;
|
|
+
|
|
+ if (!try_module_get(THIS_MODULE))
|
|
+ printk(KERN_WARNING "%s: %s:cannot get module\n",
|
|
+ p->name, __func__);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+channel_ctrl(struct port *p, struct mISDN_ctrl_req *cq)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (debug)
|
|
+ printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n",
|
|
+ p->name, __func__, (cq->op), (cq->channel));
|
|
+
|
|
+ switch (cq->op) {
|
|
+ case MISDN_CTRL_GETOP:
|
|
+ cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT |
|
|
+ MISDN_CTRL_DISCONNECT;
|
|
+ break;
|
|
+ default:
|
|
+ printk(KERN_WARNING "%s: %s: unknown Op %x\n",
|
|
+ p->name, __func__, cq->op);
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+l1loop_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) {
|
|
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
|
|
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
|
|
+ struct port *p = dch->hw;
|
|
+ struct channel_req *rq;
|
|
+ int err = 0;
|
|
+
|
|
+ if (dch->debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s: cmd:%x %p\n",
|
|
+ p->name, __func__, cmd, arg);
|
|
+ switch (cmd) {
|
|
+ case OPEN_CHANNEL:
|
|
+ rq = arg;
|
|
+ if ((rq->protocol == ISDN_P_TE_S0) ||
|
|
+ (rq->protocol == ISDN_P_NT_S0) ||
|
|
+ (rq->protocol == ISDN_P_TE_E1) ||
|
|
+ (rq->protocol == ISDN_P_NT_E1))
|
|
+ err = open_dchannel(p, ch, rq);
|
|
+ else
|
|
+ err = open_bchannel(p, dch, rq);
|
|
+ break;
|
|
+ case CLOSE_CHANNEL:
|
|
+ if (debug & DEBUG_HW_OPEN)
|
|
+ printk(KERN_DEBUG "%s: %s: dev(%d) close from %p\n",
|
|
+ p->name, __func__, p->dch.dev.id,
|
|
+ __builtin_return_address(0));
|
|
+ module_put(THIS_MODULE);
|
|
+ break;
|
|
+ case CONTROL_CHANNEL:
|
|
+ err = channel_ctrl(p, arg);
|
|
+ break;
|
|
+ default:
|
|
+ if (dch->debug & DEBUG_HW)
|
|
+ printk(KERN_DEBUG "%s: %s: unknown command %x\n",
|
|
+ p->name, __func__, cmd);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int
|
|
+setup_instance(struct l1loop *hw) {
|
|
+ struct port *p;
|
|
+ u_long flags;
|
|
+ int err = 0, i, b, n;
|
|
+
|
|
+ if (debug)
|
|
+ printk(KERN_DEBUG "%s: %s\n", DRIVER_NAME, __func__);
|
|
+
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ p = hw->ports + i;
|
|
+ n = nchannel[0];
|
|
+ if (vline == VLINE_LINK && nchannel[i >> 1])
|
|
+ n = nchannel[i >> 1];
|
|
+ p->bch = kzalloc(sizeof(struct bchannel)*n, GFP_KERNEL);
|
|
+ if (!p->bch) {
|
|
+ printk(KERN_ERR "%s: %s: no kmem for bchannels\n",
|
|
+ DRIVER_NAME, __func__);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ spin_lock_init(&p->lock);
|
|
+ p->instance = i;
|
|
+ mISDN_initdchannel(&p->dch, MAX_DFRAME_LEN_L1, ph_state);
|
|
+ p->dch.debug = debug & 0xFFFF;
|
|
+ p->dch.hw = p;
|
|
+ if (!pri)
|
|
+ p->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) |
|
|
+ (1 << ISDN_P_NT_S0);
|
|
+ else
|
|
+ p->dch.dev.Dprotocols = (1 << ISDN_P_TE_E1) |
|
|
+ (1 << ISDN_P_NT_E1);
|
|
+ p->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
|
|
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
|
|
+ p->dch.dev.D.send = l1loop_l2l1D;
|
|
+ p->dch.dev.D.ctrl = l1loop_dctrl;
|
|
+ p->dch.dev.nrbchan = n;
|
|
+ p->nrbchan = n;
|
|
+ for (b = 0; b < n; b++) {
|
|
+ p->bch[b].nr = b + 1 + (b >= 15);
|
|
+ set_channelmap(p->bch[b].nr, p->dch.dev.channelmap);
|
|
+ p->bch[b].debug = debug;
|
|
+ mISDN_initbchannel(&p->bch[b], MAX_DATA_MEM);
|
|
+ p->bch[b].hw = p;
|
|
+ p->bch[b].ch.send = l1loop_l2l1B;
|
|
+ p->bch[b].ch.ctrl = l1loop_bctrl;
|
|
+ p->bch[b].ch.nr = p->bch[b].nr;
|
|
+ list_add(&p->bch[b].ch.list, &p->dch.dev.bchannels);
|
|
+ }
|
|
+
|
|
+ snprintf(p->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME,
|
|
+ l1loop_cnt + 1);
|
|
+ printk(KERN_INFO "%s: registered as '%s'\n",
|
|
+ DRIVER_NAME, p->name);
|
|
+
|
|
+ /* TODO: parent device? */
|
|
+ err = mISDN_register_device(&p->dch.dev, NULL, p->name);
|
|
+ if (err) {
|
|
+ for (b = 0; b < n; b++)
|
|
+ mISDN_freebchannel(&p->bch[b]);
|
|
+ mISDN_freedchannel(&p->dch);
|
|
+ } else {
|
|
+ l1loop_cnt++;
|
|
+ write_lock_irqsave(&l1loop_lock, flags);
|
|
+ list_add_tail(&hw->list, &l1loop_list);
|
|
+ write_unlock_irqrestore(&l1loop_lock, flags);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int
|
|
+release_instance(struct l1loop *hw) {
|
|
+ struct port *p;
|
|
+ int i, b;
|
|
+
|
|
+ if (debug)
|
|
+ printk(KERN_DEBUG "%s: %s\n", DRIVER_NAME, __func__);
|
|
+
|
|
+ for (i = 0; i < interfaces; i++) {
|
|
+ p = hw->ports + i;
|
|
+ for (b = 0; b < p->nrbchan; b++)
|
|
+ l1loop_setup_bch(&p->bch[b], ISDN_P_NONE);
|
|
+
|
|
+ mISDN_unregister_device(&p->dch.dev);
|
|
+ for (b = 0; b < p->nrbchan; b++)
|
|
+ mISDN_freebchannel(&p->bch[b]);
|
|
+ mISDN_freedchannel(&p->dch);
|
|
+ }
|
|
+
|
|
+ if (hw) {
|
|
+ if (hw->ports) {
|
|
+ kfree(hw->ports->bch);
|
|
+ kfree(hw->ports);
|
|
+ }
|
|
+ kfree(hw);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __init
|
|
+l1loop_init(void)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (vline == 3 && (interfaces & 1)) {
|
|
+ printk(KERN_ERR "%s: %s: an even number of interfaces are "
|
|
+ "expected\n", DRIVER_NAME, __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (interfaces > 64)
|
|
+ interfaces = 64;
|
|
+ if (vline == 3) {
|
|
+ for (i = 0; i < (interfaces >> 1); i++) {
|
|
+ if (nchannel[i] > 126)
|
|
+ nchannel[i] = 126;
|
|
+ }
|
|
+ } else {
|
|
+ if (nchannel[0] > 126)
|
|
+ nchannel[0] = 126;
|
|
+ }
|
|
+ if (pri && (vline == VLINE_BUS) && (interfaces > 2))
|
|
+ interfaces = 2;
|
|
+ if (vline > MAX_VLINE_OPTION)
|
|
+ return -ENODEV;
|
|
+
|
|
+ printk(KERN_INFO DRIVER_NAME " driver Rev. %s "
|
|
+ "debug(0x%x) interfaces(%i) nchannel[0](%i) vline(%s)\n",
|
|
+ l1loop_rev, debug, interfaces, nchannel[0], VLINE_MODES[vline]);
|
|
+
|
|
+ hw = kzalloc(sizeof(struct l1loop), GFP_KERNEL);
|
|
+ if (!hw) {
|
|
+ printk(KERN_ERR "%s: %s: no kmem for hw\n",
|
|
+ DRIVER_NAME, __func__);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ hw->ports = kzalloc(sizeof(struct port)*interfaces, GFP_KERNEL);
|
|
+ if (!hw->ports) {
|
|
+ printk(KERN_ERR "%s: %s: no kmem for interfaces\n",
|
|
+ DRIVER_NAME, __func__);
|
|
+ kfree(hw);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ return setup_instance(hw);
|
|
+}
|
|
+
|
|
+static void __exit
|
|
+l1loop_cleanup(void)
|
|
+{
|
|
+ if (debug)
|
|
+ printk(KERN_DEBUG DRIVER_NAME ": %s\n", __func__);
|
|
+
|
|
+ release_instance(hw);
|
|
+}
|
|
+
|
|
+module_init(l1loop_init);
|
|
+module_exit(l1loop_cleanup);
|
|
Index: linux-2.6.39/drivers/isdn/hardware/mISDN/l1loop.h
|
|
===================================================================
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ linux-2.6.39/drivers/isdn/hardware/mISDN/l1loop.h 2011-10-13 01:29:45.607638544 +0800
|
|
@@ -0,0 +1,64 @@
|
|
+/*
|
|
+ * hwskel.h
|
|
+ */
|
|
+
|
|
+#ifndef __L1LOOP_H__
|
|
+#define __L1LOOP_H__
|
|
+
|
|
+
|
|
+#define DRIVER_NAME "mISDN_l1loop"
|
|
+
|
|
+/* layer1 ph commands */
|
|
+#define L1_ACTIVATE_TE 1
|
|
+#define L1_ACTIVATE_NT 2
|
|
+#define L1_DEACTIVATE_NT 3
|
|
+
|
|
+/* virtual ISDN line modes */
|
|
+#define VLINE_NONE 0
|
|
+#define VLINE_BUS 1
|
|
+#define VLINE_LOOP 2
|
|
+#define VLINE_LINK 3
|
|
+#define MAX_VLINE_OPTION 3
|
|
+
|
|
+const char *VLINE_MODES[] = {
|
|
+ "none",
|
|
+ "bus",
|
|
+ "loop",
|
|
+ "link"
|
|
+};
|
|
+
|
|
+#define ISDN_P_MAX ISDN_P_NT_E1
|
|
+char *ISDN_P_TEXT[] = {
|
|
+ "ISDN_P_NONE",
|
|
+ "ISDN_P_TE_S0",
|
|
+ "ISDN_P_NT_S0",
|
|
+ "ISDN_P_TE_E1",
|
|
+ "ISDN_P_NT_E1",
|
|
+ "<unknown/illegal>"
|
|
+};
|
|
+
|
|
+/* virtual bus states */
|
|
+#define VBUS_ACTIVE 1
|
|
+#define VBUS_INACTIVE 0
|
|
+
|
|
+
|
|
+struct hwskel;
|
|
+
|
|
+struct port {
|
|
+ spinlock_t lock; /* port lock */
|
|
+ int instance;
|
|
+ char name[MISDN_MAX_IDLEN];
|
|
+ struct dchannel dch;
|
|
+ struct bchannel *bch;
|
|
+ int nrbchan;
|
|
+ __u8 protocol;
|
|
+ __u8 initdone;
|
|
+ struct hwskel *hw;
|
|
+};
|
|
+
|
|
+struct l1loop {
|
|
+ struct list_head list;
|
|
+ struct port *ports;
|
|
+};
|
|
+
|
|
+#endif /* __L1LOOP_H__ */
|