summaryrefslogtreecommitdiffstats
path: root/drivers/isdn
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isdn')
-rw-r--r--drivers/isdn/Config.in14
-rw-r--r--drivers/isdn/Makefile70
-rw-r--r--drivers/isdn/icn/Makefile11
-rw-r--r--drivers/isdn/icn/icn.c1675
-rw-r--r--drivers/isdn/icn/icn.h358
-rw-r--r--drivers/isdn/isdn_audio.c622
-rw-r--r--drivers/isdn/isdn_audio.h59
-rw-r--r--drivers/isdn/isdn_cards.c53
-rw-r--r--drivers/isdn/isdn_cards.h28
-rw-r--r--drivers/isdn/isdn_common.c2160
-rw-r--r--drivers/isdn/isdn_common.h63
-rw-r--r--drivers/isdn/isdn_net.c2431
-rw-r--r--drivers/isdn/isdn_net.h52
-rw-r--r--drivers/isdn/isdn_ppp.c1591
-rw-r--r--drivers/isdn/isdn_ppp.h56
-rw-r--r--drivers/isdn/isdn_tty.c2805
-rw-r--r--drivers/isdn/isdn_tty.h51
-rw-r--r--drivers/isdn/pcbit/Makefile15
-rw-r--r--drivers/isdn/pcbit/callbacks.c362
-rw-r--r--drivers/isdn/pcbit/callbacks.h52
-rw-r--r--drivers/isdn/pcbit/capi.c679
-rw-r--r--drivers/isdn/pcbit/capi.h91
-rw-r--r--drivers/isdn/pcbit/drv.c1154
-rw-r--r--drivers/isdn/pcbit/edss1.c334
-rw-r--r--drivers/isdn/pcbit/edss1.h101
-rw-r--r--drivers/isdn/pcbit/layer2.c808
-rw-r--r--drivers/isdn/pcbit/layer2.h287
-rw-r--r--drivers/isdn/pcbit/module.c129
-rw-r--r--drivers/isdn/pcbit/pcbit.h175
-rw-r--r--drivers/isdn/teles/Makefile17
-rw-r--r--drivers/isdn/teles/buffers.c326
-rw-r--r--drivers/isdn/teles/callc.c1481
-rw-r--r--drivers/isdn/teles/card.c1892
-rw-r--r--drivers/isdn/teles/config.c48
-rw-r--r--drivers/isdn/teles/fsm.c159
-rw-r--r--drivers/isdn/teles/isdnl2.c1317
-rw-r--r--drivers/isdn/teles/isdnl3.c603
-rw-r--r--drivers/isdn/teles/l3_1TR6.c507
-rw-r--r--drivers/isdn/teles/l3_1TR6.h160
-rw-r--r--drivers/isdn/teles/llglue.c149
-rw-r--r--drivers/isdn/teles/mod.c143
-rw-r--r--drivers/isdn/teles/proto.h18
-rw-r--r--drivers/isdn/teles/q931.c1149
-rw-r--r--drivers/isdn/teles/tei.c248
-rw-r--r--drivers/isdn/teles/teles.h488
45 files changed, 24991 insertions, 0 deletions
diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in
new file mode 100644
index 000000000..96f7e5a43
--- /dev/null
+++ b/drivers/isdn/Config.in
@@ -0,0 +1,14 @@
+#
+# ISDN device configuration
+#
+if [ "$CONFIG_INET" != "n" ]; then
+ bool 'Support synchronous PPP' CONFIG_ISDN_PPP
+ if [ "$CONFIG_ISDN_PPP" != "n" ]; then
+ bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
+ bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
+ fi
+fi
+bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO
+dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
+dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
+dep_tristate 'Teles/NICCY1016PC/Creatix support' CONFIG_ISDN_DRV_TELES $CONFIG_ISDN
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile
new file mode 100644
index 000000000..3b441835e
--- /dev/null
+++ b/drivers/isdn/Makefile
@@ -0,0 +1,70 @@
+SUB_DIRS :=
+MOD_SUB_DIRS :=
+ALL_SUB_DIRS := icn teles pcbit
+
+L_OBJS :=
+LX_OBJS :=
+M_OBJS :=
+MX_OBJS :=
+O_OBJS :=
+OX_OBJS :=
+L_TARGET :=
+O_TARGET :=
+
+ifeq ($(CONFIG_ISDN),y)
+ L_TARGET := isdn.a
+ L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o
+ LX_OBJS += isdn_common.o
+ ifdef CONFIG_ISDN_PPP
+ L_OBJS += isdn_ppp.o
+ endif
+ ifdef CONFIG_ISDN_AUDIO
+ L_OBJS += isdn_audio.o
+ endif
+else
+ ifeq ($(CONFIG_ISDN),m)
+ M_OBJS += isdn.o
+ O_TARGET += isdn.o
+ O_OBJS += isdn_net.o isdn_tty.o
+ OX_OBJS += isdn_common.o
+ ifdef CONFIG_ISDN_PPP
+ O_OBJS += isdn_ppp.o
+ endif
+ ifdef CONFIG_ISDN_AUDIO
+ O_OBJS += isdn_audio.o
+ endif
+ endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_TELES),y)
+ L_OBJS += teles/teles.o
+ SUB_DIRS += teles
+ MOD_SUB_DIRS += teles
+else
+ ifeq ($(CONFIG_ISDN_DRV_TELES),m)
+ MOD_SUB_DIRS += teles
+ endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_ICN),y)
+ L_OBJS += icn/icn.o
+ SUB_DIRS += icn
+ MOD_SUB_DIRS += icn
+else
+ ifeq ($(CONFIG_ISDN_DRV_ICN),m)
+ MOD_SUB_DIRS += icn
+ endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_PCBIT),y)
+ L_OBJS += pcbit/pcbit.o
+ SUB_DIRS += pcbit
+ MOD_SUB_DIRS += pcbit
+else
+ ifeq ($(CONFIG_ISDN_DRV_PCBIT),m)
+ MOD_SUB_DIRS += pcbit
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/isdn/icn/Makefile b/drivers/isdn/icn/Makefile
new file mode 100644
index 000000000..2427bf292
--- /dev/null
+++ b/drivers/isdn/icn/Makefile
@@ -0,0 +1,11 @@
+L_OBJS :=
+M_OBJS :=
+
+ifeq ($(CONFIG_ISDN_DRV_ICN),y)
+ L_OBJS += icn.o
+else
+ M_OBJS += icn.o
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c
new file mode 100644
index 000000000..aca9fe72b
--- /dev/null
+++ b/drivers/isdn/icn/icn.c
@@ -0,0 +1,1675 @@
+/* $Id: icn.c,v 1.29 1996/08/29 20:34:54 fritz Exp $
+ *
+ * ISDN low-level module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * 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.
+ *
+ * $Log: icn.c,v $
+ * Revision 1.29 1996/08/29 20:34:54 fritz
+ * Bugfix in send queue management:
+ * sndcount was not updated correctly.
+ * Minor Bugfixes.
+ *
+ * Revision 1.28 1996/06/28 17:02:53 fritz
+ * replaced memcpy_fromfs_toio.
+ *
+ * Revision 1.27 1996/06/25 18:38:59 fritz
+ * Fixed function name in error message.
+ *
+ * Revision 1.26 1996/06/24 17:20:35 fritz
+ * Bugfixes in pollbchan_send():
+ * - Using lock field of skbuff breaks networking.
+ * - Added channel locking
+ * - changed dequeuing scheme.
+ * Eliminated misc. compiler warnings.
+ *
+ * Revision 1.25 1996/06/11 22:53:35 tsbogend
+ * fixed problem with large array on stack
+ * made the driver working on Linux/alpha
+ *
+ * Revision 1.24 1996/06/06 13:58:33 fritz
+ * Changed code to be architecture independent
+ *
+ * Revision 1.23 1996/06/03 19:59:00 fritz
+ * Fixed typos.
+ *
+ * Revision 1.22 1996/05/17 15:46:41 fritz
+ * Removed own queue management.
+ * Changed queue management to use sk_buffs.
+ *
+ * Revision 1.21 1996/05/02 04:01:20 fritz
+ * Bugfix:
+ * - icn_addcard() evaluated wrong driverId.
+ *
+ * Revision 1.20 1996/05/02 00:40:27 fritz
+ * Major rewrite to support more than one card
+ * with a single module.
+ * Support for new firmware.
+ *
+ * Revision 1.19 1996/04/21 17:43:32 fritz
+ * Changes for Support of new Firmware BRV3.02
+ *
+ * Revision 1.18 1996/04/20 16:50:26 fritz
+ * Fixed status-buffer overrun.
+ * Misc. typos
+ *
+ * Revision 1.17 1996/02/11 02:39:04 fritz
+ * Increased Buffer for status-messages.
+ * Removed conditionals for HDLC-firmware.
+ *
+ * Revision 1.16 1996/01/22 05:01:55 fritz
+ * Revert to GPL.
+ *
+ * Revision 1.15 1996/01/10 20:57:39 fritz
+ * Bugfix: Loading firmware twice caused the device stop working.
+ *
+ * Revision 1.14 1995/12/18 18:23:37 fritz
+ * Support for ICN-2B Cards.
+ * Change for supporting user-settable service-octet.
+ *
+ * Revision 1.13 1995/10/29 21:41:07 fritz
+ * Added support for DriverId's, added Jan's patches for Kernel versions.
+ *
+ * Revision 1.12 1995/04/29 13:07:35 fritz
+ * Added support for new Euro-ISDN-firmware
+ *
+ * Revision 1.11 1995/04/23 13:40:45 fritz
+ * Added support for SPV's.
+ * Changed Dial-Command to support MSN's on DSS1-Lines.
+ *
+ * Revision 1.10 1995/03/25 23:23:24 fritz
+ * Changed configurable Ports, to allow settings for DIP-Switch Cardversions.
+ *
+ * Revision 1.9 1995/03/25 23:17:30 fritz
+ * Fixed race-condition in pollbchan_send
+ *
+ * Revision 1.8 1995/03/15 12:49:44 fritz
+ * Added support for SPV's
+ * Split pollbchan_work for calling send-routine directly
+ *
+ * Revision 1.7 1995/02/20 03:48:03 fritz
+ * Added support of new request_region-function.
+ * Minor bugfixes.
+ *
+ * Revision 1.6 1995/01/31 15:48:45 fritz
+ * Added Cause-Messages to be signaled to upper layers.
+ * Added Revision-Info on load.
+ *
+ * Revision 1.5 1995/01/29 23:34:59 fritz
+ * Added stopdriver() and appropriate calls.
+ * Changed printk-statements to support loglevels.
+ *
+ * Revision 1.4 1995/01/09 07:40:46 fritz
+ * Added GPL-Notice
+ *
+ * Revision 1.3 1995/01/04 05:15:18 fritz
+ * Added undocumented "bootload-finished"-command in download-code
+ * to satisfy some brain-damaged icn card-versions.
+ *
+ * Revision 1.2 1995/01/02 02:14:45 fritz
+ * Misc Bugfixes
+ *
+ * Revision 1.1 1994/12/14 17:56:06 fritz
+ * Initial revision
+ *
+ */
+
+#include "icn.h"
+
+/*
+ * Verbose bootcode- and protocol-downloading.
+ */
+#undef BOOT_DEBUG
+
+/*
+ * Verbose Shmem-Mapping.
+ */
+#undef MAP_DEBUG
+
+static char
+*revision = "$Revision: 1.29 $";
+
+static int icn_addcard(int, char *, char *);
+
+/*
+ * Free queue completely.
+ * Parameter:
+ * queue = pointer to queue-head
+ */
+static void icn_free_queue(struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ while ((skb = skb_dequeue(queue)))
+ dev_kfree_skb(skb, FREE_WRITE);
+ restore_flags(flags);
+}
+
+/* Put a value into a shift-register, highest bit first.
+ * Parameters:
+ * port = port for output (bit 0 is significant)
+ * val = value to be output
+ * firstbit = Bit-Number of highest bit
+ * bitcount = Number of bits to output
+ */
+static inline void icn_shiftout(unsigned short port,
+ unsigned long val,
+ int firstbit,
+ int bitcount)
+{
+
+ register u_char s;
+ register u_char c;
+
+ for (s = firstbit, c = bitcount; c > 0; s--, c--)
+ OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
+}
+
+/*
+ * disable a cards shared memory
+ */
+static inline void icn_disable_ram(icn_card *card)
+{
+ OUTB_P(0, ICN_MAPRAM);
+}
+
+/*
+ * enable a cards shared memory
+ */
+static inline void icn_enable_ram(icn_card *card)
+{
+ OUTB_P(0xff, ICN_MAPRAM);
+}
+
+/*
+ * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12)
+ */
+static inline void icn_map_channel(icn_card *card, int channel)
+{
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_map_channel %d %d\n", dev->channel, channel);
+#endif
+ if ((channel == dev.channel) && (card == dev.mcard))
+ return;
+ if (dev.mcard)
+ icn_disable_ram(dev.mcard);
+ icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4); /* Select Bank */
+ icn_enable_ram(card);
+ dev.mcard = card;
+ dev.channel = channel;
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_map_channel done\n");
+#endif
+}
+
+/*
+ * Lock a cards channel.
+ * Return 0 if requested card/channel is unmapped (failure).
+ * Return 1 on success.
+ */
+static inline int icn_lock_channel(icn_card *card, int channel)
+{
+ register int retval;
+ ulong flags;
+
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
+#endif
+ save_flags(flags);
+ cli();
+ if ((dev.channel == channel) && (card == dev.mcard)) {
+ dev.chanlock++;
+ retval = 1;
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
+#endif
+ } else {
+ retval = 0;
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, device->channel);
+#endif
+ }
+ restore_flags(flags);
+ return retval;
+}
+
+/*
+ * Release current card/channel lock
+ */
+static inline void icn_release_channel(void)
+{
+ ulong flags;
+
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_release_channel l=%d\n", device->chanlock);
+#endif
+ save_flags(flags);
+ cli();
+ if (dev.chanlock)
+ dev.chanlock--;
+ restore_flags(flags);
+}
+
+/*
+ * Try to map and lock a cards channel.
+ * Return 1 on success, 0 on failure.
+ */
+static inline int icn_trymaplock_channel(icn_card *card, int channel)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,
+ dev.chanlock);
+#endif
+ if ((!dev.chanlock) ||
+ ((dev.channel == channel) && (dev.mcard == card))) {
+ dev.chanlock++;
+ icn_map_channel(card,channel);
+ restore_flags(flags);
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "trymaplock %d OK\n", channel);
+#endif
+ return 1;
+ }
+ restore_flags(flags);
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
+#endif
+ return 0;
+}
+
+/*
+ * Release current card/channel lock,
+ * then map same or other channel without locking.
+ */
+static inline void icn_maprelease_channel(icn_card *card, int channel)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);
+#endif
+ if (dev.chanlock)
+ dev.chanlock--;
+ if (!dev.chanlock)
+ icn_map_channel(card,channel);
+ restore_flags(flags);
+}
+
+/* Get Data from the B-Channel, assemble fragmented packets and put them
+ * into receive-queue. Wake up any B-Channel-reading processes.
+ * This routine is called via timer-callback from icn_pollbchan().
+ */
+
+static void icn_pollbchan_receive(int channel, icn_card *card)
+{
+ int mch = channel + ((card->secondhalf) ? 2 : 0);
+ int eflag;
+ int cnt;
+ struct sk_buff *skb;
+
+ if (icn_trymaplock_channel(card,mch)) {
+ while (rbavl) {
+ cnt = readb(&rbuf_l);
+ if ((card->rcvidx[channel] + cnt) > 4000) {
+ printk(KERN_WARNING
+ "icn: (%s) bogus packet on ch%d, dropping.\n",
+ CID,
+ channel + 1);
+ card->rcvidx[channel] = 0;
+ eflag = 0;
+ } else {
+ memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],
+ &rbuf_d, cnt);
+ card->rcvidx[channel] += cnt;
+ eflag = readb(&rbuf_f);
+ }
+ rbnext;
+ icn_maprelease_channel(card, mch & 2);
+ if (!eflag) {
+ if ((cnt = card->rcvidx[channel])) {
+ if (!(skb = dev_alloc_skb(cnt))) {
+ printk(KERN_WARNING "ïcn: receive out of memory\n");
+ break;
+ }
+ memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt);
+ card->rcvidx[channel] = 0;
+ card->interface.rcvcallb_skb(card->myid, channel, skb);
+ }
+ }
+ if (!icn_trymaplock_channel(card, mch))
+ break;
+ }
+ icn_maprelease_channel(card, mch & 2);
+ }
+}
+
+/* Send data-packet to B-Channel, split it up into fragments of
+ * ICN_FRAGSIZE length. If last fragment is sent out, signal
+ * success to upper layers via statcallb with ISDN_STAT_BSENT argument.
+ * This routine is called via timer-callback from icn_pollbchan() or
+ * directly from icn_sendbuf().
+ */
+
+static void icn_pollbchan_send(int channel, icn_card *card)
+{
+ int mch = channel + ((card->secondhalf) ? 2 : 0);
+ int cnt;
+ unsigned long flags;
+ struct sk_buff *skb;
+ isdn_ctrl cmd;
+
+ if (!card->sndcount[channel])
+ return;
+ if (icn_trymaplock_channel(card,mch)) {
+ while (sbfree && card->sndcount[channel]) {
+ save_flags(flags);
+ cli();
+ if (card->xmit_lock[channel]) {
+ restore_flags(flags);
+ break;
+ }
+ card->xmit_lock[channel]++;
+ restore_flags(flags);
+ skb = skb_dequeue(&card->spqueue[channel]);
+ if (!skb)
+ break;
+ if (skb->len > ICN_FRAGSIZE) {
+ writeb (0xff, &sbuf_f);
+ cnt = ICN_FRAGSIZE;
+ } else {
+ writeb (0x0, &sbuf_f);
+ cnt = skb->len;
+ }
+ writeb (cnt, &sbuf_l);
+ memcpy_toio(&sbuf_d, skb->data, cnt);
+ skb_pull(skb, cnt);
+ card->sndcount[channel] -= cnt;
+ sbnext; /* switch to next buffer */
+ icn_maprelease_channel(card, mch & 2);
+ if (!skb->len) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.driver = card->myid;
+ cmd.arg = channel;
+ card->interface.statcallb(&cmd);
+ } else
+ skb_queue_head(&card->spqueue[channel], skb);
+ card->xmit_lock[channel] = 0;
+ if (!icn_trymaplock_channel(card, mch))
+ break;
+ }
+ icn_maprelease_channel(card, mch & 2);
+ }
+}
+
+/* Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ */
+
+static void icn_pollbchan(unsigned long data)
+{
+ icn_card *card = (icn_card *)data;
+ unsigned long flags;
+
+ if (card->flags & ICN_FLAGS_B1ACTIVE) {
+ icn_pollbchan_receive(0, card);
+ icn_pollbchan_send(0, card);
+ }
+ if (card->flags & ICN_FLAGS_B2ACTIVE) {
+ icn_pollbchan_receive(1, card);
+ icn_pollbchan_send(1, card);
+ }
+ if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+ /* schedule b-channel polling again */
+ save_flags(flags);
+ cli();
+ del_timer(&card->rb_timer);
+ card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+ add_timer(&card->rb_timer);
+ card->flags |= ICN_FLAGS_RBTIMER;
+ restore_flags(flags);
+ } else
+ card->flags &= ~ICN_FLAGS_RBTIMER;
+}
+
+typedef struct icn_stat {
+ char *statstr;
+ int command;
+ int action;
+} icn_stat;
+
+static icn_stat icn_stat_table[] = {
+ {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */
+ {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */
+ {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */
+ {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */
+ {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */
+ {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */
+ {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */
+ {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */
+ {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */
+ {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */
+ {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */
+ {"NO D-CHAN", ISDN_STAT_NODCH, 0}, /* No D-channel available */
+ {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */
+ {NULL, 0 , -1}
+};
+
+/*
+ * Check Statusqueue-Pointer from isdn-cards.
+ * If there are new status-replies from the interface, check
+ * them against B-Channel-connects/disconnects and set flags accordingly.
+ * Wake-Up any processes, who are reading the status-device.
+ * If there are B-Channels open, initiate a timer-callback to
+ * icn_pollbchan().
+ * This routine is called periodically via timer.
+ */
+
+static int icn_parse_status(u_char *status, int channel, icn_card *card)
+{
+ icn_stat *s = icn_stat_table;
+ int action = -1;
+ int dflag = 0;
+ unsigned long flags;
+ isdn_ctrl cmd;
+
+ while (s->statstr) {
+ if (!strncmp(status,s->statstr,strlen(s->statstr))) {
+ cmd.command = s->command;
+ action = s->action;
+ break;
+ }
+ s++;
+ }
+ if (action==-1)
+ return 0;
+ cmd.driver = card->myid;
+ cmd.arg = channel;
+ switch (action) {
+ case 1:
+ card->flags |= (channel)?
+ ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE;
+ break;
+ case 2:
+ card->flags &= ~((channel)?
+ ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
+ icn_free_queue(&card->spqueue[channel]);
+ save_flags(flags);
+ cli();
+ card->rcvidx[channel] = 0;
+ restore_flags(flags);
+ dflag |= (channel+1);
+ break;
+ case 3:
+ strncpy(cmd.num, status + 6, sizeof(cmd.num) - 1);
+ break;
+ case 4:
+ sprintf(cmd.num,"LEASED%d,07,00,%d",
+ card->myid,channel+1);
+ break;
+ case 5:
+ strncpy(cmd.num, status + 3, sizeof(cmd.num) - 1);
+ break;
+ case 6:
+ sprintf(cmd.num,"%d",
+ (int)simple_strtoul(status + 7,NULL,16));
+ break;
+ case 7:
+ status += 3;
+ if (strlen(status)==4)
+ sprintf(cmd.num,"%s%c%c",
+ status+2,*status,*(status+1));
+ else
+ strncpy(cmd.num, status+1, sizeof(cmd.num) - 1);
+ break;
+ case 8:
+ cmd.arg = 0;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = 0;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_BHUP;
+ cmd.arg = 1;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = 1;
+ cmd.driver = card->myid;
+ break;
+ }
+ card->interface.statcallb(&cmd);
+ return dflag;
+}
+
+static void icn_polldchan(unsigned long data)
+{
+ icn_card *card = (icn_card *)data;
+ int mch = card->secondhalf ? 2 : 0;
+ int avail = 0;
+ int dflag = 0;
+ int left;
+ u_char c;
+ int ch;
+ int flags;
+ int i;
+ u_char *p;
+ isdn_ctrl cmd;
+
+ if (icn_trymaplock_channel(card,mch)) {
+ avail = msg_avail;
+ for (left = avail, i = readb(&msg_o); left > 0; i++, left--) {
+ c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]);
+ save_flags(flags);
+ cli();
+ *card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+ if (card->msg_buf_write == card->msg_buf_read) {
+ if (++card->msg_buf_read > card->msg_buf_end)
+ card->msg_buf_read = card->msg_buf;
+ }
+ if (card->msg_buf_write > card->msg_buf_end)
+ card->msg_buf_write = card->msg_buf;
+ restore_flags(flags);
+ if (c == 0xff) {
+ card->imsg[card->iptr] = 0;
+ card->iptr = 0;
+ if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+ card->imsg[1] <= '2' && card->imsg[2] == ';') {
+ ch = (card->imsg[1] - '0') - 1;
+ p = &card->imsg[3];
+ dflag |= icn_parse_status(p, ch, card);
+ } else {
+ p = card->imsg;
+ if (!strncmp(p, "DRV1.", 5)) {
+ u_char vstr[10];
+ u_char *q = vstr;
+
+ printk(KERN_INFO "icn: (%s) %s\n",CID,p);
+ if (!strncmp(p + 7, "TC", 2)) {
+ card->ptype = ISDN_PTYPE_1TR6;
+ card->interface.features |= ISDN_FEATURE_P_1TR6;
+ printk(KERN_INFO
+ "icn: (%s) 1TR6-Protocol loaded and running\n",CID);
+ }
+ if (!strncmp(p + 7, "EC", 2)) {
+ card->ptype = ISDN_PTYPE_EURO;
+ card->interface.features |= ISDN_FEATURE_P_EURO;
+ printk(KERN_INFO
+ "icn: (%s) Euro-Protocol loaded and running\n",CID);
+ }
+ p = strstr(card->imsg,"BRV") + 3;
+ while (*p) {
+ if (*p>='0' && *p<='9')
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ strcat(vstr,"000");
+ vstr[3] = '\0';
+ card->fw_rev = (int)simple_strtoul(vstr,NULL,10);
+ continue;
+
+ }
+ }
+ } else {
+ card->imsg[card->iptr] = c;
+ if (card->iptr < 59)
+ card->iptr++;
+ }
+ }
+ writeb((readb(&msg_o) + avail) & 0xff, &msg_o);
+ icn_release_channel();
+ }
+ if (avail) {
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = avail;
+ card->interface.statcallb(&cmd);
+ }
+ if (dflag & 1)
+ card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0);
+ if (dflag & 2)
+ card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0);
+ if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
+ if (!(card->flags & ICN_FLAGS_RBTIMER)) {
+ /* schedule b-channel polling */
+ card->flags |= ICN_FLAGS_RBTIMER;
+ save_flags(flags);
+ cli();
+ del_timer(&card->rb_timer);
+ card->rb_timer.function = icn_pollbchan;
+ card->rb_timer.data = (unsigned long)card;
+ card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+ add_timer(&card->rb_timer);
+ restore_flags(flags);
+ }
+ /* schedule again */
+ save_flags(flags);
+ cli();
+ del_timer(&card->st_timer);
+ card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+ add_timer(&card->st_timer);
+ restore_flags(flags);
+}
+
+/* Append a packet to the transmit buffer-queue.
+ * Parameters:
+ * channel = Number of B-channel
+ * skb = pointer to sk_buff
+ * card = pointer to card-struct
+ * Return:
+ * Number of bytes transferred, -E??? on error
+ */
+
+static int icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card)
+{
+ int len = skb->len;
+ unsigned long flags;
+ struct sk_buff *nskb;
+
+ if (len > 4000) {
+ printk(KERN_WARNING
+ "icn: Send packet too large\n");
+ return -EINVAL;
+ }
+ if (len) {
+ if (!(card->flags & (channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE))
+ return 0;
+ if (card->sndcount[channel] > ICN_MAX_SQUEUE)
+ return 0;
+ save_flags(flags);
+ cli();
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (nskb) {
+ skb_queue_tail(&card->spqueue[channel], nskb);
+ dev_kfree_skb(skb, FREE_WRITE);
+ } else
+ len = 0;
+ card->sndcount[channel] += len;
+ restore_flags(flags);
+ }
+ return len;
+}
+
+/*
+ * Check card's status after starting the bootstrap loader.
+ * On entry, the card's shared memory has already to be mapped.
+ * Return:
+ * 0 on success (Boot loader ready)
+ * -EIO on failure (timeout)
+ */
+static int icn_check_loader(int cardnumber)
+{
+ int timer = 0;
+
+ while (1) {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
+#endif
+ if (readb(&dev.shmem->data_control.scns) ||
+ readb(&dev.shmem->data_control.scnr)) {
+ if (timer++ > 5) {
+ printk(KERN_WARNING
+ "icn: Boot-Loader %d timed out.\n",
+ cardnumber);
+ icn_release_channel();
+ return -EIO;
+ }
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
+#endif
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+ schedule();
+ } else {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
+#endif
+ icn_release_channel();
+ return 0;
+ }
+ }
+}
+
+/* Load the boot-code into the interface-card's memory and start it.
+ * Always called from user-process.
+ *
+ * Parameters:
+ * buffer = pointer to packet
+ * Return:
+ * 0 if successfully loaded
+ */
+
+#ifdef BOOT_DEBUG
+#define SLEEP(sec) { \
+int slsec = sec; \
+ printk(KERN_DEBUG "SLEEP(%d)\n",slsec); \
+ while (slsec) { \
+ current->state = TASK_INTERRUPTIBLE; \
+ current->timeout = jiffies + HZ; \
+ schedule(); \
+ slsec--; \
+ } \
+}
+#else
+#define SLEEP(sec)
+#endif
+
+static int icn_loadboot(u_char * buffer, icn_card * card)
+{
+ int ret;
+ ulong flags;
+ u_char *codebuf;
+
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
+#endif
+ if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE1)))
+ return ret;
+ if (!(codebuf = kmalloc(ICN_CODE_STAGE1,GFP_KERNEL))) {
+ printk(KERN_WARNING "icn: Could not allocate code buffer\n");
+ return -ENOMEM;
+ }
+ save_flags(flags);
+ cli();
+ if (!card->rvalid) {
+ if (check_region(card->port, ICN_PORTLEN)) {
+ printk(KERN_WARNING
+ "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+ CID,
+ card->port,
+ card->port + ICN_PORTLEN);
+ restore_flags(flags);
+ kfree(codebuf);
+ return -EBUSY;
+ }
+ request_region(card->port, ICN_PORTLEN, card->regname);
+ card->rvalid = 1;
+ if (card->doubleS0)
+ card->other->rvalid = 1;
+ }
+ if (!dev.mvalid) {
+ if (check_shmem((ulong) dev.shmem, 0x4000)) {
+ printk(KERN_WARNING
+ "icn: memory at 0x%08lx in use.\n",
+ (ulong) dev.shmem);
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ request_shmem((ulong) dev.shmem, 0x4000, "icn");
+ dev.mvalid = 1;
+ }
+ restore_flags(flags);
+ OUTB_P(0, ICN_RUN); /* Reset Controller */
+ OUTB_P(0, ICN_MAPRAM); /* Disable RAM */
+ icn_shiftout(ICN_CFG, 0x0f, 3, 4); /* Windowsize= 16k */
+ icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10); /* Set RAM-Addr. */
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem);
+#endif
+ SLEEP(1);
+ save_flags(flags);
+ cli();
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+ icn_map_channel(card,0); /* Select Bank 0 */
+ icn_lock_channel(card,0); /* Lock Bank 0 */
+ restore_flags(flags);
+ SLEEP(1);
+ if (copy_from_user(codebuf, buffer, ICN_CODE_STAGE1))
+ return -EFAULT;
+ memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Bootloader transfered\n");
+#endif
+ if (card->doubleS0) {
+ SLEEP(1);
+ save_flags(flags);
+ cli();
+ icn_release_channel();
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Map Bank 8\n");
+#endif
+ icn_map_channel(card,2); /* Select Bank 8 */
+ icn_lock_channel(card,2); /* Lock Bank 8 */
+ restore_flags(flags);
+ SLEEP(1);
+ memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Bootloader transfered\n");
+#endif
+ }
+ kfree(codebuf);
+ SLEEP(1);
+ OUTB_P(0xff, ICN_RUN); /* Start Boot-Code */
+ if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1)))
+ return ret;
+ if (!card->doubleS0)
+ return 0;
+ /* reached only, if we have a Double-S0-Card */
+ save_flags(flags);
+ cli();
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+ icn_map_channel(card,0); /* Select Bank 0 */
+ icn_lock_channel(card,0); /* Lock Bank 0 */
+ restore_flags(flags);
+ SLEEP(1);
+ return (icn_check_loader(1));
+}
+
+static int icn_loadproto(u_char * buffer, icn_card * card)
+{
+ register u_char *p = buffer;
+ u_char codebuf[256];
+ uint left = ICN_CODE_STAGE2;
+ uint cnt;
+ int timer;
+ int ret;
+ unsigned long flags;
+
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "icn_loadproto called\n");
+#endif
+ if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2)))
+ return ret;
+ timer = 0;
+ save_flags(flags);
+ cli();
+ if (card->secondhalf) {
+ icn_map_channel(card, 2);
+ icn_lock_channel(card, 2);
+ } else {
+ icn_map_channel(card, 0);
+ icn_lock_channel(card, 0);
+ }
+ restore_flags(flags);
+ while (left) {
+ if (sbfree) { /* If there is a free buffer... */
+ cnt = MIN(256, left);
+ if (copy_from_user(codebuf, p, cnt))
+ /* FIXME -WRONG */return -EFAULT;
+ memcpy_toio(&sbuf_l, codebuf, cnt); /* copy data */
+ sbnext; /* switch to next buffer */
+ p += cnt;
+ left -= cnt;
+ timer = 0;
+ } else {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "boot 2 !sbfree\n");
+#endif
+ if (timer++ > 5) {
+ icn_maprelease_channel(card, 0);
+ return -EIO;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 10;
+ schedule();
+ }
+ }
+ writeb (0x20, &sbuf_n);
+ timer = 0;
+ while (1) {
+ if (readb(&cmd_o) || readb(&cmd_i)) {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto?\n");
+#endif
+ if (timer++ > 5) {
+ printk(KERN_WARNING
+ "icn: (%s) Protocol timed out.\n",
+ CID);
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto TO!\n");
+#endif
+ icn_maprelease_channel(card, 0);
+ return -EIO;
+ }
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto TO?\n");
+#endif
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+ schedule();
+ } else {
+ if ((card->secondhalf) || (!card->doubleS0)) {
+ save_flags(flags);
+ cli();
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
+ card->secondhalf);
+#endif
+ init_timer(&card->st_timer);
+ card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+ card->st_timer.function = icn_polldchan;
+ card->st_timer.data = (unsigned long)card;
+ add_timer(&card->st_timer);
+ card->flags |= ICN_FLAGS_RUNNING;
+ if (card->doubleS0) {
+ init_timer(&card->other->st_timer);
+ card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+ card->other->st_timer.function = icn_polldchan;
+ card->other->st_timer.data = (unsigned long)card->other;
+ add_timer(&card->other->st_timer);
+ card->other->flags |= ICN_FLAGS_RUNNING;
+ }
+ restore_flags(flags);
+ }
+ icn_maprelease_channel(card, 0);
+ return 0;
+ }
+ }
+}
+
+/* Read the Status-replies from the Interface */
+static int icn_readstatus(u_char * buf, int len, int user, icn_card * card)
+{
+ int count;
+ u_char *p;
+
+ for (p = buf, count = 0; count < len; p++, count++) {
+ if (card->msg_buf_read == card->msg_buf_write)
+ return count;
+ if (user)
+ put_user(*card->msg_buf_read++, p);
+ else
+ *p = *card->msg_buf_read++;
+ if (card->msg_buf_read > card->msg_buf_end)
+ card->msg_buf_read = card->msg_buf;
+ }
+ return count;
+}
+
+/* Put command-strings into the command-queue of the Interface */
+static int icn_writecmd(const u_char * buf, int len, int user, icn_card * card, int waitflg)
+{
+ int mch = card->secondhalf ? 2 : 0;
+ int avail;
+ int pp;
+ int i;
+ int count;
+ int ocount;
+ unsigned long flags;
+ u_char *p;
+ isdn_ctrl cmd;
+ u_char msg[0x100];
+
+ while (1) {
+ if (icn_trymaplock_channel(card, mch)) {
+ avail = cmd_free;
+ count = MIN(avail, len);
+ if (user)
+ {
+ if (copy_from_user(msg, buf, count) != 0)
+ {
+ icn_release_channel();
+ return -EFAULT;
+ }
+ }
+ else
+ memcpy(msg, buf, count);
+ save_flags(flags);
+ cli();
+ ocount = 1;
+ *card->msg_buf_write++ = '>';
+ if (card->msg_buf_write > card->msg_buf_end)
+ card->msg_buf_write = card->msg_buf;
+ for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp++) {
+ writeb((*p == '\n') ? 0xff : *p,
+ &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]);
+ *card->msg_buf_write++ = *p;
+ if ((*p == '\n') && (i > 1)) {
+ *card->msg_buf_write++ = '>';
+ if (card->msg_buf_write > card->msg_buf_end)
+ card->msg_buf_write = card->msg_buf;
+ ocount++;
+ }
+ /* No checks for buffer overflow of raw-status-device */
+ if (card->msg_buf_write > card->msg_buf_end)
+ card->msg_buf_write = card->msg_buf;
+ ocount++;
+ }
+ restore_flags(flags);
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = ocount;
+ card->interface.statcallb(&cmd);
+ writeb((readb(&cmd_i) + count) & 0xff, &cmd_i);
+ icn_release_channel();
+ waitflg = 0;
+ } else
+ count = 0;
+ if (!waitflg)
+ break;
+ current->timeout = jiffies + 10;
+ schedule();
+ }
+ return count;
+}
+
+/*
+ * Delete card's pending timers, send STOP to linklevel
+ */
+static void icn_stopcard(icn_card * card)
+{
+ unsigned long flags;
+ isdn_ctrl cmd;
+
+ save_flags(flags);
+ cli();
+ if (card->flags & ICN_FLAGS_RUNNING) {
+ card->flags &= ~ICN_FLAGS_RUNNING;
+ del_timer(&card->st_timer);
+ del_timer(&card->rb_timer);
+ cmd.command = ISDN_STAT_STOP;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ if (card->doubleS0)
+ icn_stopcard(card->other);
+ }
+ restore_flags(flags);
+}
+
+static void icn_stopallcards(void)
+{
+ icn_card *p = cards;
+
+ while (p) {
+ icn_stopcard(p);
+ p = p->next;
+ }
+}
+
+static int icn_command(isdn_ctrl * c, icn_card * card)
+{
+ ulong a;
+ ulong flags;
+ int i;
+ char cbuf[60];
+ isdn_ctrl cmd;
+ icn_cdef cdef;
+
+ switch (c->command) {
+ case ISDN_CMD_IOCTL:
+ memcpy(&a, c->num, sizeof(ulong));
+ switch (c->arg) {
+ case ICN_IOCTL_SETMMIO:
+ if ((unsigned long) dev.shmem != (a & 0x0ffc000)) {
+ if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
+ printk(KERN_WARNING
+ "icn: memory at 0x%08lx in use.\n",
+ (ulong) (a & 0x0ffc000));
+ return -EINVAL;
+ }
+ icn_stopallcards();
+ save_flags(flags);
+ cli();
+ if (dev.mvalid)
+ release_shmem((ulong) dev.shmem, 0x4000);
+ dev.mvalid = 0;
+ dev.shmem = (icn_shmem *) (a & 0x0ffc000);
+ restore_flags(flags);
+ printk(KERN_INFO
+ "icn: (%s) mmio set to 0x%08lx\n",
+ CID,
+ (unsigned long) dev.shmem);
+ }
+ break;
+ case ICN_IOCTL_GETMMIO:
+ return (long) dev.shmem;
+ case ICN_IOCTL_SETPORT:
+ if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
+ || a == 0x340 || a == 0x350 || a == 0x360 ||
+ a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
+ || a == 0x348 || a == 0x358 || a == 0x368) {
+ if (card->port != (unsigned short) a) {
+ if (check_region((unsigned short) a, ICN_PORTLEN)) {
+ printk(KERN_WARNING
+ "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+ CID, (int) a, (int) a + ICN_PORTLEN);
+ return -EINVAL;
+ }
+ icn_stopcard(card);
+ save_flags(flags);
+ cli();
+ if (card->rvalid)
+ release_region(card->port, ICN_PORTLEN);
+ card->port = (unsigned short) a;
+ card->rvalid = 0;
+ if (card->doubleS0) {
+ card->other->port = (unsigned short) a;
+ card->other->rvalid = 0;
+ }
+ restore_flags(flags);
+ printk(KERN_INFO
+ "icn: (%s) port set to 0x%03x\n",
+ CID, card->port);
+ }
+ } else
+ return -EINVAL;
+ break;
+ case ICN_IOCTL_GETPORT:
+ return (int) card->port;
+ case ICN_IOCTL_GETDOUBLE:
+ return (int) card->doubleS0;
+ case ICN_IOCTL_DEBUGVAR:
+ if ((i = verify_area(VERIFY_WRITE,
+ (void *) a,
+ sizeof(ulong) * 2)))
+ return i;
+ if (copy_to_user((char *)a,
+ (char *)&card, sizeof(ulong)))
+ return -EFAULT;
+ a += sizeof(ulong);
+ {
+ ulong l = (ulong)&dev;
+ if (copy_to_user((char *)a,
+ (char *)&l, sizeof(ulong)))
+ return -EFAULT;
+ }
+ return 0;
+ case ICN_IOCTL_LOADBOOT:
+ icn_stopcard(card);
+ return (icn_loadboot((u_char *) a, card));
+ case ICN_IOCTL_LOADPROTO:
+ icn_stopcard(card);
+ if ((i = (icn_loadproto((u_char *) a, card))))
+ return i;
+ if (card->doubleS0)
+ i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), card->other);
+ return i;
+ break;
+ case ICN_IOCTL_ADDCARD:
+ if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(icn_cdef))))
+ return i;
+ if (copy_from_user((char *)&cdef, (char *)a, sizeof(cdef)))
+ return -EFAULT;
+ return (icn_addcard(cdef.port, cdef.id1, cdef.id2));
+ break;
+ case ICN_IOCTL_LEASEDCFG:
+ if (a) {
+ if (!card->leased) {
+ card->leased = 1;
+ while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+ current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+ schedule();
+ }
+ current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+ schedule();
+ sprintf(cbuf, "00;FV2ON\n01;EAZ1\n");
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ printk(KERN_INFO
+ "icn: (%s) Leased-line mode enabled\n",
+ CID);
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ } else {
+ if (card->leased) {
+ card->leased = 0;
+ sprintf(cbuf, "00;FV2OFF\n");
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ printk(KERN_INFO
+ "icn: (%s) Leased-line mode disabled\n",
+ CID);
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ISDN_CMD_DIAL:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if ((c->arg & 255) < ICN_BCH) {
+ char *p;
+ char *p2;
+ char dial[50];
+ char sis[50];
+ char dcode[4];
+ int si1, si2;
+
+ a = c->arg;
+ strcpy(sis, c->num);
+ p = strrchr(sis, ',');
+ *p++ = '\0';
+ si2 = simple_strtoul(p,NULL,10);
+ p = strrchr(sis, ',') + 1;
+ si1 = simple_strtoul(p,NULL,10);
+ p = c->num;
+ if (*p == 's' || *p == 'S') {
+ /* Dial for SPV */
+ p++;
+ strcpy(dcode, "SCA");
+ } else
+ /* Normal Dial */
+ strcpy(dcode, "CAL");
+ strcpy(dial, p);
+ p = strchr(dial, ',');
+ *p++ = '\0';
+ p2 = strchr(p, ',');
+ *p2 = '\0';
+ sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), dcode, dial, si1,
+ si2, p);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ }
+ break;
+ case ISDN_CMD_ACCEPTD:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->fw_rev >= 300) {
+ switch (card->l2_proto[a-1]) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BX75\n", (int) a);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BTRA\n", (int) a);
+ break;
+ }
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ }
+ sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ }
+ break;
+ case ISDN_CMD_ACCEPTB:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->fw_rev >= 300)
+ switch (card->l2_proto[a-1]) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+ break;
+ }
+ else
+ sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ }
+ break;
+ case ISDN_CMD_HANGUP:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ }
+ break;
+ case ISDN_CMD_SETEAZ:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->ptype == ISDN_PTYPE_EURO) {
+ sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+ c->num[0] ? "N" : "ALL", c->num);
+ } else
+ sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+ c->num[0] ? c->num : "0123456789");
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ }
+ break;
+ case ISDN_CMD_CLREAZ:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->ptype == ISDN_PTYPE_EURO)
+ sprintf(cbuf, "%02d;MSNC\n", (int) a);
+ else
+ sprintf(cbuf, "%02d;EAZC\n", (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ }
+ break;
+ case ISDN_CMD_SETL2:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if ((c->arg & 255) < ICN_BCH) {
+ a = c->arg;
+ switch (a >> 8) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
+ card->l2_proto[a & 255] = (a >> 8);
+ }
+ break;
+ case ISDN_CMD_GETL2:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if ((c->arg & 255) < ICN_BCH)
+ return card->l2_proto[c->arg & 255];
+ else
+ return -ENODEV;
+ case ISDN_CMD_SETL3:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ return 0;
+ case ISDN_CMD_GETL3:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ if ((c->arg & 255) < ICN_BCH)
+ return ISDN_PROTO_L3_TRANS;
+ else
+ return -ENODEV;
+ case ISDN_CMD_GETEAZ:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ break;
+ case ISDN_CMD_SETSIL:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ break;
+ case ISDN_CMD_GETSIL:
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ break;
+ case ISDN_CMD_LOCK:
+ MOD_INC_USE_COUNT;
+ break;
+ case ISDN_CMD_UNLOCK:
+ MOD_DEC_USE_COUNT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline icn_card *
+ icn_findcard(int driverid)
+{
+ icn_card *p = cards;
+
+ while (p) {
+ if (p->myid == driverid)
+ return p;
+ p = p->next;
+ }
+ return (icn_card *)0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int if_command(isdn_ctrl * c)
+{
+ icn_card *card = icn_findcard(c->driver);
+
+ if (card)
+ return (icn_command(c, card));
+ printk(KERN_ERR
+ "icn: if_command %d called with invalid driverId %d!\n",
+ c->command, c->driver);
+ return -ENODEV;
+}
+
+static int if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+ icn_card *card = icn_findcard(id);
+
+ if (card) {
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ return (icn_writecmd(buf, len, user, card, 0));
+ }
+ printk(KERN_ERR
+ "icn: if_writecmd called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+ icn_card *card = icn_findcard(id);
+
+ if (card) {
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ return (icn_readstatus(buf, len, user, card));
+ }
+ printk(KERN_ERR
+ "icn: if_readstatus called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int if_sendbuf(int id, int channel, struct sk_buff *skb)
+{
+ icn_card *card = icn_findcard(id);
+
+ if (card) {
+ if (!card->flags & ICN_FLAGS_RUNNING)
+ return -ENODEV;
+ return (icn_sendbuf(channel, skb, card));
+ }
+ printk(KERN_ERR
+ "icn: if_sendbuf called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list and register it at linklevel.
+ */
+static icn_card *icn_initcard(int port, char *id) {
+ icn_card *card;
+ int i;
+
+ if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "icn: (%s) Could not allocate card-struct.\n", id);
+ return (icn_card *)0;
+ }
+ memset((char *) card, 0, sizeof(icn_card));
+ card->port = port;
+ card->interface.channels = ICN_BCH;
+ card->interface.maxbufsize = 4000;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = if_writecmd;
+ card->interface.readstat = if_readstatus;
+ card->interface.features = ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN;
+ card->ptype = ISDN_PTYPE_UNKNOWN;
+ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+ card->msg_buf_write = card->msg_buf;
+ card->msg_buf_read = card->msg_buf;
+ card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+ for (i=0;i<ICN_BCH;i++) {
+ card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+ skb_queue_head_init(&card->spqueue[i]);
+ }
+ card->next = cards;
+ cards = card;
+ if (!register_isdn(&card->interface)) {
+ cards = cards->next;
+ printk(KERN_WARNING
+ "icn: Unable to register %s\n", id);
+ kfree(card);
+ return (icn_card*)0;
+ }
+ card->myid = card->interface.channels;
+ sprintf(card->regname, "icn-isdn (%s)", card->interface.id);
+ return card;
+}
+
+static int icn_addcard(int port, char *id1, char *id2)
+{
+ ulong flags;
+ icn_card *card;
+ icn_card *card2;
+
+ save_flags(flags);
+ cli();
+ if (!(card = icn_initcard(port,id1))) {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if (!strlen(id2)) {
+ restore_flags(flags);
+ printk(KERN_INFO
+ "icn: (%s) ICN-2B, port 0x%x added\n",
+ card->interface.id, port);
+ return 0;
+ }
+ if (!(card2 = icn_initcard(port,id2))) {
+ restore_flags(flags);
+ printk(KERN_INFO
+ "icn: (%s) half ICN-4B, port 0x%x added\n",
+ card2->interface.id, port);
+ return 0;
+ }
+ card->doubleS0 = 1;
+ card->secondhalf = 0;
+ card->other = card2;
+ card2->doubleS0 = 1;
+ card2->secondhalf = 1;
+ card2->other = card;
+ restore_flags(flags);
+ printk(KERN_INFO
+ "icn: (%s and %s) ICN-4B, port 0x%x added\n",
+ card->interface.id, card2->interface.id, port);
+ return 0;
+}
+
+#ifdef MODULE
+#define icn_init init_module
+#else
+void icn_setup(char *str, int *ints)
+{
+ char *p;
+ static char sid[20];
+ static char sid2[20];
+
+ if (ints[0])
+ portbase = ints[1];
+ if (ints[0]>1)
+ membase = ints[2];
+ if (strlen(str)) {
+ strcpy(sid,str);
+ icn_id = sid;
+ if ((p = strchr(sid,','))) {
+ *p++ = 0;
+ strcpy(sid2,p);
+ icn_id2 = sid2;
+ }
+ }
+}
+#endif
+
+int icn_init(void)
+{
+ char *p;
+ char rev[10];
+
+ memset(&dev, 0, sizeof(icn_dev));
+ dev.shmem = (icn_shmem *) ((unsigned long)membase & 0x0ffc000);
+ dev.channel = -1;
+ dev.mcard = NULL;
+
+ /* No symbols to export, hide all symbols */
+ register_symtab(NULL);
+
+ if ((p = strchr(revision, ':'))) {
+ strcpy(rev, p + 1);
+ p = strchr(rev, '$');
+ *p = 0;
+ } else
+ strcpy(rev, " ??? ");
+ printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
+ (ulong) dev.shmem);
+ return (icn_addcard(portbase,icn_id,icn_id2));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ isdn_ctrl cmd;
+ icn_card *card = cards;
+ icn_card *last;
+ int i;
+
+ icn_stopallcards();
+ while (card) {
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ if (card->rvalid) {
+ OUTB_P(0, ICN_RUN); /* Reset Controller */
+ OUTB_P(0, ICN_MAPRAM); /* Disable RAM */
+ if (card->secondhalf || (!card->doubleS0)) {
+ release_region(card->port, ICN_PORTLEN);
+ card->rvalid = 0;
+ }
+ for (i = 0; i < ICN_BCH; i++)
+ icn_free_queue(&card->spqueue[i]);
+ }
+ card = card->next;
+ }
+ card = cards;
+ while (card) {
+ last = card;
+ card = card->next;
+ kfree(last);
+ }
+ if (dev.mvalid)
+ release_shmem((ulong) dev.shmem, 0x4000);
+ printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
+}
+#endif
diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h
new file mode 100644
index 000000000..655117f42
--- /dev/null
+++ b/drivers/isdn/icn/icn.h
@@ -0,0 +1,358 @@
+/* $Id: icn.h,v 1.21 1996/08/29 20:35:57 fritz Exp $
+ *
+ * ISDN lowlevel-module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * 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.
+ *
+ * $Log: icn.h,v $
+ * Revision 1.21 1996/08/29 20:35:57 fritz
+ * Speed up B-Channel polling interval.
+ *
+ * Revision 1.20 1996/06/24 17:20:37 fritz
+ * Bugfixes in pollbchan_send():
+ * - Using lock field of skbuff breaks networking.
+ * - Added channel locking
+ * - changed dequeuing scheme.
+ * Eliminated misc. compiler warnings.
+ *
+ * Revision 1.19 1996/06/06 13:58:35 fritz
+ * Changed code to be architecture independent
+ *
+ * Revision 1.18 1996/06/03 19:59:30 fritz
+ * Removed include of config.h
+ *
+ * Revision 1.17 1996/05/18 00:47:04 fritz
+ * Removed callback debug code.
+ *
+ * Revision 1.16 1996/05/17 15:46:43 fritz
+ * Removed own queue management.
+ * Changed queue management to use sk_buffs.
+ *
+ * Revision 1.15 1996/05/02 04:01:57 fritz
+ * Removed ICN_MAXCARDS
+ *
+ * Revision 1.14 1996/05/02 00:40:29 fritz
+ * Major rewrite to support more than one card
+ * with a single module.
+ * Support for new firmware.
+ *
+ * Revision 1.13 1996/04/20 16:51:41 fritz
+ * Increased status buffer.
+ * Misc. typos
+ *
+ * Revision 1.12 1996/01/22 05:01:22 fritz
+ * Revert to GPL.
+ *
+ * Revision 1.11 1995/12/18 18:25:00 fritz
+ * Support for ICN-2B Cards.
+ * Change for supporting user-settable service-octet.
+ *
+ * Revision 1.10 1995/10/29 21:43:10 fritz
+ * Added support for leased lines.
+ *
+ * Revision 1.9 1995/04/23 13:42:10 fritz
+ * Added some constants for distinguishing 1TR6 and DSS1
+ *
+ * Revision 1.8 1995/03/25 23:18:55 fritz
+ * Changed ICN_PORTLEN to reflect correct number of ports.
+ *
+ * Revision 1.7 1995/03/15 12:52:06 fritz
+ * Some cleanup
+ *
+ * Revision 1.6 1995/02/20 03:49:22 fritz
+ * Fixed ICN_MAX_SQUEUE to correctly reflect outstanding bytes, not number
+ * of buffers.
+ *
+ * Revision 1.5 1995/01/29 23:36:50 fritz
+ * Minor cleanup.
+ *
+ * Revision 1.4 1995/01/09 07:41:20 fritz
+ * Added GPL-Notice
+ *
+ * Revision 1.3 1995/01/04 05:14:20 fritz
+ * removed include of linux/asm/string.h for compiling with Linux 1.1.76
+ *
+ * Revision 1.2 1995/01/02 02:15:57 fritz
+ * Misc. Bugfixes
+ *
+ * Revision 1.1 1994/12/14 18:02:38 fritz
+ * Initial revision
+ *
+ */
+
+#ifndef icn_h
+#define icn_h
+
+#define ICN_IOCTL_SETMMIO 0
+#define ICN_IOCTL_GETMMIO 1
+#define ICN_IOCTL_SETPORT 2
+#define ICN_IOCTL_GETPORT 3
+#define ICN_IOCTL_LOADBOOT 4
+#define ICN_IOCTL_LOADPROTO 5
+#define ICN_IOCTL_LEASEDCFG 6
+#define ICN_IOCTL_GETDOUBLE 7
+#define ICN_IOCTL_DEBUGVAR 8
+#define ICN_IOCTL_ADDCARD 9
+
+/* Struct for adding new cards */
+typedef struct icn_cdef {
+ int port;
+ char id1[10];
+ char id2[10];
+} icn_cdef;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+
+/* Kernel includes */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+
+
+#endif /* __KERNEL__ */
+
+/* some useful macros for debugging */
+#ifdef ICN_DEBUG_PORT
+#define OUTB_P(v,p) {printk(KERN_DEBUG "icn: outb_p(0x%02x,0x%03x)\n",v,p); outb_p(v,p);}
+#else
+#define OUTB_P outb
+#endif
+
+/* Defaults for Port-Address and shared-memory */
+#define ICN_BASEADDR 0x320
+#define ICN_PORTLEN (0x04)
+#define ICN_MEMADDR 0x0d0000
+
+#define ICN_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */
+#define ICN_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */
+#define ICN_FLAGS_RUNNING 4 /* Cards driver activated */
+#define ICN_FLAGS_RBTIMER 8 /* cyclic scheduling of B-Channel-poll */
+
+#define ICN_BOOT_TIMEOUT1 100 /* Delay for Boot-download (jiffies) */
+#define ICN_CHANLOCK_DELAY 10 /* Delay for Channel-mapping (jiffies) */
+
+#define ICN_TIMER_BCREAD 1 /* B-Channel poll-cycle */
+#define ICN_TIMER_DCREAD 50 /* D-Channel poll-cycle */
+
+#define ICN_CODE_STAGE1 4096 /* Size of bootcode */
+#define ICN_CODE_STAGE2 65536 /* Size of protocol-code */
+
+#define ICN_MAX_SQUEUE 8000 /* Max. outstanding send-data (2* hw-buf.) */
+#define ICN_FRAGSIZE (250) /* Max. size of send-fragments */
+#define ICN_BCH 2 /* Number of supported channels per card */
+
+/* type-definitions for accessing the mmap-io-areas */
+
+#define SHM_DCTL_OFFSET (0) /* Offset to data-controlstructures in shm */
+#define SHM_CCTL_OFFSET (0x1d2) /* Offset to comm-controlstructures in shm */
+#define SHM_CBUF_OFFSET (0x200) /* Offset to comm-buffers in shm */
+#define SHM_DBUF_OFFSET (0x2000) /* Offset to data-buffers in shm */
+
+/*
+ * Layout of card's data buffers
+ */
+typedef struct {
+ unsigned char length; /* Bytecount of fragment (max 250) */
+ unsigned char endflag; /* 0=last frag., 0xff=frag. continued */
+ unsigned char data[ICN_FRAGSIZE]; /* The data */
+ /* Fill to 256 bytes */
+ char unused[0x100 - ICN_FRAGSIZE - 2];
+} frag_buf;
+
+/*
+ * Layout of card's shared memory
+ */
+typedef union {
+ struct {
+ unsigned char scns; /* Index to free SendFrag. */
+ unsigned char scnr; /* Index to active SendFrag READONLY */
+ unsigned char ecns; /* Index to free RcvFrag. READONLY */
+ unsigned char ecnr; /* Index to valid RcvFrag */
+ char unused[6];
+ unsigned short fuell1; /* Internal Buf Bytecount */
+ } data_control;
+ struct {
+ char unused[SHM_CCTL_OFFSET];
+ unsigned char iopc_i; /* Read-Ptr Status-Queue READONLY */
+ unsigned char iopc_o; /* Write-Ptr Status-Queue */
+ unsigned char pcio_i; /* Write-Ptr Command-Queue */
+ unsigned char pcio_o; /* Read-Ptr Command Queue READONLY */
+ } comm_control;
+ struct {
+ char unused[SHM_CBUF_OFFSET];
+ unsigned char pcio_buf[0x100]; /* Ring-Buffer Command-Queue */
+ unsigned char iopc_buf[0x100]; /* Ring-Buffer Status-Queue */
+ } comm_buffers;
+ struct {
+ char unused[SHM_DBUF_OFFSET];
+ frag_buf receive_buf[0x10];
+ frag_buf send_buf[0x10];
+ } data_buffers;
+} icn_shmem;
+
+/*
+ * Per card driver data
+ */
+typedef struct icn_card {
+ struct icn_card *next; /* Pointer to next device struct */
+ struct icn_card *other; /* Pointer to other card for ICN4B */
+ unsigned short port; /* Base-port-address */
+ int myid; /* Driver-Nr. assigned by linklevel */
+ int rvalid; /* IO-portregion has been requested */
+ int leased; /* Flag: This Adapter is connected */
+ /* to a leased line */
+ unsigned short flags; /* Statusflags */
+ int doubleS0; /* Flag: ICN4B */
+ int secondhalf; /* Flag: Second half of a doubleS0 */
+ int fw_rev; /* Firmware revision loaded */
+ int ptype; /* Protocol type (1TR6 or Euro) */
+ struct timer_list st_timer; /* Timer for Status-Polls */
+ struct timer_list rb_timer; /* Timer for B-Channel-Polls */
+ u_char rcvbuf[ICN_BCH][4096]; /* B-Channel-Receive-Buffers */
+ int rcvidx[ICN_BCH]; /* Index for above buffers */
+ int l2_proto[ICN_BCH]; /* Current layer-2-protocol */
+ isdn_if interface; /* Interface to upper layer */
+ int iptr; /* Index to imsg-buffer */
+ char imsg[60]; /* Internal buf for status-parsing */
+ char msg_buf[2048]; /* Buffer for status-messages */
+ char *msg_buf_write; /* Writepointer for statusbuffer */
+ char *msg_buf_read; /* Readpointer for statusbuffer */
+ char *msg_buf_end; /* Pointer to end of statusbuffer */
+ int sndcount[ICN_BCH]; /* Byte-counters for B-Ch.-send */
+ struct sk_buff_head
+ spqueue[ICN_BCH]; /* Sendqueue */
+ char regname[35]; /* Name used for request_region */
+ u_char xmit_lock[ICN_BCH]; /* Semaphore for pollbchan_send() */
+} icn_card;
+
+/*
+ * Main driver data
+ */
+typedef struct icn_dev {
+ icn_shmem *shmem; /* Pointer to memory-mapped-buffers */
+ int mvalid; /* IO-shmem has been requested */
+ int channel; /* Currently mapped channel */
+ struct icn_card *mcard; /* Currently mapped card */
+ int chanlock; /* Semaphore for channel-mapping */
+} icn_dev;
+
+typedef icn_dev *icn_devptr;
+
+#ifdef __KERNEL__
+
+static icn_card *cards = (icn_card *) 0;
+static u_char chan2bank[] = { 0, 4, 8, 12 }; /* for icn_map_channel() */
+
+static icn_dev dev;
+
+/* With modutils >= 1.1.67 Integers can be changed while loading a
+ * module. For this reason define the Port-Base an Shmem-Base as
+ * integers.
+ */
+int portbase = ICN_BASEADDR;
+int membase = ICN_MEMADDR;
+char *icn_id = "\0";
+char *icn_id2 = "\0";
+
+#endif /* __KERNEL__ */
+
+/* Utility-Macros */
+
+/* Macros for accessing ports */
+#define ICN_CFG (card->port)
+#define ICN_MAPRAM (card->port+1)
+#define ICN_RUN (card->port+2)
+#define ICN_BANK (card->port+3)
+
+/* Return true, if there is a free transmit-buffer */
+#define sbfree (((readb(&dev.shmem->data_control.scns)+1) & 0xf) != \
+ readb(&dev.shmem->data_control.scnr))
+
+/* Switch to next transmit-buffer */
+#define sbnext (writeb((readb(&dev.shmem->data_control.scns)+1) & 0xf, \
+ &dev.shmem->data_control.scns))
+
+/* Shortcuts for transmit-buffer-access */
+#define sbuf_n dev.shmem->data_control.scns
+#define sbuf_d dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].data
+#define sbuf_l dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].length
+#define sbuf_f dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].endflag
+
+/* Return true, if there is receive-data is available */
+#define rbavl (readb(&dev.shmem->data_control.ecnr) != \
+ readb(&dev.shmem->data_control.ecns))
+
+/* Switch to next receive-buffer */
+#define rbnext (writeb((readb(&dev.shmem->data_control.ecnr)+1) & 0xf, \
+ &dev.shmem->data_control.ecnr))
+
+/* Shortcuts for receive-buffer-access */
+#define rbuf_n dev.shmem->data_control.ecnr
+#define rbuf_d dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].data
+#define rbuf_l dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].length
+#define rbuf_f dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].endflag
+
+/* Shortcuts for command-buffer-access */
+#define cmd_o (dev.shmem->comm_control.pcio_o)
+#define cmd_i (dev.shmem->comm_control.pcio_i)
+
+/* Return free space in command-buffer */
+#define cmd_free ((readb(&cmd_i)>=readb(&cmd_o))? \
+ 0x100-readb(&cmd_i)+readb(&cmd_o): \
+ readb(&cmd_o)-readb(&cmd_i))
+
+/* Shortcuts for message-buffer-access */
+#define msg_o (dev.shmem->comm_control.iopc_o)
+#define msg_i (dev.shmem->comm_control.iopc_i)
+
+/* Return length of Message, if avail. */
+#define msg_avail ((readb(&msg_o)>readb(&msg_i))? \
+ 0x100-readb(&msg_o)+readb(&msg_i): \
+ readb(&msg_i)-readb(&msg_o))
+
+#define CID (card->interface.id)
+
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
+/* Hopefully, a separate resource-registration-scheme for shared-memory
+ * will be introduced into the kernel. Until then, we use the normal
+ * routines, designed for port-registration.
+ */
+#define check_shmem check_region
+#define release_shmem release_region
+#define request_shmem request_region
+
+#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif /* icn_h */
diff --git a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c
new file mode 100644
index 000000000..a356b512e
--- /dev/null
+++ b/drivers/isdn/isdn_audio.c
@@ -0,0 +1,622 @@
+/* $Id: isdn_audio.c,v 1.6 1996/06/06 14:43:31 fritz Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
+ *
+ * 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.
+ *
+ * $Log: isdn_audio.c,v $
+ * Revision 1.6 1996/06/06 14:43:31 fritz
+ * Changed to support DTMF decoding on audio playback also.
+ *
+ * Revision 1.5 1996/06/05 02:24:08 fritz
+ * Added DTMF decoder for audio mode.
+ *
+ * Revision 1.4 1996/05/17 03:48:01 fritz
+ * Removed some test statements.
+ * Added revision string.
+ *
+ * Revision 1.3 1996/05/10 08:48:11 fritz
+ * Corrected adpcm bugs.
+ *
+ * Revision 1.2 1996/04/30 09:31:17 fritz
+ * General rewrite.
+ *
+ * Revision 1.1.1.1 1996/04/28 12:25:40 fritz
+ * Taken under CVS control
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_audio.h"
+#include "isdn_common.h"
+
+char *isdn_audio_revision = "$Revision: 1.6 $";
+
+/*
+ * Misc. lookup-tables.
+ */
+
+/* ulaw -> signed 16-bit */
+static short isdn_audio_ulaw_to_s16[] = {
+ 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
+ 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
+ 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
+ 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
+ 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
+ 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
+ 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
+ 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
+ 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
+ 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
+ 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
+ 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
+ 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
+ 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
+ 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
+ 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
+ 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
+ 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
+ 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
+ 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
+ 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
+ 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
+ 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
+ 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
+ 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
+ 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
+ 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
+ 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
+ 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
+ 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
+ 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
+ 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+/* alaw -> signed 16-bit */
+static short isdn_audio_alaw_to_s16[] = {
+ 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
+ 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
+ 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
+ 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
+ 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
+ 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
+ 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
+ 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
+ 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
+ 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
+ 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
+ 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
+ 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
+ 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
+ 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
+ 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
+ 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
+ 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
+ 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
+ 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
+ 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
+ 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
+ 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
+ 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
+ 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
+ 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
+ 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
+ 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
+ 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
+ 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
+ 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
+ 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
+};
+
+/* alaw -> ulaw */
+static char isdn_audio_alaw_to_ulaw[] = {
+ 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+ 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+ 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+ 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+ 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+ 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+ 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+ 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+ 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+ 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+ 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+ 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+ 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+ 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+ 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+ 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+ 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+ 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+ 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+ 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+ 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+ 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+ 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+ 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+ 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+ 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+ 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+ 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+ 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+ 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+ 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+ 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+};
+
+/* ulaw -> alaw */
+static char isdn_audio_ulaw_to_alaw[] = {
+ 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+ 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+ 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+ 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+ 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+ 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+ 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+ 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+ 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+ 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+ 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+ 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+ 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+ 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+ 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+ 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+ 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+ 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+ 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+ 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+ 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+ 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+ 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+ 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+ 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+ 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+ 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+ 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+ 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+ 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+ 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+ 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+};
+
+#define NCOEFF 16 /* number of frequencies to be analyzed */
+#define DTMF_TRESH 50000 /* above this is dtmf */
+#define SILENCE_TRESH 100 /* below this is silence */
+#define H2_TRESH 10000 /* 2nd harmonic */
+#define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */
+#define LOGRP 0
+#define HIGRP 1
+
+typedef struct {
+ int grp; /* low/high group */
+ int k; /* k */
+ int k2; /* k fuer 2. harmonic */
+} dtmf_t;
+
+/* For DTMF recognition:
+ * 2 * cos(2 * PI * k / N) precalculated for all k
+ */
+static int cos2pik[NCOEFF] = {
+ 55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517,
+ 38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
+};
+
+static dtmf_t dtmf_tones[8] = {
+ { LOGRP, 0, 1 }, /* 697 Hz */
+ { LOGRP, 2, 3 }, /* 770 Hz */
+ { LOGRP, 4, 5 }, /* 852 Hz */
+ { LOGRP, 6, 7 }, /* 941 Hz */
+ { HIGRP, 8, 9 }, /* 1209 Hz */
+ { HIGRP, 10, 11 }, /* 1336 Hz */
+ { HIGRP, 12, 13 }, /* 1477 Hz */
+ { HIGRP, 14, 15 } /* 1633 Hz */
+};
+
+static char dtmf_matrix[4][4] = {
+ {'1', '2', '3', 'A'},
+ {'4', '5', '6', 'B'},
+ {'7', '8', '9', 'C'},
+ {'*', '0', '#', 'D'}
+};
+
+#if ((CPU == 386) || (CPU == 486) || (CPU == 586))
+static inline void
+isdn_audio_tlookup(const void *table, void *buff, unsigned long n)
+{
+ __asm__("cld\n"
+ "1:\tlodsb\n\t"
+ "xlatb\n\t"
+ "stosb\n\t"
+ "loop 1b\n\t"
+ ::"b" ((long)table), "c" (n), "D" ((long)buff), "S" ((long)buff)
+ :"bx","cx","di","si","ax");
+}
+#else
+static inline void
+isdn_audio_tlookup(const char *table, char *buff, unsigned long n)
+{
+ while (n--)
+ *buff++ = table[*buff];
+}
+#endif
+
+void
+isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
+{
+ isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
+}
+
+void
+isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len)
+{
+ isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
+}
+
+/*
+ * linear <-> adpcm conversion stuff
+ * Most parts from the mgetty-package.
+ * (C) by Gert Doering and Klaus Weidner
+ * Used by permission of Gert Doering
+ */
+
+
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#undef ZEROTRAP
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+static unsigned char
+isdn_audio_linear2ulaw(int sample) {
+ static int exp_lut[256] = {
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+ };
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if(sign != 0) sample = -sample; /* get magnitude */
+ if(sample > CLIP) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+ /* optional CCITT trap */
+ if (ulawbyte == 0) ulawbyte = 0x02;
+#endif
+ return(ulawbyte);
+}
+
+
+static int Mx[3][8] = {
+ { 0x3800, 0x5600, 0,0,0,0,0,0 },
+ { 0x399a, 0x3a9f, 0x4d14, 0x6607, 0,0,0,0 },
+ { 0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607 },
+};
+
+static int bitmask[9] = {
+ 0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
+};
+
+static int
+isdn_audio_get_bits (adpcm_state *s, unsigned char **in, int *len)
+{
+ while( s->nleft < s->nbits) {
+ int d = *((*in)++);
+ (*len)--;
+ s->word = (s->word << 8) | d;
+ s->nleft += 8;
+ }
+ s->nleft -= s->nbits;
+ return (s->word >> s->nleft) & bitmask[s->nbits];
+}
+
+static void
+isdn_audio_put_bits (int data, int nbits, adpcm_state *s,
+ unsigned char **out, int *len)
+{
+ s->word = (s->word << nbits) | (data & bitmask[nbits]);
+ s->nleft += nbits;
+ while(s->nleft >= 8) {
+ int d = (s->word >> (s->nleft-8));
+ *(out[0]++) = d & 255;
+ (*len)++;
+ s->nleft -= 8;
+ }
+}
+
+adpcm_state *
+isdn_audio_adpcm_init(adpcm_state *s, int nbits)
+{
+ if (!s)
+ s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
+ if (s) {
+ s->a = 0;
+ s->d = 5;
+ s->word = 0;
+ s->nleft = 0;
+ s->nbits = nbits;
+ }
+ return s;
+}
+
+dtmf_state *
+isdn_audio_dtmf_init(dtmf_state *s)
+{
+ if (!s)
+ s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
+ if (s) {
+ s->idx = 0;
+ s->last = ' ';
+ }
+ return s;
+}
+
+/*
+ * Decompression of adpcm data to a/u-law
+ *
+ */
+
+int
+isdn_audio_adpcm2xlaw (adpcm_state *s, int fmt, unsigned char *in,
+ unsigned char *out, int len)
+{
+ int a = s->a;
+ int d = s->d;
+ int nbits = s->nbits;
+ int olen = 0;
+
+ while (len) {
+ int e = isdn_audio_get_bits(s, &in, &len);
+ int sign;
+
+ if (nbits == 4 && e == 0)
+ d = 4;
+ sign = (e >> (nbits-1))?-1:1;
+ e &= bitmask[nbits-1];
+ a += sign * ((e << 1) + 1) * d >> 1;
+ if (d & 1)
+ a++;
+ if (fmt)
+ *out++ = isdn_audio_ulaw_to_alaw[
+ isdn_audio_linear2ulaw(a << 2)];
+ else
+ *out++ = isdn_audio_linear2ulaw(a << 2);
+ olen++;
+ d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14;
+ if ( d < 5 )
+ d = 5;
+ }
+ s->a = a;
+ s->d = d;
+ return olen;
+}
+
+int
+isdn_audio_2adpcm_flush (adpcm_state *s, unsigned char *out)
+{
+ int olen = 0;
+
+ if (s->nleft)
+ isdn_audio_put_bits(0, 8-s->nleft, s, &out, &olen);
+ return olen;
+}
+
+int
+isdn_audio_xlaw2adpcm (adpcm_state *s, int fmt, unsigned char *in,
+ unsigned char *out, int len)
+{
+ int a = s->a;
+ int d = s->d;
+ int nbits = s->nbits;
+ int olen = 0;
+
+ while (len--) {
+ int e = 0, nmax = 1 << (nbits - 1);
+ int sign, delta;
+
+ if (fmt)
+ delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
+ else
+ delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
+ if (delta < 0) {
+ e = nmax;
+ delta = -delta;
+ }
+ while( --nmax && delta > d ) {
+ delta -= d;
+ e++;
+ }
+ if (nbits == 4 && ((e & 0x0f) == 0))
+ e = 8;
+ isdn_audio_put_bits(e, nbits, s, &out, &olen);
+ sign = (e >> (nbits-1))?-1:1 ;
+ e &= bitmask[nbits-1];
+
+ a += sign * ((e << 1) + 1) * d >> 1;
+ if (d & 1)
+ a++;
+ d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14;
+ if (d < 5)
+ d=5;
+ }
+ s->a = a;
+ s->d = d;
+ return olen;
+}
+
+/*
+ * Goertzel algorithm.
+ * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/
+ * for more info.
+ * Result is stored into an sk_buff and queued up for later
+ * evaluation.
+ */
+void
+isdn_audio_goertzel(int *sample, modem_info *info) {
+ int sk, sk1, sk2;
+ int k, n;
+ struct sk_buff *skb;
+ int *result;
+
+ skb = dev_alloc_skb(sizeof(int) * NCOEFF);
+ if (!skb) {
+ printk(KERN_WARNING
+ "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
+ info->line);
+ return;
+ }
+ result = (int *)skb_put(skb, sizeof(int) * NCOEFF);
+ skb->free = 1;
+ skb->users = 0;
+ for (k = 0; k < NCOEFF; k++) {
+ sk = sk1 = sk2 = 0;
+ for (n = 0; n < DTMF_NPOINTS; n++) {
+ sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
+ sk2 = sk1;
+ sk1 = sk;
+ }
+ result[k] =
+ ((sk * sk) >> AMP_BITS) -
+ ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
+ ((sk2 * sk2) >> AMP_BITS);
+ }
+ skb_queue_tail(&info->dtmf_queue, skb);
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+}
+
+void
+isdn_audio_eval_dtmf(modem_info *info)
+{
+ struct sk_buff *skb;
+ int *result;
+ dtmf_state *s;
+ int silence;
+ int i;
+ int di;
+ int ch;
+ unsigned long flags;
+ int grp[2];
+ char what;
+ char *p;
+
+ while ((skb = skb_dequeue(&info->dtmf_queue))) {
+ result = (int *)skb->data;
+ s = info->dtmf_state;
+ grp[LOGRP] = grp[HIGRP] = -2;
+ silence = 0;
+ for(i = 0; i < 8; i++) {
+ if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
+ (result[dtmf_tones[i].k2] < H2_TRESH) )
+ grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2)?i:-1;
+ else
+ if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
+ (result[dtmf_tones[i].k2] < SILENCE_TRESH) )
+ silence++;
+ }
+ if(silence == 8)
+ what = ' ';
+ else {
+ if((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
+ what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
+ if(s->last != ' ' && s->last != '.')
+ s->last = what; /* min. 1 non-DTMF between DTMF */
+ } else
+ what = '.';
+ }
+ if ((what != s->last) && (what != ' ') && (what != '.')) {
+ printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
+ p = skb->data;
+ *p++ = 0x10;
+ *p = what;
+ skb_trim(skb, 2);
+ save_flags(flags);
+ cli();
+ di = info->isdn_driver;
+ ch = info->isdn_channel;
+ __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+ dev->drv[di]->rcvcount[ch] += 2;
+ restore_flags(flags);
+ /* Schedule dequeuing */
+ if ((dev->modempoll) && (info->rcvsched))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+ wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+ } else
+ kfree_skb(skb, FREE_READ);
+ s->last = what;
+ }
+}
+
+/*
+ * Decode DTMF tones, queue result in separate sk_buf for
+ * later examination.
+ * Parameters:
+ * s = pointer to state-struct.
+ * buf = input audio data
+ * len = size of audio data.
+ * fmt = audio data format (0 = ulaw, 1 = alaw)
+ */
+void
+isdn_audio_calc_dtmf(modem_info *info, unsigned char *buf, int len, int fmt)
+{
+ dtmf_state *s = info->dtmf_state;
+ int i;
+ int c;
+
+ while (len) {
+ c = MIN(len, (DTMF_NPOINTS - s->idx));
+ if (c <= 0)
+ break;
+ for (i = 0; i < c; i++) {
+ if (fmt)
+ s->buf[s->idx++] =
+ isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
+ else
+ s->buf[s->idx++] =
+ isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
+ }
+ if (s->idx == DTMF_NPOINTS) {
+ isdn_audio_goertzel(s->buf, info);
+ s->idx = 0;
+ }
+ len -= c;
+ }
+}
+
+
diff --git a/drivers/isdn/isdn_audio.h b/drivers/isdn/isdn_audio.h
new file mode 100644
index 000000000..a4a5c9a13
--- /dev/null
+++ b/drivers/isdn/isdn_audio.h
@@ -0,0 +1,59 @@
+/* $Id: isdn_audio.h,v 1.4 1996/06/06 14:43:32 fritz Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_audio.h,v $
+ * Revision 1.4 1996/06/06 14:43:32 fritz
+ * Changed to support DTMF decoding on audio playback also.
+ *
+ * Revision 1.3 1996/06/05 02:24:09 fritz
+ * Added DTMF decoder for audio mode.
+ *
+ * Revision 1.2 1996/05/10 08:48:32 fritz
+ * Corrected adpcm bugs.
+ *
+ * Revision 1.1 1996/04/30 09:29:06 fritz
+ * Taken under CVS control.
+ *
+ */
+
+#define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */
+typedef struct adpcm_state {
+ int a;
+ int d;
+ int word;
+ int nleft;
+ int nbits;
+} adpcm_state;
+
+typedef struct dtmf_state {
+ char last;
+ int idx;
+ int buf[DTMF_NPOINTS];
+} dtmf_state;
+
+extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
+extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
+extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
+extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_2adpcm_flush(adpcm_state *s, unsigned char *out);
+extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_dtmf(modem_info *);
+dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
diff --git a/drivers/isdn/isdn_cards.c b/drivers/isdn/isdn_cards.c
new file mode 100644
index 000000000..28ca52c4e
--- /dev/null
+++ b/drivers/isdn/isdn_cards.c
@@ -0,0 +1,53 @@
+/* $Id: isdn_cards.c,v 1.1 1996/04/20 16:04:36 fritz Exp $
+ *
+ * Linux ISDN subsystem, initialization for non-modularized drivers.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_cards.c,v $
+ * Revision 1.1 1996/04/20 16:04:36 fritz
+ * Initial revision
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_ISDN_DRV_ICN
+extern void icn_init(void);
+#endif
+
+#ifdef CONFIG_ISDN_DRV_TELES
+extern void teles_init(void);
+#endif
+
+#ifdef CONFIG_ISDN_DRV_PCBIT
+extern void pcbit_init(void);
+#endif
+
+void isdn_cards_init(void)
+{
+#if CONFIG_ISDN_DRV_ICN
+ icn_init();
+#endif
+#if CONFIG_ISDN_DRV_TELES
+ teles_init();
+#endif
+#if CONFIG_ISDN_DRV_PCBIT
+ pcbit_init();
+#endif
+}
+
diff --git a/drivers/isdn/isdn_cards.h b/drivers/isdn/isdn_cards.h
new file mode 100644
index 000000000..5bece603b
--- /dev/null
+++ b/drivers/isdn/isdn_cards.h
@@ -0,0 +1,28 @@
+/* $Id: isdn_cards.h,v 1.1 1996/04/20 16:04:03 fritz Exp $
+ *
+ * Linux ISDN subsystem, initialization for non-modularized drivers.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_cards.h,v $
+ * Revision 1.1 1996/04/20 16:04:03 fritz
+ * Initial revision
+ *
+ */
+
+extern void isdn_cards_init(void);
+
diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c
new file mode 100644
index 000000000..04415c797
--- /dev/null
+++ b/drivers/isdn/isdn_common.c
@@ -0,0 +1,2160 @@
+/* $Id: isdn_common.c,v 1.23 1996/06/25 18:35:38 fritz Exp $
+ *
+ * Linux ISDN subsystem, common used functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_common.c,v $
+ * Revision 1.23 1996/06/25 18:35:38 fritz
+ * Fixed bogus memory access in isdn_set_allcfg().
+ *
+ * Revision 1.22 1996/06/24 17:37:37 fritz
+ * Bugfix: isdn_timer_ctrl() did restart timer, even if it
+ * was already running.
+ * lowlevel driver locking did use wrong parameters.
+ *
+ * Revision 1.21 1996/06/15 14:58:20 fritz
+ * Added version signatures for data structures used
+ * by userlevel programs.
+ *
+ * Revision 1.20 1996/06/12 16:01:49 fritz
+ * Bugfix: Remote B-channel hangup sometimes did not result
+ * in a NO CARRIER on tty.
+ *
+ * Revision 1.19 1996/06/11 14:52:04 hipp
+ * minor bugfix in isdn_writebuf_skb_stub()
+ *
+ * Revision 1.18 1996/06/06 14:51:51 fritz
+ * Changed to support DTMF decoding on audio playback also.
+ *
+ * Revision 1.17 1996/06/05 02:24:10 fritz
+ * Added DTMF decoder for audio mode.
+ *
+ * Revision 1.16 1996/06/03 20:09:05 fritz
+ * Bugfix: called wrong function pointer for locking in
+ * isdn_get_free_channel().
+ *
+ * Revision 1.15 1996/05/31 01:10:54 fritz
+ * Bugfixes:
+ * Lowlevel modules did not get locked correctly.
+ * Did show wrong revision when initializing.
+ * Minor fixes in ioctl code.
+ * sk_buff did not get freed, if error in writebuf_stub.
+ *
+ * Revision 1.14 1996/05/18 01:36:55 fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.13 1996/05/17 15:43:30 fritz
+ * Bugfix: decrement of rcvcount in readbchan() corrected.
+ *
+ * Revision 1.12 1996/05/17 03:55:43 fritz
+ * Changed DLE handling for audio receive.
+ * Some cleanup.
+ * Added display of isdn_audio_revision.
+ *
+ * Revision 1.11 1996/05/11 21:51:32 fritz
+ * Changed queue management to use sk_buffs.
+ *
+ * Revision 1.10 1996/05/10 08:49:16 fritz
+ * Checkin before major changes of tty-code.
+ *
+ * Revision 1.9 1996/05/07 09:19:41 fritz
+ * Adapted to changes in isdn_tty.c
+ *
+ * Revision 1.8 1996/05/06 11:34:51 hipp
+ * fixed a few bugs
+ *
+ * Revision 1.7 1996/05/02 03:55:17 fritz
+ * Bugfixes:
+ * - B-channel connect message for modem devices
+ * sometimes did not result in a CONNECT-message.
+ * - register_isdn did not check for driverId-conflicts.
+ *
+ * Revision 1.6 1996/04/30 20:57:21 fritz
+ * Commit test
+ *
+ * Revision 1.5 1996/04/20 16:19:07 fritz
+ * Changed slow timer handlers to increase accuracy.
+ * Added statistic information for usage by xisdnload.
+ * Fixed behaviour of isdnctrl-device on non-blocked io.
+ * Fixed all io to go through generic writebuf-function without
+ * bypassing. Same for incoming data.
+ * Fixed bug: Last channel had been unusable.
+ * Fixed kfree of tty xmit_buf on ppp initialization failure.
+ *
+ * Revision 1.4 1996/02/11 02:33:26 fritz
+ * Fixed bug in main timer-dispatcher.
+ * Bugfix: Lot of tty-callbacks got called regardless of the events already
+ * been handled by network-devices.
+ * Changed ioctl-names.
+ *
+ * Revision 1.3 1996/01/22 05:16:11 fritz
+ * Changed ioctl-names.
+ * Fixed bugs in isdn_open and isdn_close regarding PPP_MINOR.
+ *
+ * Revision 1.2 1996/01/21 16:52:40 fritz
+ * Support for sk_buffs added, changed header-stuffing.
+ *
+ * Revision 1.1 1996/01/09 04:12:52 fritz
+ * Initial revision
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#ifndef __GENKSYMS__ /* Don't want genksyms report unneeded structs */
+#include <linux/isdn.h>
+#endif
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#endif
+#include "isdn_cards.h"
+
+/* Debugflags */
+#undef ISDN_DEBUG_STATCALLB
+#define NEW_ISDN_TIMER_CTRL
+
+isdn_dev *dev = (isdn_dev *) 0;
+
+static int has_exported = 0;
+static char *isdn_revision = "$Revision: 1.23 $";
+
+extern char *isdn_net_revision;
+extern char *isdn_tty_revision;
+#ifdef CONFIG_ISDN_PPP
+extern char *isdn_ppp_revision;
+#else
+static char *isdn_ppp_revision = ": none $";
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+extern char *isdn_audio_revision;
+#else
+static char *isdn_audio_revision = ": none $";
+#endif
+
+void isdn_MOD_INC_USE_COUNT(void)
+{
+ MOD_INC_USE_COUNT;
+}
+
+void isdn_MOD_DEC_USE_COUNT(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+void isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+{
+ int dumpc;
+
+ printk(KERN_DEBUG "%s(%d) ", s, len);
+ for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++)
+ printk(" %02x", *p++);
+ printk("\n");
+}
+#endif
+
+static __inline void isdn_trash_skb(struct sk_buff *skb, int rw)
+{
+ skb->free = 1;
+ kfree_skb(skb, rw);
+}
+
+static void isdn_free_queue(struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (skb_queue_len(queue))
+ while ((skb = skb_dequeue(queue)))
+ isdn_trash_skb(skb, FREE_READ);
+ restore_flags(flags);
+}
+
+int isdn_dc2minor(int di, int ch)
+{
+ int i;
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (dev->chanmap[i] == ch && dev->drvmap[i] == di)
+ return i;
+ return -1;
+}
+
+static int isdn_timer_cnt1 = 0;
+static int isdn_timer_cnt2 = 0;
+
+static void isdn_timer_funct(ulong dummy)
+{
+ int tf = dev->tflags;
+
+ if (tf & ISDN_TIMER_FAST) {
+ if (tf & ISDN_TIMER_MODEMREAD)
+ isdn_tty_readmodem();
+ if (tf & ISDN_TIMER_MODEMPLUS)
+ isdn_tty_modem_escape();
+ if (tf & ISDN_TIMER_MODEMXMIT)
+ isdn_tty_modem_xmit();
+ }
+ if (tf & ISDN_TIMER_SLOW) {
+ if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) {
+ isdn_timer_cnt1 = 0;
+ if (tf & ISDN_TIMER_NETDIAL)
+ isdn_net_dial();
+ }
+ if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
+ isdn_timer_cnt2 = 0;
+ if (tf & ISDN_TIMER_NETHANGUP)
+ isdn_net_autohup();
+ if (tf & ISDN_TIMER_MODEMRING)
+ isdn_tty_modem_ring();
+#if (defined CONFIG_ISDN_PPP ) && (defined ISDN_CONFIG_MPP)
+ if (tf & ISDN_TIMER_IPPP)
+ isdn_ppp_timer_timeout();
+#endif
+ }
+ }
+ if (tf) {
+ int flags;
+
+ save_flags(flags);
+ cli();
+ del_timer(&dev->timer);
+#ifndef NEW_ISDN_TIMER_CTRL
+ dev->timer.function = isdn_timer_funct;
+#endif
+ dev->timer.expires = jiffies + ISDN_TIMER_RES;
+ add_timer(&dev->timer);
+ restore_flags(flags);
+ }
+}
+
+void isdn_timer_ctrl(int tf, int onoff)
+{
+ int flags;
+
+ save_flags(flags);
+ cli();
+ if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) {
+ /* If the slow-timer wasn't activated until now */
+ isdn_timer_cnt1 = 0;
+ isdn_timer_cnt2 = 0;
+ }
+ if (onoff)
+ dev->tflags |= tf;
+ else
+ dev->tflags &= ~tf;
+#ifdef NEW_ISDN_TIMER_CTRL
+ if (dev->tflags) {
+ if (!del_timer(&dev->timer)) /* del_timer is 1, when active */
+ dev->timer.expires = jiffies + ISDN_TIMER_RES;
+ add_timer(&dev->timer);
+ }
+#else
+ if (dev->tflags) {
+ del_timer(&dev->timer);
+ dev->timer.function = isdn_timer_funct;
+ dev->timer.expires = jiffies + ISDN_TIMER_RES;
+ add_timer(&dev->timer);
+ }
+#endif
+ restore_flags(flags);
+}
+
+/*
+ * Receive a packet from B-Channel. (Called from low-level-module)
+ */
+static void isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
+{
+ ulong flags;
+ int i;
+ int midx;
+#ifdef CONFIG_ISDN_AUDIO
+ int ifmt;
+#endif
+ modem_info *info;
+
+ if ((i = isdn_dc2minor(di,channel))==-1) {
+ isdn_trash_skb(skb, FREE_READ);
+ return;
+ }
+ /* Update statistics */
+ dev->ibytes[i] += skb->len;
+ /* First, try to deliver data to network-device */
+ if (isdn_net_rcv_skb(i, skb))
+ return;
+ /* No network-device found, deliver to tty or raw-channel */
+ skb->free = 1;
+ if (skb->len) {
+ if ((midx = dev->m_idx[i])<0) {
+ /* if midx is invalid, drop packet */
+ isdn_trash_skb(skb, FREE_READ);
+ return;
+ }
+ info = &dev->mdm.info[midx];
+#ifdef CONFIG_ISDN_AUDIO
+ ifmt = 1;
+
+ if (info->vonline)
+ isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+#endif
+ if ((info->online < 2) &&
+ (!(info->vonline & 1))) {
+ /* If Modem not listening, drop data */
+ isdn_trash_skb(skb, FREE_READ);
+ return;
+ }
+ if (info->emu.mdmreg[13] & 2)
+ /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+ if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
+ skb_pull(skb,4);
+ /* The users field of an sk_buff is used in a special way
+ * with tty's incoming data:
+ * users is set to the number of DLE codes when in audio mode.
+ */
+ skb->users = 0;
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 1) {
+ /* voice conversion/compression */
+ switch (info->emu.vpar[3]) {
+ case 2:
+ case 3:
+ case 4:
+ /* adpcm
+ * Since compressed data takes less
+ * space, we can overwrite the buffer.
+ */
+ skb_trim(skb,isdn_audio_xlaw2adpcm(info->adpcmr,
+ ifmt,
+ skb->data,
+ skb->data,
+ skb->len));
+ break;
+ case 5:
+ /* a-law */
+ if (!ifmt)
+ isdn_audio_ulaw2alaw(skb->data,skb->len);
+ break;
+ case 6:
+ /* u-law */
+ if (ifmt)
+ isdn_audio_alaw2ulaw(skb->data,skb->len);
+ break;
+ }
+ skb->users = isdn_tty_countDLE(skb->data,skb->len);
+ }
+#endif
+ /* Try to deliver directly via tty-flip-buf if queue is empty */
+ save_flags(flags);
+ cli();
+ if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+ if (isdn_tty_try_read(info, skb)) {
+ restore_flags(flags);
+ return;
+ }
+ /* Direct deliver failed or queue wasn't empty.
+ * Queue up for later dequeueing via timer-irq.
+ */
+ __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
+ dev->drv[di]->rcvcount[channel] += (skb->len + skb->users);
+ restore_flags(flags);
+ /* Schedule dequeuing */
+ if ((dev->modempoll) && (info->rcvsched))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+ wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+ } else
+ isdn_trash_skb(skb, FREE_READ);
+}
+
+void isdn_all_eaz(int di, int ch)
+{
+ isdn_ctrl cmd;
+
+ cmd.driver = di;
+ cmd.arg = ch;
+ cmd.command = ISDN_CMD_SETEAZ;
+ cmd.num[0] = '\0';
+ (void) dev->drv[di]->interface->command(&cmd);
+}
+
+static int isdn_status_callback(isdn_ctrl * c)
+{
+ int di;
+ int mi;
+ ulong flags;
+ int i;
+ int r;
+ modem_info *info;
+ isdn_ctrl cmd;
+
+ di = c->driver;
+ i = isdn_dc2minor(di, c->arg);
+ switch (c->command) {
+ case ISDN_STAT_BSENT:
+ if (i<0)
+ return -1;
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ if (isdn_net_stat_callback(i, c->command))
+ return 0;
+ isdn_tty_bsent(di, c->arg);
+ wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+ break;
+ case ISDN_STAT_STAVAIL:
+ save_flags(flags);
+ cli();
+ dev->drv[di]->stavail += c->arg;
+ restore_flags(flags);
+ wake_up_interruptible(&dev->drv[di]->st_waitq);
+ break;
+ case ISDN_STAT_RUN:
+ dev->drv[di]->running = 1;
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (dev->drvmap[i] == di)
+ isdn_all_eaz(di, dev->chanmap[i]);
+ break;
+ case ISDN_STAT_STOP:
+ dev->drv[di]->running = 0;
+ break;
+ case ISDN_STAT_ICALL:
+ if (i<0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->num);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_HANGUP;
+ dev->drv[di]->interface->command(&cmd);
+ return 0;
+ }
+
+ /* Try to find a network-interface which will accept incoming call */
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_LOCK;
+ dev->drv[di]->interface->command(&cmd);
+ r = isdn_net_find_icall(di, c->arg, i, c->num);
+ switch (r) {
+ case 0:
+ /* No network-device replies. Schedule RING-message to
+ * tty and set RI-bit of modem-status.
+ */
+ if ((mi = isdn_tty_find_icall(di, c->arg, c->num)) >= 0) {
+ info = &dev->mdm.info[mi];
+ info->msr |= UART_MSR_RI;
+ isdn_tty_modem_result(2, info);
+ isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+ return 0;
+ } else if (dev->drv[di]->reject_bus) {
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_HANGUP;
+ dev->drv[di]->interface->command(&cmd);
+ }
+ break;
+ case 1:
+ /* Schedule connection-setup */
+ isdn_net_dial();
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_ACCEPTD;
+ dev->drv[di]->interface->command(&cmd);
+ return 0;
+ break;
+ case 2: /* For calling back, first reject incoming call ... */
+ case 3: /* Interface found, but down, reject call actively */
+ printk(KERN_INFO "isdn: Rejecting Call\n");
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_HANGUP;
+ dev->drv[di]->interface->command(&cmd);
+ if (r == 3)
+ break;
+ /* Fall through */
+ case 4:
+ /* ... then start callback. */
+ isdn_net_dial();
+ return 0;
+ }
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_UNLOCK;
+ dev->drv[di]->interface->command(&cmd);
+ return 0;
+ break;
+ case ISDN_STAT_CINF:
+ if (i<0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->num);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ if (strcmp(c->num, "0"))
+ isdn_net_stat_callback(i, c->command);
+ break;
+ case ISDN_STAT_CAUSE:
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->num);
+#endif
+ printk(KERN_INFO "isdn: cause: %s\n", c->num);
+ break;
+ case ISDN_STAT_DCONN:
+ if (i<0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ /* Find any network-device, waiting for D-channel setup */
+ if (isdn_net_stat_callback(i, c->command))
+ break;
+
+ if ((mi = dev->m_idx[i]) >= 0) {
+ /* If any tty has just dialed-out, setup B-Channel */
+ info = &dev->mdm.info[mi];
+ if (info->flags &
+ (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+ if (info->dialing == 1) {
+ info->dialing = 2;
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_ACCEPTB;
+ dev->drv[di]->interface->command(&cmd);
+ return 0;
+ }
+ }
+ }
+ break;
+ case ISDN_STAT_DHUP:
+ if (i<0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ dev->drv[di]->flags &= ~(1 << (c->arg));
+ isdn_info_update();
+ /* Signal hangup to network-devices */
+ if (isdn_net_stat_callback(i, c->command))
+ break;
+ if ((mi = dev->m_idx[i]) >= 0) {
+ /* Signal hangup to tty-device */
+ info = &dev->mdm.info[mi];
+ if (info->flags &
+ (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+ if (info->dialing == 1) {
+ info->dialing = 0;
+ isdn_tty_modem_result(7, info);
+ }
+ if (info->online)
+ isdn_tty_modem_result(3, info);
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+#endif
+ isdn_tty_modem_hup(info);
+ return 0;
+ }
+ }
+ break;
+ case ISDN_STAT_BCONN:
+ if (i<0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+#endif
+ /* Signal B-channel-connect to network-devices */
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ dev->drv[di]->flags |= (1 << (c->arg));
+ isdn_info_update();
+ if (isdn_net_stat_callback(i, c->command))
+ break;
+ if ((mi = dev->m_idx[i]) >= 0) {
+ /* Schedule CONNECT-Message to any tty, waiting for it and
+ * set DCD-bit of its modem-status.
+ */
+ info = &dev->mdm.info[mi];
+ if (info->flags &
+ (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+ info->msr |= UART_MSR_DCD;
+ if (info->dialing)
+ info->dialing = 0;
+ info->rcvsched = 1;
+ if (USG_MODEM(dev->usage[i]))
+ isdn_tty_modem_result(5, info);
+ if (USG_VOICE(dev->usage[i]))
+ isdn_tty_modem_result(11, info);
+ }
+ }
+ break;
+ case ISDN_STAT_BHUP:
+ if (i<0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ dev->drv[di]->flags &= ~(1 << (c->arg));
+ isdn_info_update();
+ if ((mi = dev->m_idx[i]) >= 0) {
+ /* Signal hangup to tty-device, schedule NO CARRIER-message */
+ info = &dev->mdm.info[mi];
+ if (info->flags &
+ (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+ if (info->msr & UART_MSR_DCD)
+ isdn_tty_modem_result(3, info);
+ info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+#endif
+ isdn_tty_modem_hup(info);
+ }
+ }
+ break;
+ case ISDN_STAT_NODCH:
+ if (i<0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ if (isdn_net_stat_callback(i, c->command))
+ break;
+ if ((mi = dev->m_idx[i]) >= 0) {
+ info = &dev->mdm.info[mi];
+ if (info->flags &
+ (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+ if (info->dialing) {
+ info->dialing = 0;
+ isdn_tty_modem_result(6, info);
+ }
+ info->msr &= ~UART_MSR_DCD;
+ if (info->online) {
+ isdn_tty_modem_result(3, info);
+ info->online = 0;
+ }
+ }
+ }
+ break;
+ case ISDN_STAT_ADDCH:
+ break;
+ case ISDN_STAT_UNLOAD:
+ save_flags(flags);
+ cli();
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (dev->drvmap[i] == di) {
+ dev->drvmap[i] = -1;
+ dev->chanmap[i] = -1;
+ }
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+
+ if (info->isdn_driver == di) {
+ info->isdn_driver = -1;
+ info->isdn_channel = -1;
+ if (info->online) {
+ isdn_tty_modem_result(3, info);
+ isdn_tty_modem_hup(info);
+ }
+ }
+ }
+ dev->drivers--;
+ dev->channels -= dev->drv[di]->channels;
+ kfree(dev->drv[di]->rcverr);
+ kfree(dev->drv[di]->rcvcount);
+ for (i = 0; i < dev->drv[di]->channels; i++)
+ isdn_free_queue(&dev->drv[di]->rpqueue[i]);
+ kfree(dev->drv[di]->rpqueue);
+ kfree(dev->drv[di]->rcv_waitq);
+ kfree(dev->drv[di]->snd_waitq);
+ kfree(dev->drv[di]);
+ dev->drv[di] = NULL;
+ dev->drvid[di][0] = '\0';
+ isdn_info_update();
+ restore_flags(flags);
+ return 0;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Get integer from char-pointer, set pointer to end of number
+ */
+int isdn_getnum(char **p)
+{
+ int v = -1;
+
+ while (*p[0] >= '0' && *p[0] <= '9')
+ v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
+ return v;
+}
+
+#define DLE 0x10
+
+/*
+ * isdn_readbchan() tries to get data from the read-queue.
+ * It MUST be called with interrupts off.
+ *
+ * I hope I got the EFAULT error path right -AK.
+ */
+int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user)
+{
+ int left;
+ int count;
+ int count_pull;
+ int count_put;
+ int dflag;
+ struct sk_buff *skb;
+ u_char *cp;
+ int ret = 0;
+
+ if (!dev->drv[di])
+ return 0;
+ if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) {
+ if (user)
+ interruptible_sleep_on(&dev->drv[di]->rcv_waitq[channel]);
+ else
+ return 0;
+ }
+ left = MIN(len, dev->drv[di]->rcvcount[channel]);
+ cp = buf;
+ count = 0;
+ while (left) {
+ ret = 0;
+ if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+ break;
+ if (skb->lock)
+ break;
+ skb->lock = 1;
+ if (skb->users) {
+ /* users is the count of DLE's in
+ * this buff when in voice mode.
+ */
+ char *p = skb->data;
+ unsigned long DLEmask = (1 << channel);
+
+ dflag = 0;
+ count_pull = count_put = 0;
+ while ((count_pull < skb->len) && (left-- > 0)) {
+ if (dev->drv[di]->DLEflag & DLEmask) {
+ if (user) {
+ ret = put_user(DLE,cp);
+ cp++;
+ if (ret) break;
+ } else
+ *cp++ = DLE;
+ dev->drv[di]->DLEflag &= ~DLEmask;
+ } else {
+ if (user) {
+ ret = put_user(*p,cp);
+ if (ret) break;
+ cp++;
+ } else
+ *cp++ = *p;
+ if (*p == DLE) {
+ dev->drv[di]->DLEflag |= DLEmask;
+ skb->users--;
+ }
+ p++;
+ count_pull++;
+ }
+ count_put++;
+ }
+ if (count_pull >= skb->len)
+ dflag = 1;
+ } else {
+ /* No DLE's in buff, so simply copy it */
+ dflag = 1;
+ if ((count_pull = skb->len) > left) {
+ count_pull = left;
+ dflag = 0;
+ }
+ count_put = count_pull;
+ ret = 0;
+ if (user)
+ ret = copy_to_user(cp, skb->data, count_put);
+ else
+ memcpy(cp, skb->data, count_put);
+ count_put -= ret;
+ cp += count_put;
+ left -= count_put;
+ }
+ count += count_put;
+ if (fp) {
+ memset(fp, 0, count_put);
+ fp += count_put;
+ }
+ if (dflag) {
+ /* We got all the data in this buff.
+ * Now we can dequeue it.
+ */
+ if (fp)
+ *(fp - 1) = 0xff;
+ skb->lock = 0;
+ skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+ isdn_trash_skb(skb, FREE_READ);
+ } else {
+ /* Not yet emptied this buff, so it
+ * must stay in the queue, for further calls
+ * but we pull off the data we got until now.
+ */
+ skb_pull(skb,count_pull);
+ skb->lock = 0;
+ }
+ dev->drv[di]->rcvcount[channel] -= count_put;
+ }
+ return ret ? -EFAULT : count;
+}
+
+static __inline int isdn_minor2drv(int minor)
+{
+ return (dev->drvmap[minor]);
+}
+
+static __inline int isdn_minor2chan(int minor)
+{
+ return (dev->chanmap[minor]);
+}
+
+#define INF_DV 0x01 /* Data version for /dev/isdninfo */
+
+static char *
+ isdn_statstr(void)
+{
+ static char istatbuf[2048];
+ char *p;
+ int i;
+
+ sprintf(istatbuf, "idmap:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\nchmap:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%d ", dev->chanmap[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\ndrmap:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%d ", dev->drvmap[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\nusage:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%d ", dev->usage[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\nflags:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+ if (dev->drv[i]) {
+ sprintf(p, "%ld ", dev->drv[i]->flags);
+ p = istatbuf + strlen(istatbuf);
+ } else {
+ sprintf(p, "? ");
+ p = istatbuf + strlen(istatbuf);
+ }
+ }
+ sprintf(p, "\nphone:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%s ", dev->num[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\n");
+ return istatbuf;
+}
+
+/* Module interface-code */
+
+void isdn_info_update(void)
+{
+ infostruct *p = dev->infochain;
+
+ while (p) {
+ *(p->private) = 1;
+ p = (infostruct *) p->next;
+ }
+ wake_up_interruptible(&(dev->info_waitq));
+}
+
+static long isdn_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ uint minor = MINOR(inode->i_rdev);
+ int len = 0;
+ ulong flags;
+ int drvidx;
+ int chidx;
+
+ if (minor == ISDN_MINOR_STATUS) {
+ char *p;
+ if (!file->private_data) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ interruptible_sleep_on(&(dev->info_waitq));
+ }
+ p = isdn_statstr();
+ file->private_data = 0;
+ if ((len = strlen(p)) <= count) {
+ if (copy_to_user(buf, p, len))
+ return -EFAULT;
+ file->f_pos += len;
+ return len;
+ }
+ return 0;
+ }
+ if (!dev->drivers)
+ return -ENODEV;
+ if (minor < ISDN_MINOR_CTRL) {
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0)
+ return -ENODEV;
+ if (!dev->drv[drvidx]->running)
+ return -ENODEV;
+ chidx = isdn_minor2chan(minor);
+ save_flags(flags);
+ cli();
+ len = isdn_readbchan(drvidx, chidx, buf, 0, count, 1);
+ file->f_pos += len;
+ restore_flags(flags);
+ return len;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+ if (drvidx < 0)
+ return -ENODEV;
+ if (!dev->drv[drvidx]->stavail) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
+ }
+ if (dev->drv[drvidx]->interface->readstat)
+ len = dev->drv[drvidx]->interface->
+ readstat(buf, MIN(count, dev->drv[drvidx]->stavail),
+ 1, drvidx, isdn_minor2chan(minor));
+ else
+ len = 0;
+ save_flags(flags);
+ cli();
+ if (len)
+ dev->drv[drvidx]->stavail -= len;
+ else
+ dev->drv[drvidx]->stavail = 0;
+ restore_flags(flags);
+ file->f_pos += len;
+ return len;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX)
+ return (isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count));
+#endif
+ return -ENODEV;
+}
+
+static long long isdn_lseek(struct inode *inode, struct file *file, long long offset, int orig)
+{
+ return -ESPIPE;
+}
+
+static long isdn_write(struct inode *inode, struct file *file,
+ const char *buf, unsigned long count)
+{
+ uint minor = MINOR(inode->i_rdev);
+ int drvidx;
+ int chidx;
+
+ if (minor == ISDN_MINOR_STATUS)
+ return -EPERM;
+ if (!dev->drivers)
+ return -ENODEV;
+ if (minor < ISDN_MINOR_CTRL) {
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0)
+ return -ENODEV;
+ if (!dev->drv[drvidx]->running)
+ return -ENODEV;
+ chidx = isdn_minor2chan(minor);
+ while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count)
+ interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
+ return count;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+ if (drvidx < 0)
+ return -ENODEV;
+ /*
+ * We want to use the isdnctrl device to load the firmware
+ *
+ if (!dev->drv[drvidx]->running)
+ return -ENODEV;
+ */
+ if (dev->drv[drvidx]->interface->writecmd)
+ return (dev->drv[drvidx]->interface->
+ writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor)));
+ else
+ return count;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX)
+ return (isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count));
+#endif
+ return -ENODEV;
+}
+
+static int isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
+{
+ uint minor = MINOR(inode->i_rdev);
+ int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+
+ if (minor == ISDN_MINOR_STATUS) {
+ if (file->private_data)
+ return 1;
+ else {
+ if (st)
+ select_wait(&(dev->info_waitq), st);
+ return 0;
+ }
+ }
+ if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
+ if (drvidx < 0)
+ return -ENODEV;
+ if (dev->drv[drvidx]->stavail)
+ return 1;
+ else {
+ if (st)
+ select_wait(&(dev->drv[drvidx]->st_waitq), st);
+ return 0;
+ }
+ return 1;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX)
+ return (isdn_ppp_select(minor - ISDN_MINOR_PPP, file, type, st));
+#endif
+ return -ENODEV;
+}
+
+static int isdn_set_allcfg(char *src)
+{
+ int ret;
+ int i;
+ ulong flags;
+ char buf[1024];
+ isdn_net_ioctl_cfg cfg;
+ isdn_net_ioctl_phone phone;
+
+ if ((ret = isdn_net_rmall()))
+ return ret;
+ save_flags(flags);
+ cli();
+ ret = get_user(i, src);
+ if (ret)
+ goto out;
+ src += sizeof(int);
+ while (i) {
+ char *c;
+ char *c2;
+
+ if(copy_from_user((char *) &cfg, src, sizeof(cfg)))
+ goto fault;
+ src += sizeof(cfg);
+ if (!isdn_net_new(cfg.name, NULL)) {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if ((ret = isdn_net_setcfg(&cfg)))
+ goto out;
+ if(copy_from_user(buf, src, sizeof(buf)))
+ goto fault;
+ src += sizeof(buf);
+ c = buf;
+ while (*c) {
+ if ((c2 = strchr(c, ' ')))
+ *c2++ = '\0';
+ strcpy(phone.phone, c);
+ strcpy(phone.name, cfg.name);
+ phone.outgoing = 0;
+ if ((ret = isdn_net_addphone(&phone)))
+ goto fault;
+ if (c2)
+ c = c2;
+ else
+ c += strlen(c);
+ }
+ if(copy_from_user(buf, src, sizeof(buf)))
+ goto fault;
+ src += sizeof(buf);
+ c = buf;
+ while (*c) {
+ if ((c2 = strchr(c, ' ')))
+ *c2++ = '\0';
+ strcpy(phone.phone, c);
+ strcpy(phone.name, cfg.name);
+ phone.outgoing = 1;
+ if ((ret = isdn_net_addphone(&phone)))
+ goto out;
+ if (c2)
+ c = c2;
+ else
+ c += strlen(c);
+ }
+ i--;
+ }
+out:
+ restore_flags(flags);
+ return ret;
+fault:
+ restore_flags(flags);
+ return -EFAULT;
+}
+
+static int isdn_get_allcfg(char *dest)
+{
+ isdn_net_ioctl_cfg cfg;
+ isdn_net_ioctl_phone phone;
+ isdn_net_dev *p;
+ ulong flags;
+ int ret = 0;
+
+ /* Walk through netdev-chain */
+ save_flags(flags);
+ cli();
+ p = dev->netdev;
+ while (p) {
+ strcpy(cfg.eaz, p->local.msn);
+ cfg.exclusive = p->local.exclusive;
+ if (p->local.pre_device >= 0) {
+ sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device],
+ p->local.pre_channel);
+ } else
+ cfg.drvid[0] = '\0';
+ cfg.onhtime = p->local.onhtime;
+ cfg.charge = p->local.charge;
+ cfg.l2_proto = p->local.l2_proto;
+ cfg.l3_proto = p->local.l3_proto;
+ cfg.p_encap = p->local.p_encap;
+ cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
+ cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
+ cfg.chargehup = (p->local.hupflags & 4) ? 1 : 0;
+ cfg.ihup = (p->local.hupflags & 8) ? 1 : 0;
+ ret = 0;
+ ret += copy_to_user(dest, p->local.name, 10);
+ dest += 10;
+ ret += copy_to_user(dest, (char *) &cfg, sizeof(cfg));
+ dest += sizeof(cfg);
+ strcpy(phone.name, p->local.name);
+ phone.outgoing = 0;
+ if (ret)
+ break;
+ if ((ret = isdn_net_getphones(&phone, dest)) < 0)
+ break;
+ else
+ dest += ret;
+ strcpy(phone.name, p->local.name);
+ phone.outgoing = 1;
+ if ((ret = isdn_net_getphones(&phone, dest)) < 0)
+ break;
+ else
+ dest += ret;
+ p = p->next;
+ }
+ restore_flags(flags);
+ return ret;
+}
+
+static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ uint minor = MINOR(inode->i_rdev);
+ isdn_ctrl c;
+ int drvidx;
+ int chidx;
+ int ret = 0;
+ char *s;
+ char name[10];
+ char bname[21];
+ isdn_ioctl_struct iocts;
+ isdn_net_ioctl_phone phone;
+ isdn_net_ioctl_cfg cfg;
+
+ if (minor == ISDN_MINOR_STATUS) {
+ switch (cmd) {
+ case IIOCGETDVR:
+ return(TTY_DV +
+ (NET_DV << 8) +
+ (INF_DV << 16));
+ case IIOCGETCPS:
+ if (arg) {
+ ulong *p = (ulong *)arg;
+ int i;
+ for (i = 0;i<ISDN_MAX_CHANNELS;i++) {
+ ret = put_user(dev->ibytes[i],p);
+ if (ret) break;
+ p++;
+ ret = put_user(dev->obytes[i],p);
+ p++;
+ if (ret) break;
+ }
+ return ret;
+ } else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (!dev->drivers)
+ return -ENODEV;
+ if (minor < ISDN_MINOR_CTRL) {
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0)
+ return -ENODEV;
+ chidx = isdn_minor2chan(minor);
+ if (!dev->drv[drvidx]->running)
+ return -ENODEV;
+ return 0;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ switch (cmd) {
+#ifdef CONFIG_NETDEVICES
+ case IIOCNETAIF:
+ /* Add a network-interface */
+ if (arg) {
+ if(copy_from_user(name, (char *) arg, sizeof(name)))
+ return -EFAULT;
+ s = name;
+ } else
+ s = NULL;
+ if ((s = isdn_net_new(s, NULL))) {
+ return copy_to_user((char *) arg, s, strlen(s) + 1) ? -EFAULT : ret;
+ } else
+ return -ENODEV;
+ case IIOCNETASL:
+ /* Add a slave to a network-interface */
+ if (arg) {
+ if(copy_from_user(bname, (char *) arg, sizeof(bname)))
+ return -EFAULT;
+ } else
+ return -EINVAL;
+ if ((s = isdn_net_newslave(bname))) {
+ return copy_to_user((char *) arg, s, strlen(s) + 1) ? -EFAULT : 0;
+ } else
+ return -ENODEV;
+ case IIOCNETDIF:
+ /* Delete a network-interface */
+ if (arg) {
+ ret = copy_from_user(name, (char *) arg, sizeof(name));
+ return ret ? -EFAULT : isdn_net_rm(name);
+ } else
+ return -EINVAL;
+ case IIOCNETSCF:
+ /* Set configurable parameters of a network-interface */
+ if (arg) {
+ ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg));
+ return ret ? -EFAULT : isdn_net_setcfg(&cfg);
+ } else
+ return -EINVAL;
+ case IIOCNETGCF:
+ /* Get configurable parameters of a network-interface */
+ if (arg) {
+ if(copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
+ return -EFAULT;
+ if (!(ret = isdn_net_getcfg(&cfg))) {
+ if(copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
+ return -EFAULT;
+ }
+ return ret;
+ } else
+ return -EINVAL;
+ case IIOCNETANM:
+ /* Add a phone-number to a network-interface */
+ if (arg) {
+ ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
+ return ret ? -EFAULT : isdn_net_addphone(&phone);
+ } else
+ return -EINVAL;
+ case IIOCNETGNM:
+ /* Get list of phone-numbers of a network-interface */
+ if (arg) {
+ ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
+ return ret ? -EFAULT : isdn_net_getphones(&phone, (char *) arg);
+ } else
+ return -EINVAL;
+ case IIOCNETDNM:
+ /* Delete a phone-number of a network-interface */
+ if (arg) {
+ ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
+ return ret ? -EFAULT : isdn_net_delphone(&phone);
+ } else
+ return -EINVAL;
+ case IIOCNETDIL:
+ /* Force dialing of a network-interface */
+ if (arg) {
+ ret = copy_from_user(name, (char *) arg, sizeof(name));
+ return ret ? -EFAULT : isdn_net_force_dial(name);
+ } else
+ return -EINVAL;
+#ifdef CONFIG_ISDN_PPP
+ case IIOCNETALN:
+ if (arg)
+ ret = copy_from_user(name,(char*)arg,sizeof(name));
+ else
+ return -EINVAL;
+ return ret ? -EFAULT : isdn_ppp_dial_slave(name);
+ case IIOCNETDLN:
+
+ if(arg) {
+ ret = copy_from_user(name,(char*)arg,sizeof(name));
+ } else
+ return -EINVAL;
+ return ret ? -EFAULT : isdn_ppp_hangup_slave(name);
+#endif
+ case IIOCNETHUP:
+ /* Force hangup of a network-interface */
+ if (arg) {
+ ret = copy_from_user(name, (char *) arg, sizeof(name));
+ return ret ? -EFAULT : isdn_net_force_hangup(name);
+ } else
+ return -EINVAL;
+ break;
+#endif /* CONFIG_NETDEVICES */
+ case IIOCSETVER:
+ dev->net_verbose = arg;
+ printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+ return 0;
+ case IIOCSETGST:
+ if (arg)
+ dev->global_flags |= ISDN_GLOBAL_STOPPED;
+ else
+ dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+ printk(KERN_INFO "isdn: Global Mode %s\n",
+ (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+ return 0;
+ case IIOCSETBRJ:
+ drvidx = -1;
+ if (arg) {
+ int i;
+ char *p;
+ if(copy_from_user((char *) &iocts, (char *) arg,
+ sizeof(isdn_ioctl_struct)))
+ return -EFAULT;
+ if (strlen(iocts.drvid)) {
+ if ((p = strchr(iocts.drvid, ',')))
+ *p = 0;
+ drvidx = -1;
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+ drvidx = i;
+ break;
+ }
+ }
+ }
+ if (drvidx == -1)
+ return -ENODEV;
+ dev->drv[drvidx]->reject_bus = iocts.arg;
+ return 0;
+ case IIOCGETSET:
+ /* Get complete setup (all network-interfaces and profile-
+ settings of all tty-devices */
+ if (arg)
+ return (isdn_get_allcfg((char *) arg));
+ else
+ return -EINVAL;
+ break;
+ case IIOCSETSET:
+ /* Set complete setup (all network-interfaces and profile-
+ settings of all tty-devices */
+ if (arg)
+ return (isdn_set_allcfg((char *) arg));
+ else
+ return -EINVAL;
+ break;
+ case IIOCSIGPRF:
+ dev->profd = current;
+ return 0;
+ break;
+ case IIOCGETPRF:
+ /* Get all Modem-Profiles */
+ if (arg) {
+ char *p = (char *) arg;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if(copy_to_user(p, dev->mdm.info[i].emu.profile,
+ ISDN_MODEM_ANZREG))
+ return -EFAULT;
+ p += ISDN_MODEM_ANZREG;
+ if(copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
+ return -EFAULT;
+ p += ISDN_MSNLEN;
+ }
+ return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+ } else
+ return -EINVAL;
+ break;
+ case IIOCSETPRF:
+ /* Set all Modem-Profiles */
+ if (arg) {
+ char *p = (char *) arg;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if(copy_from_user(dev->mdm.info[i].emu.profile, p,
+ ISDN_MODEM_ANZREG))
+ return -EFAULT;
+ p += ISDN_MODEM_ANZREG;
+ if(copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))
+ return -EFAULT;
+ p += ISDN_MSNLEN;
+ }
+ return 0;
+ } else
+ return -EINVAL;
+ break;
+ case IIOCSETMAP:
+ case IIOCGETMAP:
+ /* Set/Get MSN->EAZ-Mapping for a driver */
+ if (arg) {
+ int i;
+ char *p;
+ char nstring[255];
+
+ ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+
+ if (ret) return -EFAULT;
+ if (strlen(iocts.drvid)) {
+ drvidx = -1;
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+ drvidx = i;
+ break;
+ }
+ } else
+ drvidx = 0;
+ if (drvidx == -1)
+ return -ENODEV;
+ if (cmd == IIOCSETMAP) {
+ ret = copy_from_user(nstring, (char *) iocts.arg, 255);
+ if (ret) return -EFAULT;
+ memset(dev->drv[drvidx]->msn2eaz, 0,
+ sizeof(dev->drv[drvidx]->msn2eaz));
+ p = strtok(nstring, ",");
+ i = 0;
+ while ((p) && (i < 10)) {
+ strcpy(dev->drv[drvidx]->msn2eaz[i++], p);
+ p = strtok(NULL, ",");
+ }
+ } else {
+ p = nstring;
+ for (i = 0; i < 10; i++)
+ p += sprintf(p, "%s%s",
+ strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+ dev->drv[drvidx]->msn2eaz[i] : "-",
+ (i < 9) ? "," : "\0");
+ if(copy_to_user((char *) iocts.arg, nstring, strlen(nstring) + 1))
+ return -EFAULT;
+ }
+ return 0;
+ } else
+ return -EINVAL;
+ case IIOCDBGVAR:
+ if (arg) {
+ return copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)) ? -EFAULT : 0;
+ } else
+ return -EINVAL;
+ break;
+ default:
+ if ((cmd&IIOCDRVCTL)==IIOCDRVCTL)
+ cmd = ((cmd>>_IOC_NRSHIFT)&_IOC_NRMASK)& ISDN_DRVIOCTL_MASK;
+ else
+ return -EINVAL;
+ if (arg) {
+ int i;
+ char *p;
+
+ ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+ if (ret)
+ return -EFAULT;
+ if (strlen(iocts.drvid)) {
+ if ((p = strchr(iocts.drvid, ',')))
+ *p = 0;
+ drvidx = -1;
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+ drvidx = i;
+ break;
+ }
+ } else
+ drvidx = 0;
+ if (drvidx == -1)
+ return -ENODEV;
+ c.driver = drvidx;
+ c.command = ISDN_CMD_IOCTL;
+ c.arg = cmd;
+ memcpy(c.num, (char *) &iocts.arg, sizeof(ulong));
+ ret = dev->drv[drvidx]->interface->command(&c);
+ memcpy((char *) &iocts.arg, c.num, sizeof(ulong));
+ return copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)) ? -EFAULT : 0;
+ } else
+ return -EINVAL;
+ }
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX)
+ return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg));
+#endif
+ return -ENODEV;
+}
+
+/*
+ * Open the device code.
+ * MOD_INC_USE_COUNT make sure that the driver memory is not freed
+ * while the device is in use.
+ */
+static int isdn_open(struct inode *ino, struct file *filep)
+{
+ uint minor = MINOR(ino->i_rdev);
+ int drvidx;
+ int chidx;
+ isdn_ctrl c;
+
+ if (minor == ISDN_MINOR_STATUS) {
+ infostruct *p;
+
+ if ((p = (infostruct *) kmalloc(sizeof(infostruct), GFP_KERNEL))) {
+ MOD_INC_USE_COUNT;
+ p->next = (char *) dev->infochain;
+ p->private = (char *) &(filep->private_data);
+ dev->infochain = p;
+ /* At opening we allow a single update */
+ filep->private_data = (char *) 1;
+ return 0;
+ } else
+ return -ENOMEM;
+ }
+ if (!dev->channels)
+ return -ENODEV;
+ if (minor < ISDN_MINOR_CTRL) {
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0)
+ return -ENODEV;
+ chidx = isdn_minor2chan(minor);
+ if (!dev->drv[drvidx]->running)
+ return -ENODEV;
+ if (!(dev->drv[drvidx]->flags & (1 << chidx)))
+ return -ENODEV;
+ c.command = ISDN_CMD_LOCK;
+ c.driver = drvidx;
+ (void) dev->drv[drvidx]->interface->command(&c);
+ MOD_INC_USE_COUNT;
+ return 0;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+ if (drvidx < 0)
+ return -ENODEV;
+ c.command = ISDN_CMD_LOCK;
+ c.driver = drvidx;
+ MOD_INC_USE_COUNT;
+ (void) dev->drv[drvidx]->interface->command(&c);
+ return 0;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX) {
+ int ret;
+ if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
+ MOD_INC_USE_COUNT;
+ return ret;
+ }
+#endif
+ return -ENODEV;
+}
+
+static void isdn_close(struct inode *ino, struct file *filep)
+{
+ uint minor = MINOR(ino->i_rdev);
+ int drvidx;
+ isdn_ctrl c;
+
+ MOD_DEC_USE_COUNT;
+ if (minor == ISDN_MINOR_STATUS) {
+ infostruct *p = dev->infochain;
+ infostruct *q = NULL;
+ while (p) {
+ if (p->private == (char *) &(filep->private_data)) {
+ if (q)
+ q->next = p->next;
+ else
+ dev->infochain = (infostruct *) (p->next);
+ return;
+ }
+ q = p;
+ p = (infostruct *) (p->next);
+ }
+ printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
+ return;
+ }
+ if (minor < ISDN_MINOR_CTRL) {
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0)
+ return;
+ c.command = ISDN_CMD_UNLOCK;
+ c.driver = drvidx;
+ (void) dev->drv[drvidx]->interface->command(&c);
+ return;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+ if (drvidx < 0)
+ return;
+ if (dev->profd == current)
+ dev->profd = NULL;
+ c.command = ISDN_CMD_UNLOCK;
+ c.driver = drvidx;
+ (void) dev->drv[drvidx]->interface->command(&c);
+ return;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX)
+ isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
+#endif
+}
+
+static struct file_operations isdn_fops =
+{
+ isdn_lseek,
+ isdn_read,
+ isdn_write,
+ NULL, /* isdn_readdir */
+ isdn_select, /* isdn_select */
+ isdn_ioctl, /* isdn_ioctl */
+ NULL, /* isdn_mmap */
+ isdn_open,
+ isdn_close,
+ NULL /* fsync */
+};
+
+char *
+ isdn_map_eaz2msn(char *msn, int di)
+{
+ driver *this = dev->drv[di];
+ int i;
+
+ if (strlen(msn) == 1) {
+ i = msn[0] - '0';
+ if ((i >= 0) && (i <= 9))
+ if (strlen(this->msn2eaz[i]))
+ return (this->msn2eaz[i]);
+ }
+ return (msn);
+}
+
+/*
+ * Find an unused ISDN-channel, whose feature-flags match the
+ * given L2- and L3-protocols.
+ */
+int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+ ,int pre_chan)
+{
+ int i;
+ ulong flags;
+ ulong features;
+ isdn_ctrl cmd;
+
+ save_flags(flags);
+ cli();
+ features = (1 << l2_proto) | (0x100 << l3_proto);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (USG_NONE(dev->usage[i]) &&
+ (dev->drvmap[i] != -1)) {
+ int d = dev->drvmap[i];
+ if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
+ ((pre_dev != d) || (pre_chan != dev->chanmap[i])))
+ continue;
+ if ((dev->drv[d]->running)) {
+ if ((dev->drv[d]->interface->features & features) == features) {
+ if ((pre_dev < 0) || (pre_chan < 0)) {
+ dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i] |= usage;
+ isdn_info_update();
+ cmd.driver = d;
+ cmd.arg = 0;
+ cmd.command = ISDN_CMD_LOCK;
+ (void) dev->drv[d]->interface->command(&cmd);
+ restore_flags(flags);
+ return i;
+ } else {
+ if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) {
+ dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i] |= usage;
+ isdn_info_update();
+ cmd.driver = d;
+ cmd.arg = 0;
+ cmd.command = ISDN_CMD_LOCK;
+ (void) dev->drv[d]->interface->command(&cmd);
+ restore_flags(flags);
+ return i;
+ }
+ }
+ }
+ }
+ }
+ restore_flags(flags);
+ return -1;
+}
+
+/*
+ * Set state of ISDN-channel to 'unused'
+ */
+void isdn_free_channel(int di, int ch, int usage)
+{
+ int i;
+ ulong flags;
+ isdn_ctrl cmd;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (((dev->usage[i] & ISDN_USAGE_MASK) == usage) &&
+ (dev->drvmap[i] == di) &&
+ (dev->chanmap[i] == ch)) {
+ dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
+ strcpy(dev->num[i], "???");
+ dev->ibytes[i] = 0;
+ dev->obytes[i] = 0;
+ isdn_info_update();
+ isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
+ cmd.driver = di;
+ cmd.arg = ch;
+ cmd.command = ISDN_CMD_UNLOCK;
+ (void) dev->drv[di]->interface->command(&cmd);
+ restore_flags(flags);
+ return;
+ }
+ restore_flags(flags);
+}
+
+/*
+ * Cancel Exclusive-Flag for ISDN-channel
+ */
+void isdn_unexclusive_channel(int di, int ch)
+{
+ int i;
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if ((dev->drvmap[i] == di) &&
+ (dev->chanmap[i] == ch)) {
+ dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE;
+ isdn_info_update();
+ restore_flags(flags);
+ return;
+ }
+ restore_flags(flags);
+}
+
+/*
+ * receive callback handler for drivers not supporting sk_buff's.
+ * Parameters:
+ *
+ * di = Driver-Index.
+ * channel = Number of B-Channel (0...)
+ * buf = pointer to packet-data
+ * len = Length of packet-data
+ *
+ */
+void isdn_receive_callback(int drvidx, int chan, u_char *buf, int len)
+{
+ struct sk_buff *skb;
+
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return;
+ skb = dev_alloc_skb(len);
+ if (skb) {
+ memcpy(skb_put(skb, len), buf, len);
+ isdn_receive_skb_callback(drvidx, chan, skb);
+ } else
+ printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n");
+}
+
+/*
+ * writebuf replacement for SKB_ABLE drivers
+ */
+int isdn_writebuf_stub(int drvidx, int chan, const u_char *buf, int len,
+ int user)
+{
+ int ret;
+
+ if (dev->drv[drvidx]->interface->writebuf)
+ ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf,
+ len, user);
+ else {
+ struct sk_buff * skb;
+
+ skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len,
+ GFP_ATOMIC);
+ if (skb == NULL)
+ return 0;
+
+ skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
+ skb->free = 1;
+
+ if (user) {
+ if(copy_from_user(skb_put(skb, len), buf, len)) {
+ kfree_skb(skb,FREE_WRITE);
+ return -EFAULT;
+ }
+ } else
+ memcpy(skb_put(skb, len), buf, len);
+
+ ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx,
+ chan, skb);
+ if (ret <= 0)
+ kfree_skb(skb, FREE_WRITE);
+ }
+ if (ret > 0)
+ dev->obytes[isdn_dc2minor(drvidx,chan)] += ret;
+ return ret;
+}
+
+/*
+ * writebuf_skb replacement for NON SKB_ABLE drivers
+ * If lowlevel-device does not support supports skbufs, use
+ * standard send-routine, else sind directly.
+ *
+ * Return: length of data on success, -ERRcode on failure.
+ */
+
+int isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff * skb)
+{
+ int ret;
+ int len = skb->len; /* skb pointer no longer valid after free */
+
+ if (dev->drv[drvidx]->interface->writebuf_skb)
+ ret = dev->drv[drvidx]->interface->
+ writebuf_skb(drvidx, chan, skb);
+ else {
+ if ((ret = dev->drv[drvidx]->interface->
+ writebuf(drvidx,chan,skb->data,skb->len,0)) == len)
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ if (ret > 0)
+ dev->obytes[isdn_dc2minor(drvidx,chan)] += len;
+ return ret;
+}
+
+/*
+ * Low-level-driver registration
+ */
+
+int register_isdn(isdn_if * i)
+{
+ driver *d;
+ int n, j, k;
+ ulong flags;
+ int drvidx;
+
+ if (dev->drivers >= ISDN_MAX_DRIVERS) {
+ printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n",
+ ISDN_MAX_DRIVERS);
+ return 0;
+ }
+ n = i->channels;
+ if (dev->channels + n > ISDN_MAX_CHANNELS) {
+ printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+ ISDN_MAX_CHANNELS);
+ return 0;
+ }
+ if ((!i->writebuf_skb) && (!i->writebuf)) {
+ printk(KERN_WARNING "register_isdn: No write routine given.\n");
+ return 0;
+ }
+ if (!(d = (driver *) kmalloc(sizeof(driver), GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
+ return 0;
+ }
+ memset((char *) d, 0, sizeof(driver));
+ if (!(d->rcverr = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+ kfree(d);
+ return 0;
+ }
+ memset((char *) d->rcverr, 0, sizeof(int) * n);
+ if (!(d->rcvcount = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+ kfree(d->rcverr);
+ kfree(d);
+ return 0;
+ }
+ memset((char *) d->rcvcount, 0, sizeof(int) * n);
+ if (!(d->rpqueue =
+ (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ kfree(d);
+ return 0;
+ }
+ for (j = 0; j < n; j++) {
+ skb_queue_head_init(&d->rpqueue[j]);
+ }
+ if (!(d->rcv_waitq = (struct wait_queue **)
+ kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+ kfree(d->rpqueue);
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ kfree(d);
+ return 0;
+ }
+ memset((char *) d->rcv_waitq, 0, sizeof(struct wait_queue *) * n);
+ if (!(d->snd_waitq = (struct wait_queue **)
+ kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc snd_waitq\n");
+ kfree(d->rcv_waitq);
+ kfree(d->rpqueue);
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ kfree(d);
+ return 0;
+ }
+ memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * n);
+ d->channels = n;
+ d->loaded = 1;
+ d->maxbufsize = i->maxbufsize;
+ d->pktcount = 0;
+ d->stavail = 0;
+ d->running = 0;
+ d->flags = 0;
+ d->interface = i;
+ for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+ if (!dev->drv[drvidx])
+ break;
+ i->channels = drvidx;
+
+ i->rcvcallb_skb = isdn_receive_skb_callback;
+ i->rcvcallb = isdn_receive_callback;
+ i->statcallb = isdn_status_callback;
+ if (!strlen(i->id))
+ sprintf(i->id, "line%d", drvidx);
+ save_flags(flags);
+ cli();
+ for (j = 0; j < drvidx; j++)
+ if (!strcmp(i->id,dev->drvid[j]))
+ sprintf(i->id, "line%d", drvidx);
+ for (j = 0; j < n; j++)
+ for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+ if (dev->chanmap[k] < 0) {
+ dev->chanmap[k] = j;
+ dev->drvmap[k] = drvidx;
+ break;
+ }
+ dev->drv[drvidx] = d;
+ dev->channels += n;
+ strcpy(dev->drvid[drvidx], i->id);
+ isdn_info_update();
+ dev->drivers++;
+ restore_flags(flags);
+ return 1;
+}
+
+/*
+ *****************************************************************************
+ * And now the modules code.
+ *****************************************************************************
+ */
+
+extern int printk(const char *fmt,...);
+
+#ifdef MODULE
+#define isdn_init init_module
+#endif
+
+static char *isdn_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "???";
+ return rev;
+}
+
+static struct symbol_table isdn_syms = {
+#include <linux/symtab_begin.h>
+ X(register_isdn),
+#include <linux/symtab_end.h>
+};
+
+static void isdn_export_syms(void)
+{
+ register_symtab(&isdn_syms);
+ has_exported = 1;
+}
+
+/*
+ * Allocate and initialize all data, register modem-devices
+ */
+int isdn_init(void)
+{
+ int i;
+ char irev[50];
+ char trev[50];
+ char nrev[50];
+ char prev[50];
+ char arev[50];
+
+ sti();
+ if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
+ printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
+ return -EIO;
+ }
+ memset((char *) dev, 0, sizeof(isdn_dev));
+#ifdef NEW_ISDN_TIMER_CTRL
+ init_timer(&dev->timer);
+ dev->timer.function = isdn_timer_funct;
+#endif
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ dev->drvmap[i] = -1;
+ dev->chanmap[i] = -1;
+ dev->m_idx[i] = -1;
+ strcpy(dev->num[i], "???");
+ }
+ if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
+ printk(KERN_WARNING "isdn: Could not register control devices\n");
+ kfree(dev);
+ return -EIO;
+ }
+ if ((i = isdn_tty_modem_init()) < 0) {
+ printk(KERN_WARNING "isdn: Could not register tty devices\n");
+ if (i == -3)
+ tty_unregister_driver(&dev->mdm.cua_modem);
+ if (i <= -2)
+ tty_unregister_driver(&dev->mdm.tty_modem);
+ kfree(dev);
+ unregister_chrdev(ISDN_MAJOR, "isdn");
+ return -EIO;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (isdn_ppp_init() < 0) {
+ printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n");
+ tty_unregister_driver(&dev->mdm.tty_modem);
+ tty_unregister_driver(&dev->mdm.cua_modem);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ kfree(dev->mdm.info[i].xmit_buf - 4);
+ unregister_chrdev(ISDN_MAJOR, "isdn");
+ kfree(dev);
+ return -EIO;
+ }
+#endif /* CONFIG_ISDN_PPP */
+
+ if (!has_exported)
+ isdn_export_syms();
+
+ strcpy(irev,isdn_revision);
+ strcpy(trev,isdn_tty_revision);
+ strcpy(nrev,isdn_net_revision);
+ strcpy(prev,isdn_ppp_revision);
+ strcpy(arev,isdn_audio_revision);
+ printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(irev));
+ printk("%s/", isdn_getrev(trev));
+ printk("%s/", isdn_getrev(nrev));
+ printk("%s/", isdn_getrev(prev));
+ printk("%s", isdn_getrev(arev));
+
+#ifdef MODULE
+ printk(" loaded\n");
+#else
+ printk("\n");
+ isdn_cards_init();
+#endif
+ isdn_info_update();
+ return 0;
+}
+
+#ifdef MODULE
+/*
+ * Unload module
+ */
+void cleanup_module(void)
+{
+ int flags;
+ int i;
+
+#ifdef CONFIG_ISDN_PPP
+ isdn_ppp_cleanup();
+#endif
+ save_flags(flags);
+ cli();
+ if (isdn_net_rmall() < 0) {
+ printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n");
+ restore_flags(flags);
+ return;
+ }
+ if (tty_unregister_driver(&dev->mdm.tty_modem)) {
+ printk(KERN_WARNING "isdn: ttyI-device busy, remove cancelled\n");
+ restore_flags(flags);
+ return;
+ }
+ if (tty_unregister_driver(&dev->mdm.cua_modem)) {
+ printk(KERN_WARNING "isdn: cui-device busy, remove cancelled\n");
+ restore_flags(flags);
+ return;
+ }
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
+ kfree(dev->mdm.info[i].xmit_buf - 4);
+ }
+ if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
+ printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
+ } else {
+ del_timer(&dev->timer);
+ kfree(dev);
+ printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
+ }
+ restore_flags(flags);
+}
+#endif
diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h
new file mode 100644
index 000000000..81d7905a2
--- /dev/null
+++ b/drivers/isdn/isdn_common.h
@@ -0,0 +1,63 @@
+/* $Id: isdn_common.h,v 1.3 1996/05/19 00:13:05 fritz Exp $
+ *
+ * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_common.h,v $
+ * Revision 1.3 1996/05/19 00:13:05 fritz
+ * Removed debug flag.
+ *
+ * Revision 1.2 1996/04/20 16:20:40 fritz
+ * Misc. typos.
+ *
+ * Revision 1.1 1996/01/10 21:37:19 fritz
+ * Initial revision
+ *
+ */
+
+#undef ISDN_DEBUG_MODEM_OPEN
+#undef ISDN_DEBUG_MODEM_IOCTL
+#undef ISDN_DEBUG_MODEM_WAITSENT
+#undef ISDN_DEBUG_MODEM_HUP
+#undef ISDN_DEBUG_MODEM_ICALL
+#undef ISDN_DEBUG_MODEM_DUMP
+#undef ISDN_DEBUG_AT
+#undef ISDN_DEBUG_NET_DUMP
+#undef ISDN_DEBUG_NET_DIAL
+#undef ISDN_DEBUG_NET_ICALL
+
+/* Prototypes */
+extern void isdn_MOD_INC_USE_COUNT(void);
+extern void isdn_MOD_DEC_USE_COUNT(void);
+extern void isdn_free_channel(int di, int ch, int usage);
+extern void isdn_all_eaz(int di, int ch);
+extern int isdn_dc2minor(int di, int ch);
+extern void isdn_info_update(void);
+extern char* isdn_map_eaz2msn(char *msn, int di);
+extern void isdn_timer_ctrl(int tf, int onoff);
+extern void isdn_unexclusive_channel(int di, int ch);
+extern int isdn_getnum(char **);
+extern int isdn_readbchan (int, int, u_char *, u_char *, int, int);
+extern int isdn_get_free_channel(int, int, int, int, int);
+extern int isdn_writebuf_stub(int, int, const u_char *, int, int);
+extern int isdn_writebuf_skb_stub(int, int, struct sk_buff *);
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+extern void isdn_dumppkt(char *, u_char *, int, int);
+#endif
diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c
new file mode 100644
index 000000000..99b3ae746
--- /dev/null
+++ b/drivers/isdn/isdn_net.c
@@ -0,0 +1,2431 @@
+/* $Id: isdn_net.c,v 1.20 1996/08/29 20:06:03 fritz Exp $
+ *
+ * Linux ISDN subsystem, network interfaces and related functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_net.c,v $
+ * Revision 1.20 1996/08/29 20:06:03 fritz
+ * Bugfix: Transmission timeout had been much to low.
+ *
+ * Revision 1.19 1996/08/12 16:24:32 hipp
+ * removed some (now) obsolete functions for syncPPP in rebuild_header etc.
+ *
+ * Revision 1.18 1996/07/03 13:48:51 hipp
+ * bugfix: Call dev_purge_queues() only for master device
+ *
+ * Revision 1.17 1996/06/25 18:37:37 fritz
+ * Fixed return count for empty return string in isdn_net_getphones().
+ *
+ * Revision 1.16 1996/06/24 17:48:08 fritz
+ * Bugfixes:
+ * - Did not free channel on unbinding.
+ * - ioctl returned wrong callback settings.
+ *
+ * Revision 1.15 1996/06/16 17:42:54 tsbogend
+ * fixed problem with IP addresses on Linux/Alpha (long is 8 byte there)
+ *
+ * Revision 1.14 1996/06/11 14:54:08 hipp
+ * minor bugfix in isdn_net_send_skb
+ * changes in BSENT callback handler for syncPPP
+ * added lp->sav_skb stuff
+ *
+ * Revision 1.13 1996/06/06 14:25:44 fritz
+ * Changed loglevel of "incoming ... without OAD" message, since
+ * with audio support this is quite normal.
+ *
+ * Revision 1.12 1996/06/05 02:36:45 fritz
+ * Minor bugfixes by M. Hipp.
+ *
+ * Revision 1.11 1996/05/18 01:36:59 fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.10 1996/05/17 03:49:01 fritz
+ * Some cleanup.
+ *
+ * Revision 1.9 1996/05/06 11:34:57 hipp
+ * fixed a few bugs
+ *
+ * Revision 1.8 1996/04/30 21:04:40 fritz
+ * Test commit
+ *
+ * Revision 1.7 1996/04/30 11:10:42 fritz
+ * Added Michael's ippp-bind patch.
+ *
+ * Revision 1.6 1996/04/30 09:34:35 fritz
+ * Removed compatibility-macros.
+ *
+ * Revision 1.5 1996/04/20 16:28:38 fritz
+ * Made more parameters of the dial statemachine user-configurable and
+ * added hangup after dial for more reliability using callback.
+ * Changed all io going through generic routines in isdn_common.c
+ * Added missing call to dev_free_skb on failed dialing.
+ * Added uihdlc encapsulation.
+ * Fixed isdn_net_setcfg not to destroy interface-flags anymore.
+ * Misc. typos.
+ *
+ * Revision 1.4 1996/02/19 15:23:38 fritz
+ * Bugfix: Sync-PPP packets got compressed twice, when resent due to
+ * send-queue-full reject.
+ *
+ * Revision 1.3 1996/02/11 02:22:28 fritz
+ * Changed status- receive-callbacks to use pointer-arrays for finding
+ * a corresponding interface instead of looping over all interfaces.
+ * Activate Auto-hangup-timer only when interface is online.
+ * Some bugfixes in the dialing-statemachine.
+ * Lot of bugfixes in sk_buff'ized encapsulation handling.
+ * For speedup connection-setup after dialing, remember sk_buf that triggered
+ * dialing.
+ * Fixed isdn_net_log_packet according to different encapsulations.
+ * Correct ARP-handling for ETHERNET-encapsulation.
+ *
+ * Revision 1.2 1996/01/22 05:05:12 fritz
+ * Changed returncode-logic for isdn_net_start_xmit() and its
+ * helper-functions.
+ * Changed handling of buildheader for RAWIP and ETHERNET-encapsulation.
+ *
+ * Revision 1.1 1996/01/09 04:12:34 fritz
+ * Initial revision
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include <linux/if_arp.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
+#include "isdn_ppp.h"
+#endif
+
+/* In ksyms.c, but why not in some .h ??? */
+extern int arp_find(unsigned char *, u32, struct device *, u32,
+ struct sk_buff *);
+
+/* Prototypes */
+
+int isdn_net_force_dial_lp(isdn_net_local *);
+static int isdn_net_wildmat(char *s, char *p);
+static int isdn_net_start_xmit(struct sk_buff *, struct device *);
+static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
+
+extern void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */
+
+char *isdn_net_revision = "$Revision: 1.20 $";
+
+ /*
+ * Code for raw-networking over ISDN
+ */
+
+static void
+isdn_net_reset(struct device *dev)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli(); /* Avoid glitch on writes to CMD regs */
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ restore_flags(flags);
+}
+
+/* Open/initialize the board. */
+static int
+isdn_net_open(struct device *dev)
+{
+ int i;
+ struct device *p;
+
+ isdn_net_reset(dev);
+ dev->start = 1;
+ /* Fill in the MAC-level header. */
+ for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
+ dev->dev_addr[i] = 0xfc;
+ memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(u32));
+
+ /* If this interface has slaves, start them also */
+
+ if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+ while (p) {
+ isdn_net_reset(p);
+ p->start = 1;
+ p = (((isdn_net_local *) p->priv)->slave);
+ }
+ }
+
+ isdn_MOD_INC_USE_COUNT();
+ return 0;
+}
+
+/*
+ Assign an ISDN-channel to a net-interface
+ */
+static void
+isdn_net_bind_channel(isdn_net_local * lp, int idx)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+ lp->isdn_device = dev->drvmap[idx];
+ lp->isdn_channel = dev->chanmap[idx];
+ dev->rx_netdev[idx] = lp->netdev;
+ dev->st_netdev[idx] = lp->netdev;
+ restore_flags(flags);
+}
+
+/*
+ * unbind a net-interface (resets interface after an error)
+ */
+static void
+isdn_net_unbind_channel(isdn_net_local * lp)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+ if (lp->first_skb) {
+ dev_kfree_skb(lp->first_skb,FREE_WRITE);
+ lp->first_skb = NULL;
+ }
+ if(lp->sav_skb) {
+ dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+ lp->sav_skb = NULL;
+ }
+ if(!lp->master) /* purge only for master device */
+ dev_purge_queues(&lp->netdev->dev);
+ lp->dialstate = 0;
+ dev->rx_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+ dev->st_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+ isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+ lp->flags &= ~ISDN_NET_CONNECTED;
+ lp->isdn_device = -1;
+ lp->isdn_channel = -1;
+
+ restore_flags(flags);
+}
+
+/*
+ * Perform auto-hangup and cps-calculation for net-interfaces.
+ *
+ * auto-hangup:
+ * Increment idle-counter (this counter is reset on any incoming or
+ * outgoing packet), if counter exceeds configured limit either do a
+ * hangup immediately or - if configured - wait until just before the next
+ * charge-info.
+ *
+ * cps-calculation (needed for dynamic channel-bundling):
+ * Since this function is called every second, simply reset the
+ * byte-counter of the interface after copying it to the cps-variable.
+ */
+void
+isdn_net_autohup()
+{
+ isdn_net_dev *p = dev->netdev;
+ int anymore;
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+ anymore = 0;
+ while (p) {
+ isdn_net_local *l = (isdn_net_local *) & (p->local);
+ l->cps = l->transcount;
+ l->transcount = 0;
+ if (dev->net_verbose > 3)
+ printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
+ if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
+ anymore = 1;
+ l->huptimer++;
+ if ((l->onhtime) && (l->huptimer > l->onhtime))
+ if (l->outgoing) {
+ if (l->hupflags & 4) {
+ if (l->hupflags & 1)
+ isdn_net_hangup(&p->dev);
+ else if (jiffies - l->chargetime > l->chargeint)
+ isdn_net_hangup(&p->dev);
+ } else
+ isdn_net_hangup(&p->dev);
+ } else if (l->hupflags & 8)
+ isdn_net_hangup(&p->dev);
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,anymore);
+ restore_flags(flags);
+}
+
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the low-level driver.
+ * Return: 1 = Event handled, 0 = not for us or unknown Event.
+ */
+int
+isdn_net_stat_callback(int idx, int cmd)
+{
+ isdn_net_dev *p = dev->st_netdev[idx];
+
+ if (p) {
+ isdn_net_local *lp = &(p->local);
+ switch (cmd) {
+ case ISDN_STAT_BSENT:
+ /* A packet has successfully been sent out */
+ if ((lp->flags & ISDN_NET_CONNECTED) &&
+ (!lp->dialstate)) {
+ lp->stats.tx_packets++;
+ if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) {
+ struct device *mdev;
+ if(lp->master)
+ mdev = lp->master;
+ else
+ mdev = &lp->netdev->dev;
+ if(!isdn_net_send_skb(mdev,lp,lp->sav_skb)) {
+ lp->sav_skb = NULL;
+ mark_bh(NET_BH);
+ }
+ else {
+ return 1;
+ }
+ }
+ if (clear_bit(0,(void*)&(p->dev.tbusy)))
+ mark_bh(NET_BH);
+ }
+ return 1;
+ case ISDN_STAT_DCONN:
+ /* D-Channel is up */
+ switch (lp->dialstate) {
+ case 4:
+ case 7:
+ case 8:
+ lp->dialstate++;
+ return 1;
+ case 12:
+ lp->dialstate = 5;
+ return 1;
+ }
+ break;
+ case ISDN_STAT_DHUP:
+ /* Either D-Channel-hangup or error during dialout */
+ if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
+ lp->flags &= ~ISDN_NET_CONNECTED;
+ if(lp->first_skb) {
+ dev_kfree_skb(lp->first_skb,FREE_WRITE);
+ lp->first_skb = NULL;
+ }
+ if(lp->sav_skb) {
+ dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+ lp->sav_skb = NULL;
+ }
+ isdn_free_channel(lp->isdn_device, lp->isdn_channel,
+ ISDN_USAGE_NET);
+#ifdef CONFIG_ISDN_PPP
+ isdn_ppp_free(lp);
+#endif
+ isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+ printk(KERN_INFO "%s: remote hangup\n", lp->name);
+ printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
+ lp->charge);
+ lp->isdn_device = -1;
+ lp->isdn_channel = -1;
+ dev->st_netdev[idx] = NULL;
+ dev->rx_netdev[idx] = NULL;
+ return 1;
+ }
+ break;
+ case ISDN_STAT_BCONN:
+ /* B-Channel is up */
+ switch (lp->dialstate) {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 12:
+ if (lp->dialstate <= 6) {
+ dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+ isdn_info_update();
+ } else
+ dev->rx_netdev[idx] = p;
+ lp->dialstate = 0;
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,1);
+ printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
+ /* If first Chargeinfo comes before B-Channel connect,
+ * we correct the timestamp here.
+ */
+ lp->chargetime = jiffies;
+ /* Immediately send first skb to speed up arp */
+#ifdef CONFIG_ISDN_PPP
+ if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ isdn_ppp_wakeup_daemon(lp);
+#endif
+ if (lp->first_skb) {
+ if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb)))
+ lp->first_skb = NULL;
+ }
+ return 1;
+ }
+ break;
+ case ISDN_STAT_NODCH:
+ /* No D-Channel avail. */
+ if (lp->dialstate == 4) {
+ lp->dialstate--;
+ return 1;
+ }
+ break;
+ case ISDN_STAT_CINF:
+ /* Charge-info from TelCo. Calculate interval between
+ * charge-infos and set timestamp for last info for
+ * usage by isdn_net_autohup()
+ */
+ lp->charge++;
+ if (lp->hupflags & 2) {
+ lp->hupflags &= ~1;
+ lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
+ }
+ if (lp->hupflags & 1)
+ lp->hupflags |= 2;
+ lp->chargetime = jiffies;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Check, if a number contains wildcard-characters, in which case it
+ * is for incoming purposes only.
+ */
+static int
+isdn_net_checkwild(char *num)
+{
+ return ((strchr(num, '?')) ||
+ (strchr(num, '*')) ||
+ (strchr(num, '[')) ||
+ (strchr(num, ']')) ||
+ (strchr(num, '^')));
+}
+
+/*
+ * Perform dialout for net-interfaces and timeout-handling for
+ * D-Channel-up and B-Channel-up Messages.
+ * This function is initially called from within isdn_net_start_xmit() or
+ * or isdn_net_find_icall() after initializing the dialstate for an
+ * interface. If further calls are needed, the function schedules itself
+ * for a timer-callback via isdn_timer_function().
+ * The dialstate is also affected by incoming status-messages from
+ * the ISDN-Channel which are handled in isdn_net_stat_callback() above.
+ */
+void
+isdn_net_dial(void)
+{
+ isdn_net_dev *p = dev->netdev;
+ int anymore = 0;
+ int i;
+ isdn_ctrl cmd;
+
+ while (p) {
+#ifdef ISDN_DEBUG_NET_DIAL
+ if (p->local.dialstate)
+ printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name,p->local.dialstate);
+#endif
+ switch (p->local.dialstate) {
+ case 0:
+ /* Nothing to do for this interface */
+ break;
+ case 1:
+ /* Initiate dialout. Set phone-number-pointer to first number
+ * of interface.
+ */
+ p->local.dial = p->local.phone[1];
+ anymore = 1;
+ p->local.dialstate++;
+ break;
+ /* Prepare dialing. Clear EAZ, then set EAZ. */
+ case 2:
+ cmd.driver = p->local.isdn_device;
+ cmd.arg = p->local.isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ sprintf(cmd.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
+ cmd.command = ISDN_CMD_SETEAZ;
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ p->local.dialretry = 0;
+ anymore = 1;
+ p->local.dialstate++;
+ break;
+ case 3:
+ /* Setup interface, dial current phone-number, switch to next number.
+ * If list of phone-numbers is exhausted, increment
+ * retry-counter.
+ */
+ cmd.driver = p->local.isdn_device;
+ cmd.command = ISDN_CMD_SETL2;
+ cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ cmd.driver = p->local.isdn_device;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ cmd.driver = p->local.isdn_device;
+ cmd.arg = p->local.isdn_channel;
+ p->local.huptimer = 0;
+ p->local.outgoing = 1;
+ p->local.hupflags |= 1;
+ p->local.hupflags &= ~2;
+ if (!strcmp(p->local.dial->num, "LEASED")) {
+ p->local.dialstate = 4;
+ printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
+ } else {
+ cmd.command = ISDN_CMD_DIAL;
+ sprintf(cmd.num, "%s,%s,7,0", p->local.dial->num,
+ isdn_map_eaz2msn(p->local.msn, cmd.driver));
+ i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
+ if (i >= 0) {
+ strcpy(dev->num[i], p->local.dial->num);
+ isdn_info_update();
+ }
+ printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
+ p->local.dialretry, p->local.dial->num);
+ /*
+ * Switch to next number or back to start if at end of list.
+ */
+ if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
+ p->local.dial = p->local.phone[1];
+ p->local.dialretry++;
+ }
+ p->local.dtimer = 0;
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
+ p->local.isdn_channel);
+#endif
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ }
+ anymore = 1;
+ p->local.dialstate =
+ (p->local.cbdelay &&
+ (p->local.flags & ISDN_NET_CBOUT))?12:4;
+ break;
+ case 4:
+ /* Wait for D-Channel-connect.
+ * If timeout and max retries not
+ * reached, switch back to state 3.
+ */
+ if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+ if (p->local.dialretry < p->local.dialmax) {
+ p->local.dialstate = 3;
+ } else
+ isdn_net_hangup(&p->dev);
+ anymore = 1;
+ break;
+ case 5:
+ /* Got D-Channel-Connect, send B-Channel-request */
+ cmd.driver = p->local.isdn_device;
+ cmd.arg = p->local.isdn_channel;
+ cmd.command = ISDN_CMD_ACCEPTB;
+ anymore = 1;
+ p->local.dtimer = 0;
+ p->local.dialstate++;
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ break;
+ case 6:
+ /* Wait for B- or D-Channel-connect. If timeout,
+ * switch back to state 3.
+ */
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
+#endif
+ if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+ p->local.dialstate = 3;
+ anymore = 1;
+ break;
+ case 7:
+ /* Got incoming Call, setup L2 and L3 protocols,
+ * then wait for D-Channel-connect
+ */
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+#endif
+ cmd.driver = p->local.isdn_device;
+ cmd.command = ISDN_CMD_SETL2;
+ cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ cmd.driver = p->local.isdn_device;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
+ isdn_net_hangup(&p->dev);
+ else {
+ anymore = 1;
+ p->local.dialstate++;
+ }
+ break;
+ case 9:
+ /* Got incoming D-Channel-Connect, send B-Channel-request */
+ cmd.driver = p->local.isdn_device;
+ cmd.arg = p->local.isdn_channel;
+ cmd.command = ISDN_CMD_ACCEPTB;
+ dev->drv[p->local.isdn_device]->interface->command(&cmd);
+ anymore = 1;
+ p->local.dtimer = 0;
+ p->local.dialstate++;
+ break;
+ case 8:
+ case 10:
+ /* Wait for B- or D-channel-connect */
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+#endif
+ if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+ isdn_net_hangup(&p->dev);
+ else
+ anymore = 1;
+ break;
+ case 11:
+ /* Callback Delay */
+ if (p->local.dtimer++ > p->local.cbdelay)
+ p->local.dialstate = 1;
+ anymore = 1;
+ break;
+ case 12:
+ /* Remote does callback. Hangup after cbdelay, then wait for incoming
+ * call (in state 4).
+ */
+ if (p->local.dtimer++ > p->local.cbdelay) {
+ printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name);
+ p->local.dtimer = 0;
+ p->local.dialstate = 4;
+ cmd.driver = p->local.isdn_device;
+ cmd.command = ISDN_CMD_HANGUP;
+ cmd.arg = p->local.isdn_channel;
+ (void) dev->drv[cmd.driver]->interface->command(&cmd);
+ isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
+ }
+ anymore = 1;
+ break;
+ default:
+ printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+ p->local.dialstate, p->local.name);
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+ isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore);
+}
+
+/*
+ * Perform hangup for a net-interface.
+ */
+void
+isdn_net_hangup(struct device *d)
+{
+ isdn_net_local *lp = (isdn_net_local *) d->priv;
+ isdn_ctrl cmd;
+
+ if (lp->flags & ISDN_NET_CONNECTED) {
+ printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
+#ifdef CONFIG_ISDN_PPP
+ isdn_ppp_free(lp);
+#endif
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_HANGUP;
+ cmd.arg = lp->isdn_channel;
+ (void) dev->drv[cmd.driver]->interface->command(&cmd);
+ printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
+ isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+ }
+ isdn_net_unbind_channel(lp);
+}
+
+typedef struct {
+ unsigned short source;
+ unsigned short dest;
+} ip_ports;
+
+static void
+isdn_net_log_packet(u_char * buf, isdn_net_local * lp)
+{
+ u_char *p = buf;
+ unsigned short proto = ETH_P_IP;
+ int data_ofs;
+ ip_ports *ipp;
+ char addinfo[100];
+
+ addinfo[0] = '\0';
+ switch (lp->p_encap) {
+ case ISDN_NET_ENCAP_IPTYP:
+ proto = ntohs(*(unsigned short *)&buf[0]);
+ p = &buf[2];
+ break;
+ case ISDN_NET_ENCAP_ETHER:
+ proto = ntohs(*(unsigned short *)&buf[12]);
+ p = &buf[14];
+ break;
+ case ISDN_NET_ENCAP_CISCOHDLC:
+ proto = ntohs(*(unsigned short *)&buf[2]);
+ p = &buf[4];
+ break;
+ }
+ data_ofs = ((p[0] & 15) * 4);
+ switch (proto) {
+ case ETH_P_IP:
+ switch (p[9]) {
+ case 1:
+ strcpy(addinfo, " ICMP");
+ break;
+ case 2:
+ strcpy(addinfo, " IGMP");
+ break;
+ case 4:
+ strcpy(addinfo, " IPIP");
+ break;
+ case 6:
+ ipp = (ip_ports *) (&p[data_ofs]);
+ sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+ ntohs(ipp->dest));
+ break;
+ case 8:
+ strcpy(addinfo, " EGP");
+ break;
+ case 12:
+ strcpy(addinfo, " PUP");
+ break;
+ case 17:
+ ipp = (ip_ports *) (&p[data_ofs]);
+ sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+ ntohs(ipp->dest));
+ break;
+ case 22:
+ strcpy(addinfo, " IDP");
+ break;
+ }
+ printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+ p[12], p[13], p[14], p[15],
+ p[16], p[17], p[18], p[19],
+ addinfo);
+ break;
+ case ETH_P_ARP:
+ printk(KERN_INFO "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+ p[14], p[15], p[16], p[17],
+ p[24], p[25], p[26], p[27]);
+ break;
+ }
+}
+
+/*
+ * Generic routine to send out an skbuf.
+ * If lowlevel-device does not support supports skbufs, use
+ * standard send-routine, else send directly.
+ *
+ * Return: 0 on success, !0 on failure.
+ * Side-effects: ndev->tbusy is cleared on success.
+ */
+int
+isdn_net_send_skb(struct device *ndev, isdn_net_local *lp,
+ struct sk_buff *skb)
+{
+ int ret;
+ int len = skb->len; /* save len */
+
+ ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb);
+ if (ret == len) {
+ lp->transcount += len;
+ clear_bit(0, (void *)&(ndev->tbusy));
+ return 0;
+ }
+ if (ret < 0) {
+ skb->free = 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ lp->stats.tx_errors++;
+ clear_bit(0, (void *)&(ndev->tbusy));
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * Helper function for isdn_net_start_xmit.
+ * When called, the connection is already established.
+ * Based on cps-calculation, check if device is overloaded.
+ * If so, and if a slave exists, trigger dialing for it.
+ * If any slave is online, deliver packets using a simple round robin
+ * scheme.
+ *
+ * Return: 0 on success, !0 on failure.
+ */
+
+static int
+isdn_net_xmit(struct device *ndev, isdn_net_local *lp, struct sk_buff *skb)
+{
+ int ret;
+
+ /* For the other encaps the header has already been built */
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+ return isdn_ppp_xmit(skb, ndev);
+ }
+#endif
+ /* Reset hangup-timeout */
+ lp->huptimer = 0;
+ if (lp->cps > 7000) {
+ /* Device overloaded */
+
+ /*
+ * Packet-delivery via round-robin over master
+ * and all connected slaves.
+ */
+ if (lp->master)
+ /* Slaves always deliver themselves */
+ ret = isdn_net_send_skb(ndev, lp, skb);
+ else {
+ isdn_net_local *slp = (isdn_net_local *) (lp->srobin->priv);
+ /* Master delivers via srobin and maintains srobin */
+ if (lp->srobin == ndev)
+ ret = isdn_net_send_skb(ndev, lp, skb);
+ else
+ ret = ndev->tbusy = isdn_net_start_xmit(skb, lp->srobin);
+ lp->srobin = (slp->slave) ? slp->slave : ndev;
+ slp = (isdn_net_local *) (lp->srobin->priv);
+ if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0)))
+ lp->srobin = ndev;
+ }
+ /* Slave-startup using delay-variable */
+ if (lp->slave) {
+ if (!lp->sqfull) {
+ /* First time overload: set timestamp only */
+ lp->sqfull = 1;
+ lp->sqfull_stamp = jiffies;
+ }
+ else {
+ /* subsequent overload: if slavedelay exceeded, start dialing */
+ if ((jiffies - lp->sqfull_stamp) > lp->slavedelay)
+ isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+ }
+ }
+ }
+ else {
+ /* Not overloaded, deliver locally */
+ ret = isdn_net_send_skb(ndev, lp, skb);
+ if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10*HZ) )))
+ lp->sqfull = 0;
+ }
+ return ret;
+}
+
+/*
+ * Try sending a packet.
+ * If this interface isn't connected to a ISDN-Channel, find a free channel,
+ * and start dialing.
+ */
+int
+isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev)
+{
+ isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+ if (ndev->tbusy) {
+ if (jiffies - ndev->trans_start < (2 * HZ))
+ return 1;
+ if (!lp->dialstate)
+ lp->stats.tx_errors++;
+ ndev->tbusy = 0;
+ ndev->trans_start = jiffies;
+ }
+ if (skb == NULL) {
+ dev_tint(ndev);
+ return 0;
+ }
+ /* Avoid timer-based retransmission conflicts. */
+ if (set_bit(0, (void *) &ndev->tbusy) != 0)
+ printk(KERN_WARNING
+ "%s: Transmitter access conflict.\n",
+ ndev->name);
+ else {
+ u_char *buf = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+ isdn_dumppkt("S:", buf, skb->len, 40);
+#endif
+ if (!(lp->flags & ISDN_NET_CONNECTED)) {
+ int chi;
+ if (lp->phone[1]) {
+ ulong flags;
+ save_flags(flags);
+ cli();
+ /* Grab a free ISDN-Channel */
+ if ((chi =
+ isdn_get_free_channel(ISDN_USAGE_NET,
+ lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel)) < 0) {
+ printk(KERN_WARNING
+ "isdn_net_start_xmit: No channel for %s\n",
+ ndev->name);
+ restore_flags(flags);
+ /* we probably should drop the skb here and return 0 to omit
+ 'socket destroy delayed' messages */
+ return 1;
+ }
+ /* Log packet, which triggered dialing */
+ if (dev->net_verbose)
+ isdn_net_log_packet(buf, lp);
+ lp->dialstate = 1;
+ lp->flags |= ISDN_NET_CONNECTED;
+ /* Connect interface with channel */
+ isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+ /* no 'first_skb' handling for syncPPP */
+ if (isdn_ppp_bind(lp) < 0) {
+ dev_kfree_skb(skb,FREE_WRITE);
+ isdn_net_unbind_channel(lp);
+ restore_flags(flags);
+ return 0; /* STN (skb to nirvana) ;) */
+ }
+ isdn_net_dial(); /* Initiate dialing */
+ restore_flags(flags);
+ return 1; /* let upper layer requeue skb packet */
+ }
+#endif
+ /* remember first skb to speed up arp
+ * when using encap ETHER
+ */
+ if (lp->first_skb) {
+ printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
+ dev_kfree_skb(lp->first_skb,FREE_WRITE);
+ lp->first_skb = NULL;
+ }
+ lp->first_skb = skb;
+ /* Initiate dialing */
+ isdn_net_dial();
+ ndev->tbusy = 0;
+ restore_flags(flags);
+ return 0;
+ } else {
+ /*
+ * Having no phone-number is a permanent
+ * failure or misconfiguration.
+ * Instead of just dropping, we should also
+ * have the upper layers to respond
+ * with an ICMP No route to host in the
+ * future, however at the moment, i don't
+ * know a simple way to do that.
+ * The same applies, when the telecom replies
+ * "no destination" to our dialing-attempt.
+ */
+ printk(KERN_WARNING
+ "isdn_net: No phone number for %s, packet dropped\n",
+ ndev->name);
+ dev_kfree_skb(skb, FREE_WRITE);
+ ndev->tbusy = 0;
+ return 0;
+ }
+ } else {
+ /* Connection is established, try sending */
+ ndev->trans_start = jiffies;
+ if (!lp->dialstate) {
+ if (lp->first_skb) {
+ if (isdn_net_xmit(ndev,lp,lp->first_skb))
+ return 1;
+ lp->first_skb = NULL;
+ }
+ return(isdn_net_xmit(ndev, lp, skb));
+ } else
+ ndev->tbusy = 1;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Shutdown a net-interface.
+ */
+static int
+isdn_net_close(struct device *dev)
+{
+ struct device *p;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+ isdn_net_hangup(dev);
+ if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+ /* If this interface has slaves, stop them also */
+ while (p) {
+ isdn_net_hangup(p);
+ p->tbusy = 1;
+ p->start = 0;
+ p = (((isdn_net_local *) p->priv)->slave);
+ }
+ }
+ isdn_MOD_DEC_USE_COUNT();
+ return 0;
+}
+
+/*
+ * Get statistics
+ */
+static struct enet_statistics *
+ isdn_net_get_stats(struct device *dev)
+{
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ return &lp->stats;
+}
+
+/* This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
+ * instead of dev->hard_header_len off. This is done because the
+ * lowlevel-driver has already pulled off its stuff when we get
+ * here and this routine only gets called with p_encap == ETHER.
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ */
+
+unsigned short isdn_net_type_trans(struct sk_buff *skb, struct device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb_pull(skb,ETH_HLEN);
+ eth= skb->mac.ethernet;
+
+ if(*eth->h_dest&1) {
+ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ /*
+ * This ALLMULTI check should be redundant by 1.4
+ * so don't forget to remove it.
+ */
+
+ else if (dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) {
+ if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+
+/*
+ * Got a packet from ISDN-Channel.
+ */
+static void
+isdn_net_receive(struct device *ndev, struct sk_buff *skb)
+{
+ isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+#ifdef CONFIG_ISDN_PPP
+ isdn_net_local *olp = lp; /* original 'lp' */
+#endif
+
+ lp->transcount += skb->len;
+ lp->stats.rx_packets++;
+ lp->huptimer = 0;
+
+ if (lp->master) {
+ /* Bundling: If device is a slave-device, deliver to master, also
+ * handle master's statistics and hangup-timeout
+ */
+ ndev = lp->master;
+ lp = (isdn_net_local *) ndev->priv;
+ lp->stats.rx_packets++;
+ lp->huptimer = 0;
+ }
+
+ skb->dev = ndev;
+ skb->pkt_type = PACKET_HOST;
+ skb->mac.raw = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+ isdn_dumppkt("R:", skb->data, skb->len, 40);
+#endif
+ switch (lp->p_encap) {
+ case ISDN_NET_ENCAP_ETHER:
+ /* Ethernet over ISDN */
+ skb->protocol = isdn_net_type_trans(skb,ndev);
+ break;
+ case ISDN_NET_ENCAP_UIHDLC:
+ /* HDLC with UI-frame (for ispa with -h1 option) */
+ skb_pull(skb,2);
+ /* Fall through */
+ case ISDN_NET_ENCAP_RAWIP:
+ /* RAW-IP without MAC-Header */
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case ISDN_NET_ENCAP_CISCOHDLC:
+ /* CISCO-HDLC IP with type field and fake I-frame-header */
+ skb_pull(skb, 2);
+ /* Fall through */
+ case ISDN_NET_ENCAP_IPTYP:
+ /* IP with type field */
+ skb->protocol = *(unsigned short *)&(skb->data[0]);
+ skb_pull(skb, 2);
+ if (*(unsigned short *)skb->data == 0xFFFF)
+ skb->protocol = htons(ETH_P_802_3);
+ break;
+#ifdef CONFIG_ISDN_PPP
+ case ISDN_NET_ENCAP_SYNCPPP:
+ isdn_ppp_receive(lp->netdev, olp, skb);
+ return;
+#endif
+ default:
+ printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+ lp->name);
+ kfree_skb(skb,FREE_READ);
+ return;
+ }
+ netif_rx(skb);
+ return;
+}
+
+/*
+ * A packet arrived via ISDN. Search interface-chain for a corresponding
+ * interface. If found, deliver packet to receiver-function and return 1,
+ * else return 0.
+ */
+int
+isdn_net_receive_callback(int idx, u_char * buf, int len)
+{
+ isdn_net_dev *p = dev->rx_netdev[idx];
+ struct sk_buff *skb;
+
+ if (p) {
+ isdn_net_local *lp = &p->local;
+ if ((lp->flags & ISDN_NET_CONNECTED) &&
+ (!lp->dialstate)) {
+ skb = dev_alloc_skb(len);
+ if (skb == NULL) {
+ printk(KERN_WARNING "out of memory\n");
+ return 0;
+ }
+ memcpy(skb_put(skb, len), buf, len);
+ isdn_net_receive(&p->dev, skb);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * receive callback for lowlevel drivers, which support skb's
+ */
+
+int
+isdn_net_rcv_skb(int idx, struct sk_buff *skb)
+{
+ isdn_net_dev *p = dev->rx_netdev[idx];
+
+ if (p) {
+ isdn_net_local *lp = &p->local;
+ if ((lp->flags & ISDN_NET_CONNECTED) &&
+ (!lp->dialstate)) {
+ isdn_net_receive(&p->dev, skb);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
+
+ /*
+ * Set the protocol type. For a packet of type ETH_P_802_3 we
+ * put the length here instead. It is up to the 802.2 layer to
+ * carry protocol information.
+ */
+
+ if(type!=ETH_P_802_3)
+ eth->h_proto = htons(type);
+ else
+ eth->h_proto = htons(len);
+
+ /*
+ * Set the source hardware address.
+ */
+ if(saddr)
+ memcpy(eth->h_source,saddr,dev->addr_len);
+ else
+ memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+
+ /*
+ * Anyway, the loopback-device should never use this function...
+ */
+
+ if (dev->flags & IFF_LOOPBACK) {
+ memset(eth->h_dest, 0, dev->addr_len);
+ return(dev->hard_header_len);
+ }
+
+ if(daddr) {
+ memcpy(eth->h_dest,daddr,dev->addr_len);
+ return dev->hard_header_len;
+ }
+
+ return -dev->hard_header_len;
+}
+
+/*
+ * build an header
+ * depends on encaps that is being used.
+ */
+
+static int
+isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned plen)
+{
+ isdn_net_local *lp = dev->priv;
+ ushort len = 0;
+
+ switch (lp->p_encap) {
+ case ISDN_NET_ENCAP_ETHER:
+ len = my_eth_header(skb, dev, type, daddr, saddr, plen);
+ break;
+ case ISDN_NET_ENCAP_RAWIP:
+ printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+ len = 0;
+ break;
+ case ISDN_NET_ENCAP_IPTYP:
+ /* ethernet type field */
+ *((ushort*) skb_push(skb, 2)) = htons(type);
+ len = 2;
+ break;
+ case ISDN_NET_ENCAP_UIHDLC:
+ /* HDLC with UI-Frames (for ispa with -h1 option) */
+ *((ushort*) skb_push(skb, 2)) = htons(0x0103);
+ len = 2;
+ break;
+ case ISDN_NET_ENCAP_CISCOHDLC:
+ skb_push(skb, 4);
+ skb->data[0] = 0x0f;
+ skb->data[1] = 0x00;
+ *((ushort*)&skb->data[2]) = htons(type);
+ len = 4;
+ break;
+ }
+ return len;
+}
+
+/* We don't need to send arp, because we have point-to-point connections. */
+
+static int
+isdn_net_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+ struct sk_buff *skb)
+{
+ isdn_net_local *lp = dev->priv;
+ int ret = 0;
+
+ if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+ struct ethhdr *eth = (struct ethhdr *)buff;
+
+ /*
+ * Only ARP/IP is currently supported
+ */
+
+ if(eth->h_proto != htons(ETH_P_IP)) {
+ printk(KERN_WARNING
+ "isdn_net: %s don't know how to resolve type %d addresses?\n",
+ dev->name, (int)eth->h_proto);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return 0;
+ }
+ /*
+ * Try to get ARP to resolve the header.
+ */
+#ifdef CONFIG_INET
+ ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
+#endif
+ }
+ return ret;
+}
+
+/*
+ * Interface-setup. (called just after registering a new interface)
+ */
+static int
+isdn_net_init(struct device *ndev)
+{
+ ushort max_hlhdr_len = 0;
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ int drvidx, i;
+
+ if (ndev == NULL) {
+ printk(KERN_WARNING "isdn_net_init: dev = NULL!\n");
+ return -ENODEV;
+ }
+ if (ndev->priv == NULL) {
+ printk(KERN_WARNING "isdn_net_init: dev->priv = NULL!\n");
+ return -ENODEV;
+ }
+
+ ether_setup(ndev);
+ lp->org_hcb = ndev->header_cache_bind;
+ lp->org_hcu = ndev->header_cache_update;
+
+ /* Setup the generic properties */
+
+ ndev->hard_header = NULL;
+ ndev->header_cache_bind = NULL;
+ ndev->header_cache_update = NULL;
+ ndev->mtu = 1500;
+ ndev->flags = IFF_NOARP;
+ ndev->family = AF_INET;
+ ndev->type = ARPHRD_ETHER;
+ ndev->addr_len = ETH_ALEN;
+ ndev->pa_addr = 0;
+ ndev->pa_brdaddr = 0;
+ ndev->pa_mask = 0;
+ ndev->pa_alen = 4;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ ndev->broadcast[i]=0xff;
+
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&ndev->buffs[i]);
+
+ /* The ISDN-specific entries in the device structure. */
+ ndev->open = &isdn_net_open;
+ ndev->hard_start_xmit = &isdn_net_start_xmit;
+
+ /*
+ * up till binding we ask the protocol layer to reserve as much
+ * as we might need for HL layer
+ */
+
+ for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+ if (dev->drv[drvidx])
+ if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
+ max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
+
+ ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
+
+ ndev->stop = &isdn_net_close;
+ ndev->get_stats = &isdn_net_get_stats;
+ ndev->rebuild_header = &isdn_net_rebuild_header;
+
+#ifdef CONFIG_ISDN_PPP
+ ndev->do_ioctl = isdn_ppp_dev_ioctl;
+#endif
+ return 0;
+}
+
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+
+static int
+isdn_net_Star(char *s, char *p)
+{
+ while (isdn_net_wildmat(s, p) == 0)
+ if (*++s == '\0')
+ return (0);
+ return (1);
+}
+
+/*
+ * Shell-type Pattern-matching for incoming caller-Ids
+ * This function gets a string in s and checks, if it matches the pattern
+ * given in p. It returns 1 on success, 0 otherwise.
+ *
+ * Possible Patterns:
+ *
+ * '?' matches one character
+ * '*' matches zero or more characters
+ * [xyz] matches the set of characters in brackets.
+ * [^xyz] matches any single character not in the set of characters
+ */
+
+static int
+isdn_net_wildmat(char *s, char *p)
+{
+ register int last;
+ register int matched;
+ register int reverse;
+
+ for (; *p; s++, p++)
+ switch (*p) {
+ case '\\':
+ /*
+ * Literal match with following character,
+ * fall through.
+ */
+ p++;
+ default:
+ if (*s != *p)
+ return (0);
+ continue;
+ case '?':
+ /* Match anything. */
+ if (*s == '\0')
+ return (0);
+ continue;
+ case '*':
+ /* Trailing star matches everything. */
+ return (*++p ? isdn_net_Star(s, p) : 1);
+ case '[':
+ /* [^....] means inverse character class. */
+ if ((reverse = (p[1] == '^')))
+ p++;
+ for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+ /* This next line requires a good C compiler. */
+ if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+ matched = 1;
+ if (matched == reverse)
+ return (0);
+ continue;
+ }
+ return (*s == '\0');
+}
+
+static void
+isdn_net_swapbind(int drvidx)
+{
+ isdn_net_dev *p;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx);
+#endif
+ p = dev->netdev;
+ while (p) {
+ if (p->local.pre_device == drvidx)
+ switch (p->local.pre_channel) {
+ case 0:
+ p->local.pre_channel = 1;
+ break;
+ case 1:
+ p->local.pre_channel = 0;
+ break;
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+}
+
+static void
+isdn_net_swap_usage(int i1, int i2)
+{
+ int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE;
+ int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2);
+#endif
+ dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i1] |= u2;
+ dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i2] |= u1;
+ isdn_info_update();
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the interface-chain for an appropriate interface.
+ * If found, connect the interface to the ISDN-channel and initiate
+ * D- and B-Channel-setup. If secure-flag is set, accept only
+ * configured phone-numbers. If callback-flag is set, initiate
+ * callback-dialing.
+ *
+ * Return-Value: 0 = No appropriate interface for this call.
+ * 1 = Call accepted
+ * 2 = Reject call, wait cbdelay, then call back
+ * 3 = Reject call
+ * 4 = Wait cbdelay, then call back
+ */
+int
+isdn_net_find_icall(int di, int ch, int idx, char *num)
+{
+ char *eaz;
+ int si1;
+ int si2;
+ int ematch;
+ int swapped;
+ int sidx = 0;
+ isdn_net_dev *p;
+ isdn_net_phone *n;
+ ulong flags;
+ char nr[31];
+ char *s;
+
+ /* Search name in netdev-chain */
+ save_flags(flags);
+ cli();
+ if (num[0] == ',') {
+ nr[0] = '0';
+ strncpy(&nr[1], num, 30);
+ printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
+ } else
+ strncpy(nr, num, 30);
+ s = strtok(nr, ",");
+ s = strtok(NULL, ",");
+ if (!s) {
+ printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
+ num);
+ restore_flags(flags);
+ return 0;
+ }
+ si1 = (int)simple_strtoul(s,NULL,10);
+ s = strtok(NULL, ",");
+ if (!s) {
+ printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
+ num);
+ restore_flags(flags);
+ return 0;
+ }
+ si2 = (int)simple_strtoul(s,NULL,10);
+ eaz = strtok(NULL, ",");
+ if (!eaz) {
+ printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
+ eaz = "0";
+ }
+ if (dev->net_verbose > 1)
+ printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
+ /* Accept only calls with Si1 = 7 (Data-Transmission) */
+ if (si1 != 7) {
+ if (dev->net_verbose > 1)
+ printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
+ return 0;
+ }
+ n = (isdn_net_phone *) 0;
+ p = dev->netdev;
+ ematch = 0;
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
+ dev->usage[idx]);
+#endif
+ swapped = 0;
+ while (p) {
+ /* If last check has triggered as binding-swap, revert it */
+ switch (swapped) {
+ case 2:
+ isdn_net_swap_usage(idx, sidx);
+ /* fall through */
+ case 1:
+ isdn_net_swapbind(di);
+ break;
+ }
+ swapped = 0;
+ if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
+ ematch = 1;
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
+ p->local.name, p->local.msn, p->local.flags, p->local.dialstate);
+#endif
+ if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) && /* EAZ is matching */
+ (((!(p->local.flags & ISDN_NET_CONNECTED)) && /* but not connected */
+ (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */
+ ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) && /* if dialing */
+ (!(p->local.flags & ISDN_NET_CALLBACK))) /* but no callback */
+ ))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
+ p->local.pre_device, p->local.pre_channel);
+#endif
+ if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
+ if ((p->local.pre_channel != ch) ||
+ (p->local.pre_device != di)) {
+ /* Here we got a problem:
+ If using an ICN-Card, an incoming call is always signaled on
+ on the first channel of the card, if both channels are
+ down. However this channel may be bound exclusive. If the
+ second channel is free, this call should be accepted.
+ The solution is horribly but it runs, so what:
+ We exchange the exclusive bindings of the two channels, the
+ corresponding variables in the interface-structs.
+ */
+ if (ch == 0) {
+ sidx = isdn_dc2minor(di, 1);
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: ch is 0\n");
+#endif
+ if (USG_NONE(dev->usage[sidx])) {
+ /* Second Channel is free, now see if it is bound
+ exclusive too. */
+ if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
+#endif
+ /* Yes, swap bindings only, if the original
+ binding is bound to channel 1 of this driver */
+ if ((p->local.pre_device == di) &&
+ (p->local.pre_channel == 1)) {
+ isdn_net_swapbind(di);
+ swapped = 1;
+ } else {
+ /* ... else iterate next device */
+ p = (isdn_net_dev *) p->next;
+ continue;
+ }
+ } else {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n");
+#endif
+ /* No, swap always and swap excl-usage also */
+ isdn_net_swap_usage(idx, sidx);
+ isdn_net_swapbind(di);
+ swapped = 2;
+ }
+ /* Now check for exclusive binding again */
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: final check\n");
+#endif
+ if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) &&
+ ((p->local.pre_channel != ch) ||
+ (p->local.pre_device != di))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: final check failed\n");
+#endif
+ p = (isdn_net_dev *) p->next;
+ continue;
+ }
+ }
+ } else {
+ /* We are already on the second channel, so nothing to do */
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
+#endif
+ p = (isdn_net_dev *) p->next;
+ continue;
+ }
+ }
+ } /* if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) */
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: match2\n");
+#endif
+ n = p->local.phone[0];
+ if (p->local.flags & ISDN_NET_SECURE) {
+ while (n) {
+ if (isdn_net_wildmat(nr, n->num))
+ break;
+ n = (isdn_net_phone *) n->next;
+ }
+ }
+ if (n || (!(p->local.flags & ISDN_NET_SECURE))) {
+ isdn_net_local *lp = &(p->local);
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: match3\n");
+#endif
+ /* Here we got an interface matched, now see if it is up.
+ * If not, reject the call actively.
+ */
+ if (!p->dev.start) {
+ restore_flags(flags);
+ printk(KERN_INFO "%s: incoming call, if down -> rejected\n",
+ lp->name);
+ return 3;
+ }
+ /* Interface is up, now see if it's a slave. If so, see if
+ * it's master and parent slave is online. If not, reject the call.
+ */
+ if (lp->master) {
+ isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
+ printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
+ printk(KERN_DEBUG "master=%s\n", mlp->name);
+ if (mlp->flags & ISDN_NET_CONNECTED) {
+ printk(KERN_DEBUG "master online\n");
+ /* Master is online, find parent-slave (master if first slave) */
+ while (mlp->slave) {
+ if ((isdn_net_local *) mlp->slave->priv == lp)
+ break;
+ mlp = (isdn_net_local *) mlp->slave->priv;
+ }
+ } else
+ printk(KERN_DEBUG "master offline\n");
+ /* Found parent, if it's offline iterate next device */
+ printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
+ if (!(mlp->flags & ISDN_NET_CONNECTED)) {
+ p = (isdn_net_dev *) p->next;
+ continue;
+ }
+ }
+ if (lp->flags & ISDN_NET_CALLBACK) {
+ int chi;
+ printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
+ lp->name, nr, eaz);
+ if (lp->phone[1]) {
+ /* Grab a free ISDN-Channel */
+ if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel)) < 0) {
+ printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
+ restore_flags(flags);
+ return 0;
+ }
+ /* Setup dialstate. */
+ lp->dtimer = 0;
+ lp->dialstate = 11;
+ lp->flags |= ISDN_NET_CONNECTED;
+ /* Connect interface with channel */
+ isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ if (isdn_ppp_bind(lp) < 0) {
+ isdn_net_unbind_channel(lp);
+ restore_flags(flags);
+ return 0;
+ }
+#endif
+ /* Initiate dialing by returning 2 or 4 */
+ restore_flags(flags);
+ return (lp->flags & ISDN_NET_CBHUP)?2:4;
+ } else
+ printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
+ restore_flags(flags);
+ return 0;
+ } else {
+ printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", lp->name, nr,
+ eaz);
+ /* if this interface is dialing, it does it probably on a different
+ device, so free this device */
+ if ((p->local.dialstate == 4) || (p->local.dialstate == 12))
+ isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+ ISDN_USAGE_NET);
+ dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[idx] |= ISDN_USAGE_NET;
+ strcpy(dev->num[idx], nr);
+ isdn_info_update();
+ dev->st_netdev[idx] = lp->netdev;
+ p->local.isdn_device = di;
+ p->local.isdn_channel = ch;
+ p->local.ppp_slot = -1;
+ p->local.pppbind = -1;
+ p->local.flags |= ISDN_NET_CONNECTED;
+ p->local.dialstate = 7;
+ p->local.dtimer = 0;
+ p->local.outgoing = 0;
+ p->local.huptimer = 0;
+ p->local.hupflags |= 1;
+ p->local.hupflags &= ~2;
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ if (isdn_ppp_bind(lp) < 0) {
+ isdn_net_unbind_channel(lp);
+ restore_flags(flags);
+ return 0;
+ }
+#endif
+ restore_flags(flags);
+ return 1;
+ }
+ }
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+ /* If none of configured EAZ/MSN matched and not verbose, be silent */
+ if (ematch || dev->net_verbose)
+ printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Search list of net-interfaces for an interface with given name.
+ */
+isdn_net_dev *
+ isdn_net_findif(char *name)
+{
+ isdn_net_dev *p = dev->netdev;
+
+ while (p) {
+ if (!strcmp(p->local.name, name))
+ return p;
+ p = (isdn_net_dev *) p->next;
+ }
+ return (isdn_net_dev *) NULL;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is called from the userlevel-routine below or
+ * from isdn_net_start_xmit().
+ */
+int isdn_net_force_dial_lp(isdn_net_local * lp)
+{
+ if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
+ int chi;
+ if (lp->phone[1]) {
+ ulong flags;
+ save_flags(flags);
+ cli();
+ /* Grab a free ISDN-Channel */
+ if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel)) < 0) {
+ printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ lp->dialstate = 1;
+ lp->flags |= ISDN_NET_CONNECTED;
+ /* Connect interface with channel */
+ isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ if (isdn_ppp_bind(lp) < 0) {
+ isdn_net_unbind_channel(lp);
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+#endif
+ /* Initiate dialing */
+ isdn_net_dial();
+ restore_flags(flags);
+ return 0;
+ } else
+ return -EINVAL;
+ } else
+ return -EBUSY;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
+ */
+int
+isdn_net_force_dial(char *name)
+{
+ isdn_net_dev *p = isdn_net_findif(name);
+
+ if (!p)
+ return -ENODEV;
+ return (isdn_net_force_dial_lp(&p->local));
+}
+
+/*
+ * Allocate a new network-interface and initialize its data structures.
+ */
+char *
+isdn_net_new(char *name, struct device *master)
+{
+ isdn_net_dev *netdev;
+
+ /* Avoid creating an existing interface */
+ if (isdn_net_findif(name)) {
+ printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
+ return NULL;
+ }
+ if (!(netdev = (isdn_net_dev *) kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
+ printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
+ return NULL;
+ }
+ memset(netdev, 0, sizeof(isdn_net_dev));
+ if (name == NULL)
+ strcpy(netdev->local.name, " ");
+ else
+ strcpy(netdev->local.name, name);
+ netdev->dev.name = netdev->local.name;
+ netdev->dev.priv = &netdev->local;
+ netdev->dev.init = isdn_net_init;
+ netdev->local.p_encap = ISDN_NET_ENCAP_RAWIP;
+ if (master) {
+ /* Device shall be a slave */
+ struct device *p = (((isdn_net_local *) master->priv)->slave);
+ struct device *q = master;
+
+ netdev->local.master = master;
+ /* Put device at end of slave-chain */
+ while (p) {
+ q = p;
+ p = (((isdn_net_local *) p->priv)->slave);
+ }
+ ((isdn_net_local *) q->priv)->slave = &(netdev->dev);
+ q->interrupt = 0;
+ q->tbusy = 0;
+ q->start = master->start;
+ } else {
+ /* Device shall be a master */
+ if (register_netdev(&netdev->dev) != 0) {
+ printk(KERN_WARNING "isdn_net: Could not register net-device\n");
+ kfree(netdev);
+ return NULL;
+ }
+ }
+ netdev->local.magic = ISDN_NET_MAGIC;
+
+#ifdef CONFIG_ISDN_PPP
+ netdev->mp_last = NULL; /* mpqueue is empty */
+ netdev->ib.next_num = 0;
+ netdev->ib.last = NULL;
+#endif
+ netdev->queue = &netdev->local;
+ netdev->local.last = &netdev->local;
+ netdev->local.netdev = netdev;
+ netdev->local.next = &netdev->local;
+
+ netdev->local.isdn_device = -1;
+ netdev->local.isdn_channel = -1;
+ netdev->local.pre_device = -1;
+ netdev->local.pre_channel = -1;
+ netdev->local.exclusive = -1;
+ netdev->local.ppp_slot = -1;
+ netdev->local.pppbind = -1;
+ netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
+ netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
+ netdev->local.slavedelay = 10 * HZ;
+ netdev->local.srobin = &netdev->dev;
+ netdev->local.hupflags = 8; /* Do hangup even on incoming calls */
+ netdev->local.onhtime = 10; /* Default hangup-time for saving costs
+ of those who forget configuring this */
+ netdev->local.dialmax = 1;
+ netdev->local.flags = ISDN_NET_CBHUP; /* Hangup before Callback */
+ netdev->local.cbdelay = 25; /* Wait 5 secs before Callback */
+ /* Put into to netdev-chain */
+ netdev->next = (void *) dev->netdev;
+ dev->netdev = netdev;
+ return netdev->dev.name;
+}
+
+char *
+isdn_net_newslave(char *parm)
+{
+ char *p = strchr(parm, ',');
+ isdn_net_dev *n;
+ char newname[10];
+
+ if (p) {
+ /* Slave-Name MUST not be empty */
+ if (!strlen(p + 1))
+ return NULL;
+ strcpy(newname, p + 1);
+ *p = 0;
+ /* Master must already exist */
+ if (!(n = isdn_net_findif(parm)))
+ return NULL;
+ /* Master must be a real interface, not a slave */
+ if (n->local.master)
+ return NULL;
+ return (isdn_net_new(newname, &(n->dev)));
+ }
+ return NULL;
+}
+
+/*
+ * Set interface-parameters.
+ * Always set all parameters, so the user-level application is responsible
+ * for not overwriting existing setups. It has to get the current
+ * setup first, if only selected parameters are to be changed.
+ */
+int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+{
+ isdn_net_dev *p = isdn_net_findif(cfg->name);
+ ulong features;
+ int i;
+ int drvidx;
+ int chidx;
+ char drvid[25];
+
+ if (p) {
+ /* See if any registered driver supports the features we want */
+ features = (1 << cfg->l2_proto) | (256 << cfg->l3_proto);
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (dev->drv[i])
+ if ((dev->drv[i]->interface->features & features) == features)
+ break;
+ if (i == ISDN_MAX_DRIVERS) {
+ printk(KERN_WARNING "isdn_net: No driver with selected features\n");
+ return -ENODEV;
+ }
+ if (p->local.p_encap != cfg->p_encap)
+ if (p->dev.start) {
+ printk(KERN_WARNING
+ "%s: cannot change encap when if is up\n",
+ p->local.name);
+ return -EBUSY;
+ }
+#ifndef CONFIG_ISDN_PPP
+ if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+ printk(KERN_WARNING "%s: SyncPPP not configured\n",
+ p->local.name);
+ return -EINVAL;
+ }
+#endif
+ if (strlen(cfg->drvid)) {
+ /* A bind has been requested ... */
+ char *c,*e;
+
+ drvidx = -1;
+ chidx = -1;
+ strcpy(drvid, cfg->drvid);
+ if ((c = strchr(drvid, ','))) {
+ /* The channel-number is appended to the driver-Id with a comma */
+ chidx = (int)simple_strtoul(c + 1,&e,10);
+ if (e == c)
+ chidx = -1;
+ *c = '\0';
+ }
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ /* Lookup driver-Id in array */
+ if (!(strcmp(dev->drvid[i], drvid))) {
+ drvidx = i;
+ break;
+ }
+ if ((drvidx == -1) || (chidx == -1))
+ /* Either driver-Id or channel-number invalid */
+ return -ENODEV;
+ } else {
+ /* Parameters are valid, so get them */
+ drvidx = p->local.pre_device;
+ chidx = p->local.pre_channel;
+ }
+ if (cfg->exclusive > 0) {
+ int flags;
+
+ /* If binding is exclusive, try to grab the channel */
+ save_flags(flags);
+ if ((i = isdn_get_free_channel(ISDN_USAGE_NET, p->local.l2_proto,
+ p->local.l3_proto,
+ drvidx,
+ chidx)) < 0) {
+ /* Grab failed, because desired channel is in use */
+ p->local.exclusive = -1;
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ /* All went ok, so update isdninfo */
+ dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
+ isdn_info_update();
+ restore_flags(flags);
+ p->local.exclusive = i;
+ } else {
+ /* Non-exclusive binding or unbind. */
+ p->local.exclusive = -1;
+ if ((p->local.pre_device != -1) && (cfg->exclusive == -1)) {
+ isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel);
+ isdn_free_channel(p->local.pre_device, p->local.pre_channel,ISDN_USAGE_NET);
+ drvidx = -1;
+ chidx = -1;
+ }
+ }
+ strcpy(p->local.msn, cfg->eaz);
+ p->local.pre_device = drvidx;
+ p->local.pre_channel = chidx;
+ p->local.onhtime = cfg->onhtime;
+ p->local.charge = cfg->charge;
+ p->local.l2_proto = cfg->l2_proto;
+ p->local.l3_proto = cfg->l3_proto;
+ p->local.cbdelay = cfg->cbdelay;
+ p->local.dialmax = cfg->dialmax;
+ p->local.slavedelay = cfg->slavedelay * HZ;
+ p->local.pppbind = cfg->pppbind;
+ if (cfg->secure)
+ p->local.flags |= ISDN_NET_SECURE;
+ else
+ p->local.flags &= ~ISDN_NET_SECURE;
+ if (cfg->cbhup)
+ p->local.flags |= ISDN_NET_CBHUP;
+ else
+ p->local.flags &= ~ISDN_NET_CBHUP;
+ switch (cfg->callback) {
+ case 0:
+ p->local.flags &= ~(ISDN_NET_CALLBACK|ISDN_NET_CBOUT);
+ break;
+ case 1:
+ p->local.flags |= ISDN_NET_CALLBACK;
+ p->local.flags &= ~ISDN_NET_CBOUT;
+ break;
+ case 2:
+ p->local.flags |= ISDN_NET_CBOUT;
+ p->local.flags &= ~ISDN_NET_CALLBACK;
+ break;
+ }
+ if (cfg->chargehup)
+ p->local.hupflags |= 4;
+ else
+ p->local.hupflags &= ~4;
+ if (cfg->ihup)
+ p->local.hupflags |= 8;
+ else
+ p->local.hupflags &= ~8;
+ if (cfg->p_encap != p->local.p_encap) {
+ if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+ p->dev.hard_header = NULL;
+ p->dev.header_cache_bind = NULL;
+ p->dev.header_cache_update = NULL;
+ p->dev.flags = IFF_NOARP;
+ } else {
+ p->dev.hard_header = isdn_net_header;
+ if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
+ p->dev.header_cache_bind = p->local.org_hcb;
+ p->dev.header_cache_update = p->local.org_hcu;
+ p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+ } else {
+ p->dev.header_cache_bind = NULL;
+ p->dev.header_cache_update = NULL;
+ p->dev.flags = IFF_NOARP;
+ }
+ }
+ }
+ p->local.p_encap = cfg->p_encap;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Perform get-interface-parameters.ioctl
+ */
+int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+{
+ isdn_net_dev *p = isdn_net_findif(cfg->name);
+
+ if (p) {
+ strcpy(cfg->eaz, p->local.msn);
+ cfg->exclusive = p->local.exclusive;
+ if (p->local.pre_device >= 0) {
+ sprintf(cfg->drvid, "%s,%d", dev->drvid[p->local.pre_device],
+ p->local.pre_channel);
+ } else
+ cfg->drvid[0] = '\0';
+ cfg->onhtime = p->local.onhtime;
+ cfg->charge = p->local.charge;
+ cfg->l2_proto = p->local.l2_proto;
+ cfg->l3_proto = p->local.l3_proto;
+ cfg->p_encap = p->local.p_encap;
+ cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
+ cfg->callback = 0;
+ if (p->local.flags & ISDN_NET_CALLBACK)
+ cfg->callback = 1;
+ if (p->local.flags & ISDN_NET_CBOUT)
+ cfg->callback = 2;
+ cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0;
+ cfg->chargehup = (p->local.hupflags & 4) ? 1 : 0;
+ cfg->ihup = (p->local.hupflags & 8) ? 1 : 0;
+ cfg->cbdelay = p->local.cbdelay;
+ cfg->dialmax = p->local.dialmax;
+ cfg->slavedelay = p->local.slavedelay / HZ;
+ cfg->pppbind = p->local.pppbind;
+ if (p->local.slave)
+ strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name);
+ else
+ cfg->slave[0] = '\0';
+ if (p->local.master)
+ strcpy(cfg->master, ((isdn_net_local *) p->local.master->priv)->name);
+ else
+ cfg->master[0] = '\0';
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Add a phone-number to an interface.
+ */
+int isdn_net_addphone(isdn_net_ioctl_phone * phone)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ isdn_net_phone *n;
+
+ if (isdn_net_checkwild(phone->phone) && (phone->outgoing & 1))
+ return -EINVAL;
+ if (p) {
+ if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
+ return -ENOMEM;
+ strcpy(n->num, phone->phone);
+ n->next = p->local.phone[phone->outgoing & 1];
+ p->local.phone[phone->outgoing & 1] = n;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Return a string of all phone-numbers of an interface.
+ */
+int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ int inout = phone->outgoing & 1;
+ int more = 0;
+ int count = 0;
+ isdn_net_phone *n;
+ int flags;
+ int ret = 0;
+
+ if (!p)
+ return -ENODEV;
+ save_flags(flags);
+ cli();
+ inout &= 1;
+ for (n = p->local.phone[inout]; n; n = n->next) {
+ if (more) {
+ ret = put_user(((char)' '), phones);
+ phones++;
+ count++;
+ }
+ if (ret || copy_to_user(phones, n->num, strlen(n->num) + 1)) {
+ restore_flags(flags);
+ return -EFAULT;
+ }
+ phones += strlen(n->num);
+ count += strlen(n->num);
+ more = 1;
+ }
+ ret = put_user(((char)0),phones);
+ count++;
+ restore_flags(flags);
+ return ret ? -EFAULT : count;
+}
+
+/*
+ * Delete a phone-number from an interface.
+ */
+
+int isdn_net_delphone(isdn_net_ioctl_phone * phone)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ int inout = phone->outgoing & 1;
+ isdn_net_phone *n;
+ isdn_net_phone *m;
+
+ if (p) {
+ n = p->local.phone[inout];
+ m = NULL;
+ while (n) {
+ if (!strcmp(n->num, phone->phone)) {
+ if (m)
+ m->next = n->next;
+ else
+ p->local.phone[inout] = n->next;
+ kfree(n);
+ return 0;
+ }
+ m = n;
+ n = (isdn_net_phone *) n->next;
+ }
+ return -EINVAL;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Delete all phone-numbers of an interface.
+ */
+static int isdn_net_rmallphone(isdn_net_dev * p)
+{
+ isdn_net_phone *n;
+ isdn_net_phone *m;
+ int flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < 2; i++) {
+ n = p->local.phone[i];
+ while (n) {
+ m = n->next;
+ kfree(n);
+ n = m;
+ }
+ p->local.phone[i] = NULL;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Force a hangup of a network-interface.
+ */
+int isdn_net_force_hangup(char *name)
+{
+ isdn_net_dev *p = isdn_net_findif(name);
+ int flags;
+ struct device *q;
+
+ if (p) {
+ save_flags(flags);
+ cli();
+ if (p->local.isdn_device < 0) {
+ restore_flags(flags);
+ return 1;
+ }
+ isdn_net_hangup(&p->dev);
+ q = p->local.slave;
+ /* If this interface has slaves, do a hangup for them also. */
+ while (q) {
+ isdn_net_hangup(q);
+ q = (((isdn_net_local *) q->priv)->slave);
+ }
+ restore_flags(flags);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Helper-function for isdn_net_rm: Do the real work.
+ */
+static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+{
+ int flags;
+
+ save_flags(flags);
+ cli();
+ if (p->local.master) {
+ /* If it's a slave, it may be removed even if it is busy. However
+ * it has to be hung up first.
+ */
+ isdn_net_hangup(&p->dev);
+ p->dev.start = 0;
+ }
+ if (p->dev.start) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ /* Free all phone-entries */
+ isdn_net_rmallphone(p);
+ /* If interface is bound exclusive, free channel-usage */
+ if (p->local.exclusive != -1)
+ isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel);
+ if (p->local.master) {
+ /* It's a slave-device, so update master's slave-pointer if necessary */
+ if (((isdn_net_local *) (p->local.master->priv))->slave == &p->dev)
+ ((isdn_net_local *) (p->local.master->priv))->slave = p->local.slave;
+ } else
+ /* Unregister only if it's a master-device */
+ unregister_netdev(&p->dev);
+ /* Unlink device from chain */
+ if (q)
+ q->next = p->next;
+ else
+ dev->netdev = p->next;
+ if (p->local.slave) {
+ /* If this interface has a slave, remove it also */
+ char *slavename = ((isdn_net_local *) (p->local.slave->priv))->name;
+ isdn_net_dev *n = dev->netdev;
+ q = NULL;
+ while (n) {
+ if (!strcmp(n->local.name, slavename)) {
+ isdn_net_realrm(n, q);
+ break;
+ }
+ q = n;
+ n = (isdn_net_dev *) n->next;
+ }
+ }
+ /* If no more net-devices remain, disable auto-hangup timer */
+ if (dev->netdev == NULL)
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+ restore_flags(flags);
+
+#ifdef CONFIG_ISDN_PPP
+ isdn_ppp_free_mpqueue(p); /* still necessary? */
+#endif
+ kfree(p);
+
+ return 0;
+}
+
+/*
+ * Remove a single network-interface.
+ */
+int isdn_net_rm(char *name)
+{
+ isdn_net_dev *p;
+ isdn_net_dev *q;
+
+ /* Search name in netdev-chain */
+ p = dev->netdev;
+ q = NULL;
+ while (p) {
+ if (!strcmp(p->local.name, name))
+ return (isdn_net_realrm(p, q));
+ q = p;
+ p = (isdn_net_dev *) p->next;
+ }
+ /* If no more net-devices remain, disable auto-hangup timer */
+ if (dev->netdev == NULL)
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+ return -ENODEV;
+}
+
+/*
+ * Remove all network-interfaces
+ */
+int isdn_net_rmall(void)
+{
+ int flags;
+ int ret;
+
+ /* Walk through netdev-chain */
+ save_flags(flags);
+ cli();
+ while (dev->netdev) {
+ if (!dev->netdev->local.master) {
+ /* Remove master-devices only, slaves get removed with their master */
+ if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
+ restore_flags(flags);
+ return ret;
+ }
+ }
+ }
+ dev->netdev = NULL;
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * helper function to flush device queues
+ * the better place would be net/core/dev.c
+ */
+void dev_purge_queues(struct device *dev)
+{
+ int i;
+ for(i=0;i<DEV_NUMBUFFS;i++) {
+ struct sk_buff *skb;
+ while((skb=skb_dequeue(&dev->buffs[i])))
+ dev_kfree_skb(skb,FREE_WRITE);
+ }
+
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h
new file mode 100644
index 000000000..5b6754e66
--- /dev/null
+++ b/drivers/isdn/isdn_net.h
@@ -0,0 +1,52 @@
+/* $Id: isdn_net.h,v 1.2 1996/04/20 16:29:43 fritz Exp $
+ *
+ * header for Linux ISDN subsystem, network related functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_net.h,v $
+ * Revision 1.2 1996/04/20 16:29:43 fritz
+ * Misc. typos
+ *
+ * Revision 1.1 1996/02/11 02:35:13 fritz
+ * Initial revision
+ *
+ */
+
+extern char* isdn_net_new(char *, struct device *);
+extern char* isdn_net_newslave(char *);
+extern int isdn_net_rm(char *);
+extern int isdn_net_rmall(void);
+extern int isdn_net_stat_callback(int, int);
+extern int isdn_net_receive_callback(int, u_char *, int);
+extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
+extern int isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int isdn_net_find_icall(int, int, int, char *);
+extern void isdn_net_hangup(struct device *);
+extern void isdn_net_dial(void);
+extern void isdn_net_autohup(void);
+extern int isdn_net_force_hangup(char *);
+extern int isdn_net_force_dial(char *);
+extern isdn_net_dev* isdn_net_findif(char *);
+extern int isdn_net_send_skb(struct device *, isdn_net_local *,
+ struct sk_buff *);
+extern int isdn_net_rcv_skb(int, struct sk_buff *);
diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c
new file mode 100644
index 000000000..6d678ed51
--- /dev/null
+++ b/drivers/isdn/isdn_ppp.c
@@ -0,0 +1,1591 @@
+/* $Id: isdn_ppp.c,v 1.14 1996/08/12 16:26:47 hipp Exp $
+ *
+ * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_ppp.c,v $
+ * Revision 1.14 1996/08/12 16:26:47 hipp
+ * code cleanup
+ * changed connection management from minors to slots
+ *
+ * Revision 1.13 1996/07/01 19:47:24 hipp
+ * Fixed memory leak in VJ handling and more VJ changes
+ *
+ * Revision 1.12 1996/06/24 17:42:03 fritz
+ * Minor bugfixes.
+ *
+ * Revision 1.11 1996/06/16 17:46:05 tsbogend
+ * changed unsigned long to u32 to make Alpha people happy
+ *
+ * Revision 1.10 1996/06/11 14:50:29 hipp
+ * Lot of changes and bugfixes.
+ * New scheme to resend packets to busy LL devices.
+ *
+ * Revision 1.9 1996/05/18 01:37:01 fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.8 1996/05/06 11:34:55 hipp
+ * fixed a few bugs
+ *
+ * Revision 1.7 1996/04/30 11:07:42 fritz
+ * Added Michael's ippp-bind patch.
+ *
+ * Revision 1.6 1996/04/30 09:33:09 fritz
+ * Removed compatibility-macros.
+ *
+ * Revision 1.5 1996/04/20 16:32:32 fritz
+ * Changed ippp_table to an array of pointers, allocating each part
+ * separately.
+ *
+ * Revision 1.4 1996/02/19 15:25:50 fritz
+ * Bugfix: Sync-PPP packets got compressed twice, when resent due to
+ * send-queue-full reject.
+ *
+ * Revision 1.3 1996/02/11 02:27:12 fritz
+ * Lot of Bugfixes my Michael.
+ * Moved calls to skb_push() into isdn_net_header()
+ * Fixed a possible race-condition in isdn_ppp_timer_timeout().
+ *
+ * Revision 1.2 1996/01/22 05:08:06 fritz
+ * Merged in Michael's patches for MP.
+ * Minor changes in isdn_ppp_xmit.
+ *
+ * Revision 1.1 1996/01/09 04:11:29 fritz
+ * Initial revision
+ *
+ */
+
+/* TODO: right tbusy handling when using MP */
+
+#include <asm/uaccess.h>
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_ppp.h"
+#include "isdn_net.h"
+
+#ifndef PPP_IPX
+#define PPP_IPX 0x002b
+#endif
+
+/* Prototypes */
+static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot);
+static int isdn_ppp_closewait(int slot);
+static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
+ struct sk_buff *skb, int proto);
+static int isdn_ppp_if_get_unit(char *namebuf);
+
+#ifdef CONFIG_ISDN_MPP
+static int isdn_ppp_bundle(struct ippp_struct *, int unit);
+static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
+static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min);
+static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
+ int BEbyte, int *sqno, int min_sqno);
+#endif
+
+char *isdn_ppp_revision = "$Revision: 1.14 $";
+struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+
+extern int isdn_net_force_dial_lp(isdn_net_local *);
+
+/*
+ * unbind isdn_net_local <=> ippp-device
+ * note: it can happen, that we hangup/free the master before the slaves
+ */
+int isdn_ppp_free(isdn_net_local *lp)
+{
+ isdn_net_local *master_lp=lp;
+ unsigned long flags;
+ struct ippp_struct *is;
+
+ if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS)
+ return 0;
+
+ is = ippp_table[lp->ppp_slot];
+
+ save_flags(flags);
+ cli();
+#ifdef CONFIG_ISDN_MPP
+ if(lp->master)
+ master_lp = (isdn_net_local *) lp->master->priv;
+
+ lp->last->next = lp->next;
+ lp->next->last = lp->last;
+ if(master_lp->netdev->queue == lp) {
+ master_lp->netdev->queue = lp->next;
+ if(lp->next == lp) { /* last link in queue? */
+ master_lp->netdev->ib.bundled = 0;
+ isdn_ppp_free_mpqueue(master_lp->netdev);
+ isdn_ppp_free_sqqueue(master_lp->netdev);
+ }
+ }
+ lp->next = lp->last = lp; /* (re)set own pointers */
+#endif
+
+ isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */
+
+ if(is->debug & 0x1)
+ printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp,(long) is->lp);
+
+ is->lp = NULL; /* link is down .. set lp to NULL */
+ lp->ppp_slot = -1; /* is this OK ?? */
+ restore_flags(flags);
+
+ return 0;
+}
+
+/*
+ * bind isdn_net_local <=> ippp-device
+ */
+int isdn_ppp_bind(isdn_net_local * lp)
+{
+ int i;
+ int unit = 0;
+ long flags;
+ struct ippp_struct *is;
+
+ if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+ return -1;
+
+ save_flags(flags);
+ cli();
+
+ if(lp->pppbind < 0) /* device bounded to ippp device ? */
+ {
+ isdn_net_dev *net_dev = dev->netdev;
+ char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */
+ memset(exclusive,0,ISDN_MAX_CHANNELS);
+ while (net_dev) { /* step through net devices to find exclusive minors */
+ isdn_net_local *lp = &net_dev->local;
+ if(lp->pppbind >= 0)
+ exclusive[lp->pppbind] = 1;
+ net_dev = net_dev->next;
+ }
+ /*
+ * search a free device / slot
+ */
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */
+ break;
+ }
+ }
+ }
+ else {
+ for(i=0;i<ISDN_MAX_CHANNELS;i++)
+ if(ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+ break;
+ }
+
+ if (i >= ISDN_MAX_CHANNELS) {
+ restore_flags(flags);
+ printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
+ return -1;
+ }
+
+ unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */
+ if(unit < 0) {
+ printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",lp->name);
+ return -1;
+ }
+
+ lp->ppp_slot = i;
+ is = ippp_table[i];
+ is->lp = lp;
+ is->unit = unit;
+ is->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+
+ restore_flags(flags);
+
+ return lp->ppp_slot;
+}
+
+/*
+ * kick the ipppd on the device
+ * (wakes up daemon after B-channel connect)
+ */
+
+void isdn_ppp_wakeup_daemon(isdn_net_local *lp)
+{
+ if (ippp_table[lp->ppp_slot]->wq)
+ wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
+}
+
+/*
+ * there was a hangup on the netdevice
+ * force wakeup of the ippp device
+ * go into 'device waits for release' state
+ */
+static int isdn_ppp_closewait(int slot)
+{
+ struct ippp_struct *is;
+
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS)
+ return 0;
+ is = ippp_table[slot];
+
+ if (is->state && is->wq)
+ wake_up_interruptible(&is->wq);
+
+ is->state = IPPP_CLOSEWAIT;
+ return 1;
+}
+
+/*
+ * isdn_ppp_find_slot / isdn_ppp_free_slot
+ */
+
+static int isdn_ppp_get_slot(void)
+{
+ int i;
+ for(i=0;i<ISDN_MAX_CHANNELS;i++) {
+ if(!ippp_table[i]->state)
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * isdn_ppp_open
+ */
+
+int isdn_ppp_open(int min, struct file *file)
+{
+ int slot;
+ struct ippp_struct *is;
+
+ slot = isdn_ppp_get_slot();
+ if(slot < 0) {
+ return -EBUSY;
+ }
+ is = file->private_data = ippp_table[slot];
+
+ if(is->debug & 0x1)
+ printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",slot, min,is->state);
+
+ is->lp = 0;
+ is->mp_seqno = 0; /* MP sequence number */
+ is->pppcfg = 0; /* ppp configuration */
+ is->mpppcfg = 0; /* mppp configuration */
+ is->range = 0x1000000; /* MP: 24 bit range */
+ is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
+ is->unit = -1; /* set, when we have our interface */
+ is->mru = 1524; /* MRU, default 1524 */
+ is->maxcid = 16; /* VJ: maxcid */
+ is->tk = current;
+ is->wq = NULL; /* read() wait queue */
+ is->wq1 = NULL; /* select() wait queue */
+ is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+ is->last = is->rq;
+ is->minor = min;
+#ifdef CONFIG_ISDN_PPP_VJ
+ /*
+ * VJ header compression init
+ */
+ is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */
+#endif
+
+ is->state = IPPP_OPEN;
+
+ return 0;
+}
+
+/*
+ * release ippp device
+ */
+void isdn_ppp_release(int min, struct file *file)
+{
+ int i;
+ struct ippp_struct *is;
+
+ if (min < 0 || min >= ISDN_MAX_CHANNELS)
+ return;
+ is = file->private_data;
+
+ if(is->debug & 0x1)
+ printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp);
+
+ if (is->lp) { /* a lp address says: this link is still up */
+ isdn_net_dev *p = dev->netdev;
+ p = is->lp->netdev;
+ is->lp->ppp_slot = -1;
+ isdn_net_hangup(&p->dev); /* lp->ppp_slot==-1 => no calling of isdn_ppp_closewait() */
+ is->lp = NULL;
+ }
+ for (i = 0; i < NUM_RCV_BUFFS; i++) {
+ if (is->rq[i].buf) {
+ kfree(is->rq[i].buf);
+ is->rq[i].buf = NULL;
+ }
+ }
+ is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+ is->last = is->rq;
+
+#ifdef CONFIG_ISDN_PPP_VJ
+ slhc_free(is->slcomp);
+ is->slcomp = NULL;
+#endif
+
+ is->state = 0;
+}
+
+#if 0 /* get_user() / put_user() in 2.1 replace them 1:1 */
+/*
+ * get_arg .. ioctl helper
+ */
+static int get_arg(void *b, unsigned long *val)
+{
+ int r;
+ if (copy_from_user((void *) val, b, sizeof(unsigned long)))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * set arg .. ioctl helper
+ */
+static int set_arg(void *b, unsigned long val)
+{
+ int r;
+ if ((r = verify_area(VERIFY_WRITE, b, sizeof(unsigned long))))
+ return r;
+ copy_to_user(b, (void *) &val, sizeof(unsigned long));
+ return 0;
+}
+#endif
+
+/*
+ * ippp device ioctl
+ */
+int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ unsigned long val;
+ int r;
+ struct ippp_struct *is;
+ unsigned long *argp = (unsigned long*)arg;
+
+ is = file->private_data;
+
+ if(is->debug & 0x1)
+ printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",min,cmd,is->state);
+
+ if (!(is->state & IPPP_OPEN))
+ return -EINVAL;
+
+ switch (cmd) {
+ case PPPIOCBUNDLE:
+#ifdef CONFIG_ISDN_MPP
+ if ((r = get_user(val, argp)))
+ return r;
+ printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+ (int) min, (int) is->unit, (int) val);
+ return isdn_ppp_bundle(is, val);
+#else
+ return -1;
+#endif
+ break;
+ case PPPIOCGUNIT: /* get ppp/isdn unit number */
+ if ((r = put_user(is->unit, argp)))
+ return r;
+ break;
+ case PPPIOCGMPFLAGS: /* get configuration flags */
+ if ((r = put_user(is->mpppcfg, argp)))
+ return r;
+ break;
+ case PPPIOCSMPFLAGS: /* set configuration flags */
+ if ((r = get_user(val, argp)))
+ return r;
+ is->mpppcfg = val;
+ break;
+ case PPPIOCGFLAGS: /* get configuration flags */
+ if ((r = put_user(is->pppcfg ,argp)))
+ return r;
+ break;
+ case PPPIOCSFLAGS: /* set configuration flags */
+ if ((r = get_user(val, argp))) {
+ return r;
+ }
+ if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP)) {
+ isdn_net_local *lp = is->lp;
+ lp->netdev->dev.tbusy = 0;
+ mark_bh(NET_BH); /* OK .. we are ready to send buffers */
+ }
+ is->pppcfg = val;
+ break;
+#if 0
+ case PPPIOCGSTAT: /* read PPP statistic information */
+ break;
+ case PPPIOCGTIME: /* read time delta information */
+ break;
+#endif
+ case PPPIOCSMRU: /* set receive unit size for PPP */
+ if ((r = get_user(val, argp)))
+ return r;
+ is->mru = val;
+ break;
+ case PPPIOCSMPMRU:
+ break;
+ case PPPIOCSMPMTU:
+ break;
+ case PPPIOCSMAXCID: /* set the maximum compression slot id */
+ if ((r = get_user(val, argp)))
+ return r;
+ val++;
+ if(is->maxcid != val) {
+#ifdef CONFIG_ISDN_PPP_VJ
+ struct slcompress *sltmp;
+#endif
+ if(is->debug & 0x1)
+ printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val);
+ is->maxcid = val;
+#ifdef CONFIG_ISDN_PPP_VJ
+ sltmp = slhc_init(16,val);
+ if(!sltmp) {
+ printk(KERN_ERR "ippp, can't realloc slhc struct\n");
+ return -ENOMEM;
+ }
+ if(is->slcomp)
+ slhc_free(is->slcomp);
+ is->slcomp = sltmp;
+#endif
+ }
+ break;
+ case PPPIOCGDEBUG:
+ if ((r = put_user(is->debug, argp)))
+ return r;
+ break;
+ case PPPIOCSDEBUG:
+ if ((r = get_user(val, argp)))
+ return r;
+ is->debug = val;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int isdn_ppp_select(int min, struct file *file, int type, select_table * st)
+{
+ struct ippp_buf_queue *bf, *bl;
+ unsigned long flags;
+ struct ippp_struct *is;
+
+ is = file->private_data;
+
+ if(is->debug & 0x2)
+ printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",min,type);
+
+ if (!(is->state & IPPP_OPEN))
+ return -EINVAL;
+
+ switch (type) {
+ case SEL_IN:
+ save_flags(flags);
+ cli();
+ bl = is->last;
+ bf = is->first;
+ /*
+ * if IPPP_NOBLOCK is set we return even if we have nothing to read
+ */
+ if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) {
+ select_wait(&is->wq, st);
+ restore_flags(flags);
+ return 0;
+ }
+ is->state &= ~IPPP_NOBLOCK;
+ restore_flags(flags);
+ return 1;
+ case SEL_OUT:
+ /* we're always ready to send .. */
+ return 1;
+ case SEL_EX:
+ select_wait(&is->wq1, st);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * fill up isdn_ppp_read() queue ..
+ */
+
+static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot)
+{
+ struct ippp_buf_queue *bf, *bl;
+ unsigned long flags;
+ unsigned char *nbuf;
+ struct ippp_struct *is;
+
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_WARNING "ippp: illegal slot.\n");
+ return 0;
+ }
+ is = ippp_table[slot];
+
+ if (!(is->state & IPPP_CONNECT)) {
+ printk(KERN_DEBUG "ippp: device not activated.\n");
+ return 0;
+ }
+
+ nbuf = (unsigned char *) kmalloc(len+4, GFP_ATOMIC);
+ if(!nbuf) {
+ printk(KERN_WARNING "ippp: Can't alloc buf\n");
+ return 0;
+ }
+ nbuf[0] = PPP_ALLSTATIONS;
+ nbuf[1] = PPP_UI;
+ nbuf[2] = proto >> 8;
+ nbuf[3] = proto & 0xff;
+ memcpy(nbuf+4, buf, len);
+
+ save_flags(flags);
+ cli();
+
+ bf = is->first;
+ bl = is->last;
+
+ if (bf == bl) {
+ printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
+ bf = bf->next;
+ kfree(bf->buf);
+ is->first = bf;
+ }
+ bl->buf = (char *) nbuf;
+ bl->len = len+4;
+
+ is->last = bl->next;
+ restore_flags(flags);
+
+ if (is->wq)
+ wake_up_interruptible(&is->wq);
+
+ return len;
+}
+
+/*
+ * read() .. non-blocking: ipppd calls it only after select()
+ * reports, that there is data
+ */
+
+int isdn_ppp_read(int min, struct file *file, char *buf, int count)
+{
+ struct ippp_struct *is;
+ struct ippp_buf_queue *b;
+ unsigned long flags;
+
+ is = file->private_data;
+
+ if (!(is->state & IPPP_OPEN))
+ return 0;
+
+ save_flags(flags);
+ cli();
+
+ b = is->first->next;
+ if (!b->buf) {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ if (b->len < count)
+ count = b->len;
+ if (copy_to_user(buf, b->buf, count))
+ count = -EFAULT; /* ugly */
+ kfree(b->buf);
+ b->buf = NULL;
+ is->first = b;
+ restore_flags(flags);
+
+ return count;
+}
+
+/*
+ * ipppd wanna write a packet to the card .. non-blocking
+ */
+
+int isdn_ppp_write(int min, struct file *file, const char *buf, int count)
+{
+ isdn_net_local *lp;
+ struct ippp_struct *is;
+
+ is = file->private_data;
+
+ if (!(is->state & IPPP_CONNECT))
+ return 0;
+
+ lp = is->lp;
+
+ /* -> push it directly to the lowlevel interface */
+
+ if (!lp)
+ printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
+ else {
+ lp->huptimer = 0;
+ if (lp->isdn_device < 0 || lp->isdn_channel < 0)
+ return 0;
+
+ if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
+ (lp->flags & ISDN_NET_CONNECTED)) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(count);
+ if(!skb) {
+ printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
+ return count;
+ }
+ skb->free = 1;
+ copy_from_user(skb_put(skb, count), buf, count);
+ if(isdn_writebuf_skb_stub(lp->isdn_device,lp->isdn_channel,skb) != count) {
+ if(lp->sav_skb) {
+ dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+ printk(KERN_INFO "isdn_ppp_write: freeing sav_skb!\n");
+ }
+ lp->sav_skb = skb;
+ }
+ }
+ }
+
+ return count;
+}
+
+/*
+ * init memory, structures etc.
+ */
+
+int isdn_ppp_init(void)
+{
+ int i, j;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (!(ippp_table[i] = (struct ippp_struct *)
+ kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
+ printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+ for (j = 0; j < i; j++)
+ kfree(ippp_table[i]);
+ return -1;
+ }
+ memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
+ ippp_table[i]->state = 0;
+ ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
+ ippp_table[i]->last = ippp_table[i]->rq;
+
+ for (j = 0; j < NUM_RCV_BUFFS; j++) {
+ ippp_table[i]->rq[j].buf = NULL;
+ ippp_table[i]->rq[j].last = ippp_table[i]->rq +
+ (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
+ ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
+ }
+ }
+ return 0;
+}
+
+void isdn_ppp_cleanup(void)
+{
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ kfree(ippp_table[i]);
+}
+
+/*
+ * handler for incoming packets on a syncPPP interface
+ */
+
+void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
+{
+ struct ippp_struct *is;
+ is = ippp_table[lp->ppp_slot];
+
+ if(is->debug & 0x4)
+ printk(KERN_DEBUG "recv skb, len: %ld\n",skb->len);
+
+ if(net_dev->local.master) {
+ printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
+ net_dev = ((isdn_net_local*) net_dev->local.master->priv)->netdev;
+ }
+
+ if(skb->data[0] == 0xff && skb->data[1] == 0x03)
+ skb_pull(skb,2);
+ else if (is->pppcfg & SC_REJ_COMP_AC) {
+ skb->free = 1;
+ dev_kfree_skb(skb,0 /* FREE_READ */ );
+ return; /* discard it silently */
+ }
+
+#ifdef CONFIG_ISDN_MPP
+ if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
+ int proto;
+ int sqno_end;
+ if (skb->data[0] & 0x1) {
+ proto = skb->data[0];
+ skb_pull(skb,1); /* protocol ID is only 8 bit */
+ } else {
+ proto = ((int) skb->data[0] << 8) + skb->data[1];
+ skb_pull(skb,2);
+ }
+ if (proto == PPP_MP) {
+ isdn_net_local *lpq;
+ int sqno, min_sqno, tseq;
+ u_char BEbyte = skb->data[0];
+ if(is->debug & 0x8)
+ printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto ,
+ (int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
+ (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+ if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) {
+ sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
+ skb_pull(skb,4);
+ } else {
+ sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
+ skb_pull(skb,2);
+ }
+
+ if ((tseq = is->last_link_seqno) >= sqno) {
+ int range = is->range;
+ if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
+ printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %d, last: %d !!!\n", sqno, tseq);
+ else {
+ sqno += range;
+ is->last_link_seqno = sqno;
+ }
+ } else
+ is->last_link_seqno = sqno;
+
+ for (min_sqno = 0, lpq = net_dev->queue;;) {
+ if (ippp_table[lpq->ppp_slot]->last_link_seqno > min_sqno)
+ min_sqno = ippp_table[lpq->ppp_slot]->last_link_seqno;
+ lpq = lpq->next;
+ if (lpq == net_dev->queue)
+ break;
+ }
+ if (min_sqno >= ippp_table[lpq->ppp_slot]->range) { /* OK, every link overflowed */
+ int mask = ippp_table[lpq->ppp_slot]->range - 1; /* range is a power of 2 */
+ isdn_ppp_cleanup_queue(net_dev, min_sqno);
+ isdn_ppp_mask_queue(net_dev, mask);
+ net_dev->ib.next_num &= mask;
+ {
+ struct sqqueue *q = net_dev->ib.sq;
+ while (q) {
+ q->sqno_start &= mask;
+ q->sqno_end &= mask;
+ }
+ }
+ min_sqno &= mask;
+ for (lpq = net_dev->queue;;) {
+ ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
+ lpq = lpq->next;
+ if (lpq == net_dev->queue)
+ break;
+ }
+ }
+ if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
+ printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
+ if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0)
+ return; /* no packet complete */
+ } else
+ sqno_end = sqno;
+
+ /*
+ * MP buffer management .. reorders incoming packets ..
+ * lotsa mem-copies and not heavily tested.
+ *
+ * first check whether there is more than one link in the bundle
+ * then check whether the number is in order
+ */
+ net_dev->ib.modify = 1; /* block timeout-timer */
+ if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
+ /*
+ * packet is not 'in order'
+ */
+ struct sqqueue *q;
+
+ q = (struct sqqueue *) kmalloc(sizeof(struct sqqueue), GFP_ATOMIC);
+ if (!q) {
+ net_dev->ib.modify = 0;
+ printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
+ skb->free = 1;
+ dev_kfree_skb(skb, 0 /* FREE_READ */ );
+ return; /* discard */
+ }
+ q->skb = skb;
+ q->sqno_end = sqno_end;
+ q->sqno_start = sqno;
+ q->timer = jiffies + (ISDN_TIMER_1SEC) * 5; /* timeout after 5 seconds */
+
+ if (!net_dev->ib.sq) {
+ net_dev->ib.sq = q;
+ q->next = NULL;
+ } else {
+ struct sqqueue *ql = net_dev->ib.sq;
+ if (ql->sqno_start > q->sqno_start) {
+ q->next = ql;
+ net_dev->ib.sq = q;
+ } else {
+ while (ql->next && ql->next->sqno_start < q->sqno_start)
+ ql = ql->next;
+ q->next = ql->next;
+ ql->next = q;
+ }
+ }
+ net_dev->ib.modify = 0;
+ return;
+ } else {
+ /*
+ * packet was 'in order' .. push it higher
+ */
+ struct sqqueue *q;
+
+ net_dev->ib.next_num = sqno_end + 1;
+ isdn_ppp_push_higher(net_dev, lp, skb, -1);
+
+ /*
+ * check queue, whether we have still buffered the next packet(s)
+ */
+ while ((q = net_dev->ib.sq) && q->sqno_start == net_dev->ib.next_num) {
+ isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
+ net_dev->ib.sq = q->next;
+ net_dev->ib.next_num = q->sqno_end + 1;
+ kfree(q);
+ }
+ }
+ net_dev->ib.modify = 0;
+
+ } else
+ isdn_ppp_push_higher(net_dev, lp, skb , proto);
+ } else
+#endif
+ isdn_ppp_push_higher(net_dev, lp, skb , -1);
+}
+
+
+/*
+ * push frame to higher layers
+ * note: net_dev has to be master net_dev
+ */
+static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto)
+{
+ struct device *dev = &net_dev->dev;
+ struct ippp_struct *is = ippp_table[lp->ppp_slot];
+
+ if (proto < 0) { /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
+ if (skb->data[0] & 0x01) { /* is it odd? */
+ proto = (unsigned char) skb->data[0];
+ skb_pull(skb,1); /* protocol ID is only 8 bit */
+ } else {
+ proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1];
+ skb_pull(skb,2);
+ }
+ }
+
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto);
+
+ switch (proto) {
+ case PPP_IPX: /* untested */
+ if(is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: _IPX\n");
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IPX);
+ break;
+#ifdef CONFIG_ISDN_PPP_VJ
+ case PPP_VJC_UNCOMP:
+ if(is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
+ if(slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+ printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
+ net_dev->local.stats.rx_dropped++;
+ skb->free = 1;
+ dev_kfree_skb(skb,0 /* FREE_READ */ );
+ return;
+ }
+#endif
+ case PPP_IP:
+ if(is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: IP\n");
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case PPP_VJC_COMP:
+ if(is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+#ifdef CONFIG_ISDN_PPP_VJ
+ {
+ struct sk_buff *skb_old = skb;
+ int pkt_len;
+ skb = dev_alloc_skb(skb_old->len + 40);
+
+ skb_old->free = 1;
+
+ if (!skb) {
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+ net_dev->local.stats.rx_dropped++;
+ dev_kfree_skb(skb_old,0 /* FREE_READ */ );
+ return;
+ }
+ skb->dev = dev;
+ skb_put(skb,skb_old->len + 40);
+ memcpy(skb->data, skb_old->data, skb_old->len);
+ skb->mac.raw = skb->data;
+ pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp,
+ skb->data, skb_old->len);
+ dev_kfree_skb(skb_old,0 /* FREE_READ */ );
+ if(pkt_len < 0) {
+ skb->free = 1;
+ dev_kfree_skb(skb, 0 /* FREE_READ */ );
+ lp->stats.rx_dropped++;
+ return;
+ }
+ skb_trim(skb, pkt_len);
+ skb->protocol = htons(ETH_P_IP);
+ }
+#else
+ printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
+ lp->stats.rx_dropped++;
+ skb->free = 1;
+ dev_kfree_skb(skb,0 /* FREE_READ */ );
+ return;
+#endif
+ break;
+ default:
+ isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_slot); /* push data to pppd device */
+ skb->free = 1;
+ dev_kfree_skb(skb,0 /* FREE_READ */ );
+ return;
+ }
+
+ netif_rx(skb);
+ net_dev->local.stats.rx_packets++;
+ /* Reset hangup-timer */
+ lp->huptimer = 0;
+
+ return;
+}
+
+/*
+ * send ppp frame .. we expect a PIDCOMPressable proto --
+ * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
+ *
+ * VJ compression may change skb pointer!!! .. requeue with old
+ * skb isn't allowed!!
+ */
+
+static void isdn_ppp_skb_destructor(struct sk_buff *skb)
+{
+ char outstr[80],*outpnt=outstr;
+ int i;
+
+ *outpnt = 0;
+ for(i=0;i<24 && i<skb->len;i++) {
+ sprintf(outpnt,"%02x ",skb->data[i]);
+ outpnt += 3;
+ }
+ printk(KERN_DEBUG "ippp_dstrct: %s\n",outstr);
+}
+
+int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct device *mdev = ((isdn_net_local *) (dev->priv) )->master; /* get master (for redundancy) */
+ isdn_net_local *lp,*mlp;
+ isdn_net_dev *nd;
+ int proto = PPP_IP; /* 0x21 */
+ struct ippp_struct *ipt,*ipts;
+
+ if(mdev)
+ mlp = (isdn_net_local *) (mdev->priv);
+ else
+ mlp = (isdn_net_local *) (dev->priv);
+ nd = mlp->netdev; /* get master lp */
+ ipts = ippp_table[mlp->ppp_slot];
+
+ if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */
+ if(ipts->debug & 0x1) {
+ printk(KERN_INFO "%s: IP frame delayed.\n",dev->name);
+ skb->destructor = isdn_ppp_skb_destructor;
+ }
+ return 1;
+ }
+
+ skb->destructor = NULL;
+
+ lp = nd->queue; /* get lp on top of queue */
+ if(lp->sav_skb) { /* find a non-busy device */
+ isdn_net_local *nlp = lp->next;
+ while(lp->sav_skb) {
+ if(lp == nlp)
+ return 1;
+ nlp = nd->queue = nd->queue->next;
+ }
+ lp = nlp;
+ }
+ ipt = ippp_table[lp->ppp_slot];
+
+ lp->huptimer = 0;
+
+ /*
+ * after this line .. requeueing in the device queue is no longer allowed!!!
+ */
+
+ if(ipt->debug & 0x4)
+ printk(KERN_DEBUG "xmit skb, len %ld\n",skb->len);
+
+#ifdef CONFIG_ISDN_PPP_VJ
+ if (ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes .. but this check again */
+ struct sk_buff *new_skb;
+
+ new_skb = dev_alloc_skb(skb->len);
+ if(new_skb) {
+ u_char *buf;
+ int pktlen;
+
+ new_skb->dev = skb->dev;
+ new_skb->free = 1;
+ skb_put(new_skb,skb->len);
+ buf = skb->data;
+
+ pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
+ &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+
+ if(buf != skb->data) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*) */
+ if(new_skb->data != buf)
+ printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
+ dev_kfree_skb(skb,FREE_WRITE);
+ skb = new_skb;
+ }
+ else {
+ dev_kfree_skb(new_skb,0 /* FREE_WRITE */ );
+ }
+
+ skb_trim(skb,pktlen);
+ if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) { /* cslip? style -> PPP */
+ proto = PPP_VJC_COMP;
+ skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
+ } else {
+ if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
+ proto = PPP_VJC_UNCOMP;
+ skb->data[0] = (skb->data[0] & 0x0f) | 0x40;
+ }
+ }
+ }
+#endif
+
+ if(ipt->debug & 0x24)
+ printk(KERN_DEBUG "xmit2 skb, len %ld, proto %04x\n",skb->len,proto);
+
+#ifdef CONFIG_ISDN_MPP
+ if (ipt->mpppcfg & SC_MP_PROT) {
+ /* we get mp_seqno from static isdn_net_local */
+ long mp_seqno = ipts->mp_seqno;
+ ipts->mp_seqno++;
+ nd->queue = nd->queue->next;
+ if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
+ skb_push(skb, 3);
+ mp_seqno &= 0xfff;
+ skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8); /* (B)egin & (E)ndbit .. */
+ skb->data[1] = mp_seqno & 0xff;
+ skb->data[2] = proto; /* PID compression */
+ } else {
+ skb_push(skb, 5);
+ skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */
+ skb->data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */
+ skb->data[2] = (mp_seqno >> 8) & 0xff;
+ skb->data[3] = (mp_seqno >> 0) & 0xff;
+ skb->data[4] = proto; /* PID compression */
+ }
+ proto = PPP_MP; /* MP Protocol, 0x003d */
+ }
+#endif
+ skb_push(skb,4);
+ skb->data[0] = 0xff; /* All Stations */
+ skb->data[1] = 0x03; /* Unnumbered information */
+ skb->data[2] = proto >> 8;
+ skb->data[3] = proto & 0xff;
+
+ /* tx-stats are now updated via BSENT-callback */
+ if(isdn_net_send_skb(dev , lp , skb)) {
+ if(lp->sav_skb) { /* whole sav_skb processing with disabled IRQs ?? */
+ printk(KERN_ERR "%s: whoops .. there is another stored skb!\n",dev->name);
+ dev_kfree_skb(skb,FREE_WRITE);
+ }
+ else
+ lp->sav_skb = skb;
+ }
+ return 0;
+}
+
+void isdn_ppp_free_sqqueue(isdn_net_dev * p)
+{
+ struct sqqueue *q = p->ib.sq;
+
+ p->ib.sq = NULL;
+ while(q) {
+ struct sqqueue *qn = q->next;
+ if(q->skb) {
+ q->skb->free = 1;
+ dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+ }
+ kfree(q);
+ q = qn;
+ }
+
+}
+
+void isdn_ppp_free_mpqueue(isdn_net_dev * p)
+{
+ struct mpqueue *ql, *q = p->mp_last;
+ while (q) {
+ ql = q->next;
+ q->skb->free = 1;
+ dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+ kfree(q);
+ q = ql;
+ }
+}
+
+#ifdef CONFIG_ISDN_MPP
+
+static int isdn_ppp_bundle(struct ippp_struct *is, int unit)
+{
+ char ifn[IFNAMSIZ + 1];
+ long flags;
+ isdn_net_dev *p;
+ isdn_net_local *lp,*nlp;
+
+ sprintf(ifn, "ippp%d", unit);
+ p = isdn_net_findif(ifn);
+ if (!p)
+ return -1;
+
+ isdn_timer_ctrl(ISDN_TIMER_IPPP, 1); /* enable timer for ippp/MP */
+
+ save_flags(flags);
+ cli();
+
+ nlp = is->lp;
+
+ lp = p->queue;
+ p->ib.bundled = 1;
+ nlp->last = lp->last;
+ lp->last->next = nlp;
+ lp->last = nlp;
+ nlp->next = lp;
+ p->queue = nlp;
+
+ ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
+/* maybe also SC_CCP stuff */
+ ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
+ (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
+
+ ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
+ (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+#if 0
+ if (ippp_table[nlp->ppp_slot]->mpppcfg != ippp_table[lp->ppp_slot]->mpppcfg) {
+ printk(KERN_WARNING "isdn_ppp_bundle: different MP options %04x and %04x\n",
+ ippp_table[nlp->ppp_slot]->mpppcfg, ippp_table[lp->ppp_slot]->mpppcfg);
+ }
+#endif
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
+{
+ struct mpqueue *q = dev->mp_last;
+ while (q) {
+ q->sqno &= mask;
+ q = q->next;
+ }
+}
+
+
+static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int BEbyte, int *sqnop, int min_sqno)
+{
+ struct mpqueue *qe, *q1, *q;
+ long cnt, flags;
+ int pktlen, sqno_end;
+ int sqno = *sqnop;
+
+ q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_KERNEL);
+ if (!q1) {
+ printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n");
+ save_flags(flags);
+ cli();
+ isdn_ppp_cleanup_queue(dev, min_sqno);
+ restore_flags(flags);
+ return -1;
+ }
+ q1->skb = *skb;
+ q1->sqno = sqno;
+ q1->BEbyte = BEbyte;
+ q1->time = jiffies;
+
+ save_flags(flags);
+ cli();
+
+ if (!(q = dev->mp_last)) {
+ dev->mp_last = q1;
+ q1->next = NULL;
+ q1->last = NULL;
+ isdn_ppp_cleanup_queue(dev, min_sqno); /* not necessary */
+ restore_flags(flags);
+ return -1;
+ }
+ for (;;) { /* the faster way would be to step from the queue-end to the start */
+ if (sqno > q->sqno) {
+ if (q->next) {
+ q = q->next;
+ continue;
+ }
+ q->next = q1;
+ q1->next = NULL;
+ q1->last = q;
+ break;
+ }
+ if (sqno == q->sqno)
+ printk(KERN_WARNING "isdn_fill_mpqueue: illegal sqno received!!\n");
+ q1->last = q->last;
+ q1->next = q;
+ if (q->last) {
+ q->last->next = q1;
+ } else
+ dev->mp_last = q1;
+ q->last = q1;
+ break;
+ }
+
+/* now we check whether we completed a packet with this fragment */
+ pktlen = -q1->skb->len;
+ q = q1;
+ cnt = q1->sqno;
+ while (!(q->BEbyte & MP_END_FRAG)) {
+ cnt++;
+ if (!(q->next) || q->next->sqno != cnt) {
+ isdn_ppp_cleanup_queue(dev, min_sqno);
+ restore_flags(flags);
+ return -1;
+ }
+ pktlen += q->skb->len;
+ q = q->next;
+ }
+ pktlen += q->skb->len;
+ qe = q;
+
+ q = q1;
+ cnt = q1->sqno;
+ while (!(q->BEbyte & MP_BEGIN_FRAG)) {
+ cnt--;
+ if (!(q->last) || q->last->sqno != cnt) {
+ isdn_ppp_cleanup_queue(dev, min_sqno);
+ restore_flags(flags);
+ return -1;
+ }
+ pktlen += q->skb->len;
+ q = q->last;
+ }
+ pktlen += q->skb->len;
+
+ if (q->last)
+ q->last->next = qe->next;
+ else
+ dev->mp_last = qe->next;
+
+ if (qe->next)
+ qe->next->last = q->last;
+ qe->next = NULL;
+ sqno_end = qe->sqno;
+ *sqnop = q->sqno;
+
+ isdn_ppp_cleanup_queue(dev, min_sqno);
+ restore_flags(flags);
+
+ *skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */
+
+ if (!(*skb)) {
+ while (q) {
+ struct mpqueue *ql = q->next;
+ q->skb->free = 1;
+ dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+ kfree(q);
+ q = ql;
+ }
+ return -2;
+ }
+ cnt = 0;
+ skb_put(*skb,pktlen);
+ while (q) {
+ struct mpqueue *ql = q->next;
+ memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
+ cnt += q->skb->len;
+ q->skb->free = 1;
+ dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+ kfree(q);
+ q = ql;
+ }
+
+ return sqno_end;
+}
+
+/*
+ * remove stale packets from list
+ */
+
+static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min_sqno)
+{
+#ifdef CONFIG_ISDN_PPP_VJ
+ int toss = 0;
+#endif
+/* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
+ eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
+ wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
+ bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete
+ groesser als mrru ist: raus damit , Pakete muessen allerdings zusammenhaengen sonst koennte
+ ja ein Paket mit B und eins mit E dazwischenpassen */
+
+ struct mpqueue *ql, *q = dev->mp_last;
+ while (q) {
+ if (q->sqno < min_sqno) {
+ if (q->BEbyte & MP_END_FRAG) {
+ printk(KERN_DEBUG "ippp: freeing stale packet!\n");
+ if ((dev->mp_last = q->next))
+ q->next->last = NULL;
+ while (q) {
+ ql = q->last;
+ q->skb->free = 1;
+ dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+ kfree(q);
+#ifdef CONFIG_ISDN_PPP_VJ
+ toss = 1;
+#endif
+ q = ql;
+ }
+ q = dev->mp_last;
+ } else
+ q = q->next;
+ } else
+ break;
+ }
+#ifdef CONFIG_ISDN_PPP_VJ
+ /* did we free a stale frame ? */
+ if(toss)
+ slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp);
+#endif
+}
+
+/*
+ * a buffered packet timed-out?
+ */
+
+#endif
+
+void isdn_ppp_timer_timeout(void)
+{
+#ifdef CONFIG_ISDN_MPP
+ isdn_net_dev *net_dev = dev->netdev;
+ struct sqqueue *q, *ql = NULL, *qn;
+
+ while (net_dev) {
+ isdn_net_local *lp = &net_dev->local;
+ if (net_dev->ib.modify || lp->master) { /* interface locked or slave?*/
+ net_dev = net_dev->next;
+ continue;
+ }
+
+ q = net_dev->ib.sq;
+ while (q) {
+ if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
+
+#ifdef CONFIG_ISDN_PPP_VJ
+ /* did we step over a missing frame ? */
+ if(q->sqno_start != net_dev->ib.next_num)
+ slhc_toss(ippp_table[lp->ppp_slot]->slcomp);
+#endif
+
+ ql = net_dev->ib.sq;
+ net_dev->ib.sq = q->next;
+ net_dev->ib.next_num = q->sqno_end + 1;
+ q->next = NULL;
+ for (; ql;) {
+ isdn_ppp_push_higher(net_dev, lp, ql->skb, -1);
+ qn = ql->next;
+ kfree(ql);
+ ql = qn;
+ }
+ q = net_dev->ib.sq;
+ } else
+ q = q->next;
+ }
+ net_dev = net_dev->next;
+ }
+#endif
+}
+
+/*
+ * network device ioctl handlers
+ */
+
+static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *dev)
+{
+ struct ppp_stats *res, t;
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+ res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+
+ /* build a temporary stat struct and copy it to user space */
+
+ memset (&t, 0, sizeof(struct ppp_stats));
+ if(dev->flags & IFF_UP) {
+ t.p.ppp_ipackets = lp->stats.rx_packets;
+ t.p.ppp_ierrors = lp->stats.rx_errors;
+ t.p.ppp_opackets = lp->stats.tx_packets;
+ t.p.ppp_oerrors = lp->stats.tx_errors;
+#ifdef CONFIG_ISDN_PPP_VJ
+ if(slot >= 0 && ippp_table[slot]->slcomp) {
+ struct slcompress *slcomp = ippp_table[slot]->slcomp;
+ t.vj.vjs_packets = slcomp->sls_o_compressed+slcomp->sls_o_uncompressed;
+ t.vj.vjs_compressed = slcomp->sls_o_compressed;
+ t.vj.vjs_searches = slcomp->sls_o_searches;
+ t.vj.vjs_misses = slcomp->sls_o_misses;
+ t.vj.vjs_errorin = slcomp->sls_i_error;
+ t.vj.vjs_tossed = slcomp->sls_i_tossed;
+ t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
+ t.vj.vjs_compressedin = slcomp->sls_i_compressed;
+ }
+#endif
+ }
+ return copy_to_user (res, &t, sizeof (struct ppp_stats)) ? -EFAULT : 0;
+}
+
+int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ int error = 0;
+ char *r;
+ int len;
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+#if 0
+ printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_slot);
+#endif
+
+ if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGPPPVER:
+ r = (char *) ifr->ifr_ifru.ifru_data;
+ len = strlen(PPP_VERSION) + 1;
+ if (copy_to_user(r, PPP_VERSION, len))
+ error = -EFAULT;
+ break;
+ case SIOCGPPPSTATS:
+ error = isdn_ppp_dev_ioctl_stats (lp->ppp_slot, ifr, dev);
+ break;
+ default:
+ error = -EINVAL;
+ break;
+ }
+ return error;
+}
+
+static int isdn_ppp_if_get_unit(char *name)
+{
+ int len, i, unit = 0, deci;
+
+ len = strlen(name);
+
+ if(strncmp("ippp",name,4) || len > 8)
+ return -1;
+
+ for (i = 0, deci = 1; i < len; i++, deci *= 10) {
+ char a = name[len-i-1];
+ if (a >= '0' && a <= '9')
+ unit += (a - '0') * deci;
+ else
+ break;
+ }
+ if (!i || len-i != 4)
+ unit = -1;
+
+ return unit;
+}
+
+
+int isdn_ppp_dial_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+ isdn_net_dev *ndev;
+ isdn_net_local *lp;
+ struct device *sdev;
+
+ if(!(ndev = isdn_net_findif(name)))
+ return 1;
+ lp = &ndev->local;
+ if(!(lp->flags & ISDN_NET_CONNECTED))
+ return 5;
+
+ sdev = lp->slave;
+ while(sdev)
+ {
+ isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+ if(!(mlp->flags & ISDN_NET_CONNECTED))
+ break;
+ sdev = mlp->slave;
+ }
+ if(!sdev)
+ return 2;
+
+ isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+int isdn_ppp_hangup_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+ isdn_net_dev *ndev;
+ isdn_net_local *lp;
+ struct device *sdev;
+
+ if(!(ndev = isdn_net_findif(name)))
+ return 1;
+ lp = &ndev->local;
+ if(!(lp->flags & ISDN_NET_CONNECTED))
+ return 5;
+
+ sdev = lp->slave;
+ while(sdev)
+ {
+ isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+ if((mlp->flags & ISDN_NET_CONNECTED))
+ break;
+ sdev = mlp->slave;
+ }
+ if(!sdev)
+ return 2;
+
+ isdn_net_hangup(sdev);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
diff --git a/drivers/isdn/isdn_ppp.h b/drivers/isdn/isdn_ppp.h
new file mode 100644
index 000000000..70de75950
--- /dev/null
+++ b/drivers/isdn/isdn_ppp.h
@@ -0,0 +1,56 @@
+/* $Id: isdn_ppp.h,v 1.4 1996/05/06 11:34:56 hipp Exp $
+ *
+ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * 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.
+ *
+ * $Log: isdn_ppp.h,v $
+ * Revision 1.4 1996/05/06 11:34:56 hipp
+ * fixed a few bugs
+ *
+ * Revision 1.3 1996/04/30 09:33:10 fritz
+ * Removed compatibility-macros.
+ *
+ * Revision 1.2 1996/04/20 16:35:11 fritz
+ * Changed isdn_ppp_receive to use sk_buff as parameter.
+ * Added definition of isdn_ppp_dial_slave and ippp_table.
+ *
+ * Revision 1.1 1996/01/10 21:39:10 fritz
+ * Initial revision
+ *
+ */
+
+extern void isdn_ppp_timer_timeout(void);
+extern int isdn_ppp_read(int , struct file *, char *, int);
+extern int isdn_ppp_write(int , struct file *, const char *, int);
+extern int isdn_ppp_open(int , struct file *);
+extern int isdn_ppp_init(void);
+extern void isdn_ppp_cleanup(void);
+extern int isdn_ppp_free(isdn_net_local *);
+extern int isdn_ppp_bind(isdn_net_local *);
+extern int isdn_ppp_xmit(struct sk_buff *, struct device *);
+extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
+extern int isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int);
+extern void isdn_ppp_free_mpqueue(isdn_net_dev *);
+extern void isdn_ppp_free_sqqueue(isdn_net_dev *);
+extern int isdn_ppp_select(int, struct file *, int, select_table *);
+extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
+extern void isdn_ppp_release(int, struct file *);
+extern int isdn_ppp_dial_slave(char *);
+extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
+
+extern struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c
new file mode 100644
index 000000000..e1522022f
--- /dev/null
+++ b/drivers/isdn/isdn_tty.c
@@ -0,0 +1,2805 @@
+/* $Id: isdn_tty.c,v 1.21 1996/06/24 17:40:28 fritz Exp $
+ *
+ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ *
+ * 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.
+ *
+ * $Log: isdn_tty.c,v $
+ * Revision 1.21 1996/06/24 17:40:28 fritz
+ * Bugfix: Did not compile without CONFIG_ISDN_AUDIO
+ *
+ * Revision 1.20 1996/06/15 14:59:39 fritz
+ * Fixed isdn_tty_tint() to handle partially sent
+ * sk_buffs.
+ *
+ * Revision 1.19 1996/06/12 15:53:56 fritz
+ * Bugfix: AT+VTX and AT+VRX could be executed without
+ * having a connection.
+ * Missing check for NULL tty in isdn_tty_flush_buffer().
+ *
+ * Revision 1.18 1996/06/07 11:17:33 tsbogend
+ * added missing #ifdef CONFIG_ISDN_AUDIO to make compiling without
+ * audio support possible
+ *
+ * Revision 1.17 1996/06/06 14:55:47 fritz
+ * Changed to support DTMF decoding on audio playback also.
+ * Bugfix: Added check for invalid info->isdn_driver in
+ * isdn_tty_senddown().
+ * Clear ncarrier flag on last close() of a tty.
+ *
+ * Revision 1.16 1996/06/05 02:24:12 fritz
+ * Added DTMF decoder for audio mode.
+ *
+ * Revision 1.15 1996/06/03 20:35:01 fritz
+ * Fixed typos.
+ *
+ * Revision 1.14 1996/06/03 20:12:19 fritz
+ * Fixed typos.
+ * Added call to write_wakeup via isdn_tty_flush_buffer()
+ * in isdn_tty_modem_hup().
+ *
+ * Revision 1.13 1996/05/31 01:33:29 fritz
+ * Changed buffering due to bad performance with mgetty.
+ * Now sk_buff is delayed allocated in isdn_tty_senddown
+ * using xmit_buff like in standard serial driver.
+ * Fixed module locking.
+ * Added DLE-DC4 handling in voice mode.
+ *
+ * Revision 1.12 1996/05/19 01:34:40 fritz
+ * Bugfix: ATS returned error.
+ * Register 20 made readonly.
+ *
+ * Revision 1.11 1996/05/18 01:37:03 fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.10 1996/05/17 03:51:49 fritz
+ * Changed DLE handling for audio receive.
+ *
+ * Revision 1.9 1996/05/11 21:52:07 fritz
+ * Changed queue management to use sk_buffs.
+ *
+ * Revision 1.8 1996/05/10 08:49:43 fritz
+ * Checkin before major changes of tty-code.
+ *
+ * Revision 1.7 1996/05/07 09:15:09 fritz
+ * Reorganized and general cleanup.
+ * Bugfixes:
+ * - Audio-transmit working now.
+ * - "NO CARRIER" now reported, when hanging up with DTR low.
+ * - Corrected CTS handling.
+ *
+ * Revision 1.6 1996/05/02 03:59:25 fritz
+ * Bugfixes:
+ * - On dialout, layer-2 setup had been incomplete
+ * when using new auto-layer2 feature.
+ * - On hangup, "NO CARRIER" message sometimes missing.
+ *
+ * Revision 1.5 1996/04/30 21:05:25 fritz
+ * Test commit
+ *
+ * Revision 1.4 1996/04/20 16:39:54 fritz
+ * Changed all io to go through generic routines in isdn_common.c
+ * Fixed a real ugly bug in modem-emulator: 'ATA' had been accepted
+ * even when a call has been cancelled from the remote machine.
+ *
+ * Revision 1.3 1996/02/11 02:12:32 fritz
+ * Bugfixes according to similar fixes in standard serial.c of kernel.
+ *
+ * Revision 1.2 1996/01/22 05:12:25 fritz
+ * replaced my_atoi by simple_strtoul
+ *
+ * Revision 1.1 1996/01/09 04:13:18 fritz
+ * Initial revision
+ *
+ */
+
+#include <asm/uaccess.h>
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#define VBUF 0x3e0
+#define VBUFX (VBUF/16)
+#endif
+
+/* Prototypes */
+
+static int isdn_tty_edit_at(const char *, int, modem_info *, int);
+static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
+static void isdn_tty_modem_reset_regs(atemu *, int);
+static void isdn_tty_cmd_ATA(modem_info *);
+static void isdn_tty_at_cout(char *, modem_info *);
+static void isdn_tty_flush_buffer(struct tty_struct *);
+
+/* Leave this unchanged unless you know what you do! */
+#define MODEM_PARANOIA_CHECK
+#define MODEM_DO_RESTART
+
+static char *isdn_ttyname_ttyI = "ttyI";
+static char *isdn_ttyname_cui = "cui";
+static int bit2si[8] = {1,5,7,7,7,7,7,7};
+static int si2bit[8] = {4,1,4,4,4,4,4,4};
+
+char *isdn_tty_revision = "$Revision: 1.21 $";
+
+#define DLE 0x10
+#define ETX 0x03
+#define DC4 0x14
+
+/* isdn_tty_try_read() is called from within isdn_receive_callback()
+ * to stuff incoming data directly into a tty's flip-buffer. This
+ * is done to speed up tty-receiving if the receive-queue is empty.
+ * This routine MUST be called with interrupts off.
+ * Return:
+ * 1 = Success
+ * 0 = Failure, data has to be buffered and later processed by
+ * isdn_tty_readmodem().
+ */
+int isdn_tty_try_read(modem_info *info, struct sk_buff *skb)
+{
+ int c;
+ int len;
+ struct tty_struct *tty;
+
+ if (info->online) {
+ if ((tty = info->tty)) {
+ if (info->mcr & UART_MCR_RTS) {
+ c = TTY_FLIPBUF_SIZE - tty->flip.count;
+ len = skb->len + skb->users;
+ if (c >= len) {
+ if (skb->users)
+ while (skb->len--) {
+ if (*skb->data == DLE)
+ tty_insert_flip_char(tty, DLE, 0);
+ tty_insert_flip_char(tty, *skb->data++, 0);
+ }
+ else {
+ memcpy(tty->flip.char_buf_ptr,
+ skb->data, len);
+ tty->flip.count += len;
+ tty->flip.char_buf_ptr += len;
+ memset(tty->flip.flag_buf_ptr, 0, len);
+ tty->flip.flag_buf_ptr += len;
+ }
+ if (info->emu.mdmreg[12] & 128)
+ tty->flip.flag_buf_ptr[len - 1] = 0xff;
+ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+ skb->free = 1;
+ kfree_skb(skb, FREE_READ);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
+ * It tries getting received data from the receive queue an stuff it into
+ * the tty's flip-buffer.
+ */
+void isdn_tty_readmodem(void)
+{
+ int resched = 0;
+ int midx;
+ int i;
+ int c;
+ int r;
+ ulong flags;
+ struct tty_struct *tty;
+ modem_info *info;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if ((midx = dev->m_idx[i]) >= 0) {
+ info = &dev->mdm.info[midx];
+ if (info->online) {
+ r = 0;
+#ifdef CONFIG_ISDN_AUDIO
+ isdn_audio_eval_dtmf(info);
+#endif
+ if ((tty = info->tty)) {
+ if (info->mcr & UART_MCR_RTS) {
+ c = TTY_FLIPBUF_SIZE - tty->flip.count;
+ if (c > 0) {
+ save_flags(flags);
+ cli();
+ r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
+ tty->flip.char_buf_ptr,
+ tty->flip.flag_buf_ptr, c, 0);
+ /* CISCO AsyncPPP Hack */
+ if (!(info->emu.mdmreg[12] & 128))
+ memset(tty->flip.flag_buf_ptr, 0, r);
+ tty->flip.count += r;
+ tty->flip.flag_buf_ptr += r;
+ tty->flip.char_buf_ptr += r;
+ if (r)
+ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+ restore_flags(flags);
+ }
+ } else
+ r = 1;
+ } else
+ r = 1;
+ if (r) {
+ info->rcvsched = 0;
+ resched = 1;
+ } else
+ info->rcvsched = 1;
+ }
+ }
+ }
+ if (!resched)
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
+}
+
+void isdn_tty_cleanup_xmit(modem_info *info)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (skb_queue_len(&info->xmit_queue))
+ while ((skb = skb_dequeue(&info->xmit_queue))) {
+ skb->free = 1;
+ kfree_skb(skb, FREE_WRITE);
+ }
+ if (skb_queue_len(&info->dtmf_queue))
+ while ((skb = skb_dequeue(&info->dtmf_queue))) {
+ skb->free = 1;
+ kfree_skb(skb, FREE_WRITE);
+ }
+ restore_flags(flags);
+}
+
+static void isdn_tty_tint(modem_info *info)
+{
+ struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
+ int len, slen;
+
+ if (!skb)
+ return;
+ len = skb->len;
+ if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
+ info->isdn_channel, skb)) == len) {
+ struct tty_struct *tty = info->tty;
+ info->send_outstanding++;
+ info->msr |= UART_MSR_CTS;
+ info->lsr |= UART_LSR_TEMT;
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup) (tty);
+ wake_up_interruptible(&tty->write_wait);
+ return;
+ }
+ if (slen > 0)
+ skb_pull(skb,slen);
+ skb_queue_head(&info->xmit_queue, skb);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+int isdn_tty_countDLE(unsigned char *buf, int len)
+{
+ int count = 0;
+
+ while (len--)
+ if (*buf++ == DLE)
+ count++;
+ return count;
+}
+
+/* This routine is called from within isdn_tty_write() to perform
+ * DLE-decoding when sending audio-data.
+ */
+static int isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len)
+{
+ unsigned char *p = &info->xmit_buf[info->xmit_count];
+ int count = 0;
+
+ while (len>0) {
+ if (m->lastDLE) {
+ m->lastDLE = 0;
+ switch (*p) {
+ case DLE:
+ /* Escape code */
+ if (len>1)
+ memmove(p,p+1,len-1);
+ p--;
+ count++;
+ break;
+ case ETX:
+ /* End of data */
+ info->vonline |= 4;
+ return count;
+ case DC4:
+ /* Abort RX */
+ info->vonline &= ~1;
+ isdn_tty_at_cout("\020\003", info);
+ if (!info->vonline)
+ isdn_tty_at_cout("\r\nVCON\r\n", info);
+ /* Fall through */
+ case 'q':
+ case 's':
+ /* Silence */
+ if (len>1)
+ memmove(p,p+1,len-1);
+ p--;
+ break;
+ }
+ } else {
+ if (*p == DLE)
+ m->lastDLE = 1;
+ else
+ count++;
+ }
+ p++;
+ len--;
+ }
+ if (len<0) {
+ printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
+ return 0;
+ }
+ return count;
+}
+
+/* This routine is called from within isdn_tty_write() when receiving
+ * audio-data. It interrupts receiving, if an character other than
+ * ^S or ^Q is sent.
+ */
+static int isdn_tty_end_vrx(const char *buf, int c, int from_user)
+{
+ char tmpbuf[VBUF];
+ char *p;
+
+ if (c > VBUF) {
+ printk(KERN_ERR "isdn_tty: (end_vrx) BUFFER OVERFLOW!!!\n");
+ return 1;
+ }
+ if (from_user) {
+ copy_from_user(tmpbuf, buf, c);
+ p = tmpbuf;
+ } else
+ p = (char *)buf;
+ while (c--) {
+ if ((*p != 0x11) && (*p != 0x13))
+ return 1;
+ p++;
+ }
+ return 0;
+}
+
+static int voice_cf[7] = { 1, 1, 4, 3, 2, 1, 1 };
+
+#endif /* CONFIG_ISDN_AUDIO */
+
+/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
+ * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
+ * outgoing data from the tty's xmit-buffer, handles voice-decompression or
+ * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
+ */
+static void isdn_tty_senddown(modem_info * info)
+{
+ unsigned char *buf = info->xmit_buf;
+ int buflen;
+ int skb_res;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (!(buflen = info->xmit_count)) {
+ restore_flags(flags);
+ return;
+ }
+ if (info->isdn_driver < 0) {
+ info->xmit_count = 0;
+ restore_flags(flags);
+ return;
+ }
+ skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 2) {
+ /* For now, ifmt is fixed to 1 (alaw), since this
+ * is used with ISDN everywhere in the world, except
+ * US, Canada and Japan.
+ * Later, when US-ISDN protocols are implemented,
+ * this setting will depend on the D-channel protocol.
+ */
+ int ifmt = 1;
+ int skb_len;
+ unsigned char hbuf[VBUF];
+
+ memcpy(hbuf,info->xmit_buf,buflen);
+ info->xmit_count = 0;
+ restore_flags(flags);
+ /* voice conversion/decompression */
+ skb_len = buflen * voice_cf[info->emu.vpar[3]];
+ skb = dev_alloc_skb(skb_len + skb_res);
+ if (!skb) {
+ printk(KERN_WARNING
+ "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
+ return;
+ }
+ skb_reserve(skb, skb_res);
+ switch (info->emu.vpar[3]) {
+ case 2:
+ case 3:
+ case 4:
+ /* adpcm, compatible to ZyXel 1496 modem
+ * with ROM revision 6.01
+ */
+ buflen = isdn_audio_adpcm2xlaw(info->adpcms,
+ ifmt,
+ hbuf,
+ skb_put(skb,skb_len),
+ buflen);
+ skb_trim(skb, buflen);
+ break;
+ case 5:
+ /* a-law */
+ if (!ifmt)
+ isdn_audio_alaw2ulaw(hbuf,buflen);
+ memcpy(skb_put(skb,buflen),hbuf,buflen);
+ break;
+ case 6:
+ /* u-law */
+ if (ifmt)
+ isdn_audio_ulaw2alaw(hbuf,buflen);
+ memcpy(skb_put(skb,buflen),hbuf,buflen);
+ break;
+ }
+ if (info->vonline & 4) {
+ info->vonline &= ~6;
+ if (!info->vonline)
+ isdn_tty_at_cout("\r\nVCON\r\n",info);
+ }
+ } else {
+#endif /* CONFIG_ISDN_AUDIO */
+ skb = dev_alloc_skb(buflen + skb_res);
+ if (!skb) {
+ printk(KERN_WARNING
+ "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
+ restore_flags(flags);
+ return;
+ }
+ skb_reserve(skb, skb_res);
+ memcpy(skb_put(skb,buflen),buf,buflen);
+ info->xmit_count = 0;
+ restore_flags(flags);
+#ifdef CONFIG_ISDN_AUDIO
+ }
+#endif
+ skb->free = 1;
+ if (info->emu.mdmreg[13] & 2)
+ /* Add T.70 simplified header */
+ memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+ skb_queue_tail(&info->xmit_queue, skb);
+ if ((info->emu.mdmreg[12] & 0x10) != 0)
+ info->msr &= UART_MSR_CTS;
+ info->lsr &= UART_LSR_TEMT;
+}
+
+/************************************************************
+ *
+ * Modem-functions
+ *
+ * mostly "stolen" from original Linux-serial.c and friends.
+ *
+ ************************************************************/
+
+/* The next routine is called once from within timer-interrupt
+ * triggered within isdn_tty_modem_ncarrier(). It calls
+ * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
+ * into the tty's flip-buffer.
+ */
+static void isdn_tty_modem_do_ncarrier(unsigned long data)
+{
+ modem_info * info = (modem_info *)data;
+ isdn_tty_modem_result(3, info);
+}
+
+/* Next routine is called, whenever the DTR-signal is raised.
+ * It checks the ncarrier-flag, and triggers the above routine
+ * when necessary. The ncarrier-flag is set, whenever DTR goes
+ * low.
+ */
+static void isdn_tty_modem_ncarrier(modem_info * info)
+{
+ if (info->ncarrier) {
+ info->ncarrier = 0;
+ info->nc_timer.expires = jiffies + HZ;
+ info->nc_timer.function = isdn_tty_modem_do_ncarrier;
+ info->nc_timer.data = (unsigned long)info;
+ add_timer(&info->nc_timer);
+ }
+}
+
+/* isdn_tty_dial() performs dialing of a tty an the necessary
+ * setup of the lower levels before that.
+ */
+static void isdn_tty_dial(char *n, modem_info * info, atemu * m)
+{
+ int usg = ISDN_USAGE_MODEM;
+ int si = 7;
+ int l2 = m->mdmreg[14];
+ isdn_ctrl cmd;
+ ulong flags;
+ int i;
+ int j;
+
+ for (j=7;j>=0;j--)
+ if (m->mdmreg[18] & (1<<j)) {
+ si = bit2si[j];
+ break;
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ if (si == 1) {
+ l2 = 4;
+ usg = ISDN_USAGE_VOICE;
+ }
+#endif
+ m->mdmreg[20] = si2bit[si];
+ save_flags(flags);
+ cli();
+ i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1);
+ if (i < 0) {
+ restore_flags(flags);
+ isdn_tty_modem_result(6, info);
+ } else {
+ info->isdn_driver = dev->drvmap[i];
+ info->isdn_channel = dev->chanmap[i];
+ info->drv_index = i;
+ dev->m_idx[i] = info->line;
+ dev->usage[i] |= ISDN_USAGE_OUTGOING;
+ isdn_info_update();
+ restore_flags(flags);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ strcpy(cmd.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETEAZ;
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver),
+ si, m->mdmreg[19]);
+ cmd.command = ISDN_CMD_DIAL;
+ info->dialing = 1;
+ strcpy(dev->num[i], n);
+ isdn_info_update();
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ }
+}
+
+/* isdn_tty_hangup() disassociates a tty from the real
+ * ISDN-line (hangup). The usage-status is cleared
+ * and some cleanup is done also.
+ */
+void isdn_tty_modem_hup(modem_info * info)
+{
+ isdn_ctrl cmd;
+ int usage;
+
+ if (!info)
+ return;
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
+#endif
+ info->rcvsched = 0;
+ info->online = 0;
+ isdn_tty_flush_buffer(info->tty);
+ if (info->vonline & 1) {
+ /* voice-recording, add DLE-ETX */
+ isdn_tty_at_cout("\020\003", info);
+ }
+ if (info->vonline & 2) {
+ /* voice-playing, add DLE-DC4 */
+ isdn_tty_at_cout("\020\024", info);
+ }
+ info->vonline = 0;
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->dtmf_state) {
+ kfree(info->dtmf_state);
+ info->dtmf_state = NULL;
+ }
+ if (info->adpcms) {
+ kfree(info->adpcms);
+ info->adpcms = NULL;
+ }
+ if (info->adpcmr) {
+ kfree(info->adpcmr);
+ info->adpcmr = NULL;
+ }
+#endif
+ info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+ info->lsr |= UART_LSR_TEMT;
+ if (info->isdn_driver >= 0) {
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_HANGUP;
+ cmd.arg = info->isdn_channel;
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ isdn_all_eaz(info->isdn_driver, info->isdn_channel);
+ usage = (info->emu.mdmreg[20] == 1)?
+ ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
+ isdn_free_channel(info->isdn_driver, info->isdn_channel,
+ usage);
+ }
+ info->isdn_driver = -1;
+ info->isdn_channel = -1;
+ if (info->drv_index >= 0) {
+ dev->m_idx[info->drv_index] = -1;
+ info->drv_index = -1;
+ }
+}
+
+static inline int isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
+{
+#ifdef MODEM_PARANOIA_CHECK
+ if (!info) {
+ printk(KERN_WARNING "isdn_tty: null info_struct for (%d, %d) in %s\n",
+ MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+ if (info->magic != ISDN_ASYNC_MAGIC) {
+ printk(KERN_WARNING "isdn_tty: bad magic for modem struct (%d, %d) in %s\n",
+ MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void isdn_tty_change_speed(modem_info * info)
+{
+ uint cflag, cval, fcr, quot;
+ int i;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+
+ quot = i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 2)
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ if (quot) {
+ info->mcr |= UART_MCR_DTR;
+ isdn_tty_modem_ncarrier(info);
+ } else {
+ info->mcr &= ~UART_MCR_DTR;
+ if (info->emu.mdmreg[13] & 4) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in changespeed\n");
+#endif
+ if (info->online)
+ info->ncarrier = 1;
+ isdn_tty_modem_reset_regs(&info->emu, 0);
+ isdn_tty_modem_hup(info);
+ }
+ return;
+ }
+ /* byte size and parity */
+ cval = cflag & (CSIZE | CSTOPB);
+ cval >>= 4;
+ if (cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+ fcr = 0;
+
+ /* CTS flow control flag and modem status interrupts */
+ if (cflag & CRTSCTS) {
+ info->flags |= ISDN_ASYNC_CTS_FLOW;
+ } else
+ info->flags &= ~ISDN_ASYNC_CTS_FLOW;
+ if (cflag & CLOCAL)
+ info->flags &= ~ISDN_ASYNC_CHECK_CD;
+ else {
+ info->flags |= ISDN_ASYNC_CHECK_CD;
+ }
+}
+
+static int isdn_tty_startup(modem_info * info)
+{
+ ulong flags;
+
+ if (info->flags & ISDN_ASYNC_INITIALIZED)
+ return 0;
+ save_flags(flags);
+ cli();
+ isdn_MOD_INC_USE_COUNT();
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
+#endif
+ /*
+ * Now, initialize the UART
+ */
+ info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ /*
+ * and set the speed of the serial port
+ */
+ isdn_tty_change_speed(info);
+
+ info->flags |= ISDN_ASYNC_INITIALIZED;
+ info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
+ info->send_outstanding = 0;
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void isdn_tty_shutdown(modem_info * info)
+{
+ ulong flags;
+
+ if (!(info->flags & ISDN_ASYNC_INITIALIZED))
+ return;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
+#endif
+ save_flags(flags);
+ cli(); /* Disable interrupts */
+ isdn_MOD_DEC_USE_COUNT();
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+ info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
+ if (info->emu.mdmreg[13] & 4) {
+ isdn_tty_modem_reset_regs(&info->emu, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+#endif
+ isdn_tty_modem_hup(info);
+ }
+ }
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ISDN_ASYNC_INITIALIZED;
+ restore_flags(flags);
+}
+
+/* isdn_tty_write() is the main send-routine. It is called from the upper
+ * levels within the kernel to perform sending data. Depending on the
+ * online-flag it either directs output to the at-command-interpreter or
+ * to the lower level. Additional tasks done here:
+ * - If online, check for escape-sequence (+++)
+ * - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
+ * - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
+ * - If dialing, abort dial.
+ */
+static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
+{
+ int c, total = 0;
+ ulong flags;
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
+ return 0;
+ if (!tty)
+ return 0;
+ save_flags(flags);
+ cli();
+ while (1) {
+ c = MIN(count, info->xmit_size - info->xmit_count);
+ if (info->isdn_driver >= 0)
+ c = MIN(c, dev->drv[info->isdn_driver]->maxbufsize);
+ if (c <= 0)
+ break;
+ if ((info->online > 1) ||
+ (info->vonline & 2)) {
+ atemu *m = &info->emu;
+
+ if (!(info->vonline & 2))
+ isdn_tty_check_esc(buf, m->mdmreg[2], c,
+ &(m->pluscount),
+ &(m->lastplus),
+ from_user);
+ if (from_user)
+ copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
+ else
+ memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 2) {
+ int cc;
+
+ if (!(cc = isdn_tty_handleDLEdown(info,m,c))) {
+ /* If DLE decoding results in zero-transmit, but
+ * c originally was non-zero, do a wakeup.
+ */
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup) (tty);
+ wake_up_interruptible(&tty->write_wait);
+ info->msr |= UART_MSR_CTS;
+ info->lsr |= UART_LSR_TEMT;
+ }
+ info->xmit_count += cc;
+ } else
+#endif
+ info->xmit_count += c;
+ if (m->mdmreg[13] & 1) {
+ isdn_tty_senddown(info);
+ isdn_tty_tint(info);
+ }
+ } else {
+ info->msr |= UART_MSR_CTS;
+ info->lsr |= UART_LSR_TEMT;
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 1) {
+ if (isdn_tty_end_vrx(buf, c, from_user)) {
+ info->vonline &= ~1;
+ isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
+ }
+ } else
+#endif
+ if (info->dialing) {
+ info->dialing = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+#endif
+ isdn_tty_modem_result(3, info);
+ isdn_tty_modem_hup(info);
+ } else
+ c = isdn_tty_edit_at(buf, c, info, from_user);
+ }
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+ restore_flags(flags);
+ return total;
+}
+
+static int isdn_tty_write_room(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+ int ret;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room"))
+ return 0;
+ if (!info->online)
+ return info->xmit_size;
+ ret = info->xmit_size - info->xmit_count;
+ return (ret < 0) ? 0 : ret;
+}
+
+static int isdn_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer"))
+ return 0;
+ if (!info->online)
+ return 0;
+ return (info->xmit_count);
+}
+
+static void isdn_tty_flush_buffer(struct tty_struct *tty)
+{
+ modem_info *info;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (!tty) {
+ restore_flags(flags);
+ return;
+ }
+ info = (modem_info *) tty->driver_data;
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) {
+ restore_flags(flags);
+ return;
+ }
+ isdn_tty_cleanup_xmit(info);
+ info->xmit_count = 0;
+ restore_flags(flags);
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup) (tty);
+}
+
+static void isdn_tty_flush_chars(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars"))
+ return;
+ if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void isdn_tty_throttle(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_throttle"))
+ return;
+ if (I_IXOFF(tty))
+ info->x_char = STOP_CHAR(tty);
+ info->mcr &= ~UART_MCR_RTS;
+}
+
+static void isdn_tty_unthrottle(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_unthrottle"))
+ return;
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ }
+ info->mcr |= UART_MCR_RTS;
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * isdn_tty_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows RS485 driver to be written in user space.
+ */
+static int isdn_tty_get_lsr_info(modem_info * info, uint * value)
+{
+ u_char status;
+ uint result;
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+ status = info->lsr;
+ restore_flags(flags);
+ result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+ return put_user(result, value);
+}
+
+
+static int isdn_tty_get_modem_info(modem_info * info, uint * value)
+{
+ u_char control, status;
+ uint result;
+ ulong flags;
+
+ control = info->mcr;
+ save_flags(flags);
+ cli();
+ status = info->msr;
+ restore_flags(flags);
+ result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+ | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+ | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
+ | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
+ | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
+ | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+ return put_user(result, value);
+}
+
+static int isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
+{
+ uint arg;
+ int pre_dtr;
+ int error;
+
+ error = get_user(arg, ((uint *) value));
+ if (error)
+ return error;
+
+ switch (cmd) {
+ case TIOCMBIS:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
+#endif
+ if (arg & TIOCM_RTS) {
+ info->mcr |= UART_MCR_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->mcr |= UART_MCR_DTR;
+ isdn_tty_modem_ncarrier(info);
+ }
+ break;
+ case TIOCMBIC:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
+#endif
+ if (arg & TIOCM_RTS) {
+ info->mcr &= ~UART_MCR_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->mcr &= ~UART_MCR_DTR;
+ if (info->emu.mdmreg[13] & 4) {
+ isdn_tty_modem_reset_regs(&info->emu, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
+#endif
+ if (info->online)
+ info->ncarrier = 1;
+ isdn_tty_modem_hup(info);
+ }
+ }
+ break;
+ case TIOCMSET:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
+#endif
+ pre_dtr = (info->mcr & UART_MCR_DTR);
+ info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
+ | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+ | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+ if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
+ if (!(info->mcr & UART_MCR_DTR)) {
+ if (info->emu.mdmreg[13] & 4) {
+ isdn_tty_modem_reset_regs(&info->emu, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+#endif
+ if (info->online)
+ info->ncarrier = 1;
+ isdn_tty_modem_hup(info);
+ }
+ } else
+ isdn_tty_modem_ncarrier(info);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+ uint cmd, ulong arg)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+ int error;
+ int retval;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
+ return -ENODEV;
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
+#endif
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
+#endif
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ return 0;
+ case TIOCGSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
+#endif
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (uint *) arg);
+ case TIOCSSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
+#endif
+ error = get_user(arg ,((uint *) arg));
+ if (error)
+ return error;
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
+#endif
+ return isdn_tty_get_modem_info(info, (uint *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
+ case TIOCSERGETLSR: /* Get line status register */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
+#endif
+ return isdn_tty_get_lsr_info(info, (uint *) arg);
+
+ default:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+#endif
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (!old_termios)
+ isdn_tty_change_speed(info);
+ else {
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+ isdn_tty_change_speed(info);
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ }
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_open() and friends
+ * ------------------------------------------------------------
+ */
+static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+{
+ struct wait_queue wait = {current, NULL};
+ int do_clocal = 0;
+ unsigned long flags;
+ int retval;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ISDN_ASYNC_CLOSING)) {
+ if (info->flags & ISDN_ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef MODEM_DO_RESTART
+ if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == ISDN_SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ISDN_ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ISDN_ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ISDN_ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+ /*
+ * If non-blocking mode is set, then make the check up front
+ * and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+ if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * isdn_tty_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
+ info->line, info->count);
+#endif
+ save_flags(flags);
+ cli();
+ if (!(tty_hung_up_p(filp)))
+ info->count--;
+ restore_flags(flags);
+ info->blocked_open++;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ISDN_ASYNC_INITIALIZED)) {
+#ifdef MODEM_DO_RESTART
+ if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ISDN_ASYNC_CLOSING) &&
+ (do_clocal || (info->msr & UART_MSR_DCD))) {
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
+ info->line, info->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int isdn_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ modem_info *info;
+ int retval, line;
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if (line < 0 || line > ISDN_MAX_CHANNELS)
+ return -ENODEV;
+ info = &dev->mdm.info[line];
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_open"))
+ return -ENODEV;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open %s%d, count = %d\n", tty->driver.name,
+ info->line, info->count);
+#endif
+ info->count++;
+ tty->driver_data = info;
+ info->tty = tty;
+ /*
+ * Start up serial port
+ */
+ retval = isdn_tty_startup(info);
+ if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open return after startup\n");
+#endif
+ return retval;
+ }
+ retval = isdn_tty_block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
+#endif
+ return retval;
+ }
+ if ((info->count == 1) && (info->flags & ISDN_ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == ISDN_SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ isdn_tty_change_speed(info);
+ }
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
+#endif
+ dev->modempoll++;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open normal exit\n");
+#endif
+ return 0;
+}
+
+static void isdn_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+ ulong flags;
+ ulong timeout;
+
+ if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close"))
+ return;
+ save_flags(flags);
+ cli();
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
+#endif
+ return;
+ }
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
+#endif
+ return;
+ }
+ info->flags |= ISDN_ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+
+ tty->closing = 1;
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ if (info->flags & ISDN_ASYNC_INITIALIZED) {
+ tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = jiffies + HZ;
+ while (!(info->lsr & UART_LSR_TEMT)) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 20;
+ schedule();
+ if (jiffies > timeout)
+ break;
+ }
+ }
+ dev->modempoll--;
+ isdn_tty_shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ info->tty = 0;
+ info->ncarrier = 0;
+ tty->closing = 0;
+ if (info->blocked_open) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 50;
+ schedule();
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
+ ISDN_ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_close normal exit\n");
+#endif
+}
+
+/*
+ * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void isdn_tty_hangup(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_hangup"))
+ return;
+ isdn_tty_shutdown(info);
+ info->count = 0;
+ info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/* This routine initializes all emulator-data.
+ */
+static void isdn_tty_reset_profile(atemu * m)
+{
+ m->profile[0] = 0;
+ m->profile[1] = 0;
+ m->profile[2] = 43;
+ m->profile[3] = 13;
+ m->profile[4] = 10;
+ m->profile[5] = 8;
+ m->profile[6] = 3;
+ m->profile[7] = 60;
+ m->profile[8] = 2;
+ m->profile[9] = 6;
+ m->profile[10] = 7;
+ m->profile[11] = 70;
+ m->profile[12] = 0x45;
+ m->profile[13] = 4;
+ m->profile[14] = ISDN_PROTO_L2_X75I;
+ m->profile[15] = ISDN_PROTO_L3_TRANS;
+ m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
+ m->profile[17] = ISDN_MODEM_WINSIZE;
+ m->profile[18] = 4;
+ m->profile[19] = 0;
+ m->profile[20] = 0;
+ m->pmsn[0] = '\0';
+}
+
+static void isdn_tty_modem_reset_vpar(atemu *m)
+{
+ m->vpar[0] = 2; /* Voice-device (2 = phone line) */
+ m->vpar[1] = 0; /* Silence detection level (0 = none ) */
+ m->vpar[2] = 70; /* Silence interval (7 sec. ) */
+ m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */
+}
+
+static void isdn_tty_modem_reset_regs(atemu * m, int force)
+{
+ if ((m->mdmreg[12] & 32) || force) {
+ memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
+ memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+ }
+ isdn_tty_modem_reset_vpar(m);
+ m->mdmcmdl = 0;
+}
+
+static void modem_write_profile(atemu * m)
+{
+ memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
+ memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+ if (dev->profd)
+ send_sig(SIGIO, dev->profd, 1);
+}
+
+int isdn_tty_modem_init(void)
+{
+ modem *m;
+ int i;
+ modem_info *info;
+
+ m = &dev->mdm;
+ memset(&m->tty_modem, 0, sizeof(struct tty_driver));
+ m->tty_modem.magic = TTY_DRIVER_MAGIC;
+ m->tty_modem.name = isdn_ttyname_ttyI;
+ m->tty_modem.major = ISDN_TTY_MAJOR;
+ m->tty_modem.minor_start = 0;
+ m->tty_modem.num = ISDN_MAX_CHANNELS;
+ m->tty_modem.type = TTY_DRIVER_TYPE_SERIAL;
+ m->tty_modem.subtype = ISDN_SERIAL_TYPE_NORMAL;
+ m->tty_modem.init_termios = tty_std_termios;
+ m->tty_modem.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ m->tty_modem.flags = TTY_DRIVER_REAL_RAW;
+ m->tty_modem.refcount = &m->refcount;
+ m->tty_modem.table = m->modem_table;
+ m->tty_modem.termios = m->modem_termios;
+ m->tty_modem.termios_locked = m->modem_termios_locked;
+ m->tty_modem.open = isdn_tty_open;
+ m->tty_modem.close = isdn_tty_close;
+ m->tty_modem.write = isdn_tty_write;
+ m->tty_modem.put_char = NULL;
+ m->tty_modem.flush_chars = isdn_tty_flush_chars;
+ m->tty_modem.write_room = isdn_tty_write_room;
+ m->tty_modem.chars_in_buffer = isdn_tty_chars_in_buffer;
+ m->tty_modem.flush_buffer = isdn_tty_flush_buffer;
+ m->tty_modem.ioctl = isdn_tty_ioctl;
+ m->tty_modem.throttle = isdn_tty_throttle;
+ m->tty_modem.unthrottle = isdn_tty_unthrottle;
+ m->tty_modem.set_termios = isdn_tty_set_termios;
+ m->tty_modem.stop = NULL;
+ m->tty_modem.start = NULL;
+ m->tty_modem.hangup = isdn_tty_hangup;
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ m->cua_modem = m->tty_modem;
+ m->cua_modem.name = isdn_ttyname_cui;
+ m->cua_modem.major = ISDN_TTYAUX_MAJOR;
+ m->tty_modem.minor_start = 0;
+ m->cua_modem.subtype = ISDN_SERIAL_TYPE_CALLOUT;
+
+ if (tty_register_driver(&m->tty_modem)) {
+ printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n");
+ return -1;
+ }
+ if (tty_register_driver(&m->cua_modem)) {
+ printk(KERN_WARNING "isdn_tty: Couldn't register modem-callout-device\n");
+ return -2;
+ }
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ info = &m->info[i];
+ isdn_tty_reset_profile(&info->emu);
+ isdn_tty_modem_reset_regs(&info->emu, 1);
+ info->magic = ISDN_ASYNC_MAGIC;
+ info->line = i;
+ info->tty = 0;
+ info->x_char = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->callout_termios = m->cua_modem.init_termios;
+ info->normal_termios = m->tty_modem.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ info->isdn_driver = -1;
+ info->isdn_channel = -1;
+ info->drv_index = -1;
+ info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
+ skb_queue_head_init(&info->xmit_queue);
+ skb_queue_head_init(&info->dtmf_queue);
+ if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+ printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+ return -3;
+ }
+ /* Make room for T.70 header */
+ info->xmit_buf += 4;
+ }
+ return 0;
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the tty-devices for an appropriate device and bind
+ * it to the ISDN-Channel.
+ * Return Index to dev->mdm or -1 if none found.
+ */
+int isdn_tty_find_icall(int di, int ch, char *num)
+{
+ char *eaz;
+ int i;
+ int idx;
+ int si1;
+ int si2;
+ char *s;
+ char nr[31];
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+ if (num[0] == ',') {
+ nr[0] = '0';
+ strncpy(&nr[1], num, 29);
+ printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
+ } else
+ strncpy(nr, num, 30);
+ s = strtok(nr, ",");
+ s = strtok(NULL, ",");
+ if (!s) {
+ printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n",
+ num);
+ restore_flags(flags);
+ return -1;
+ }
+ si1 = (int)simple_strtoul(s,NULL,10);
+ s = strtok(NULL, ",");
+ if (!s) {
+ printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n",
+ num);
+ restore_flags(flags);
+ return -1;
+ }
+ si2 = (int)simple_strtoul(s,NULL,10);
+ eaz = strtok(NULL, ",");
+ if (!eaz) {
+ printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
+ eaz = "0";
+ }
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
+#endif
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
+ info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
+ info->emu.mdmreg[18], info->emu.mdmreg[19]);
+#endif
+ if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
+ eaz)) && /* EAZ is matching */
+ (info->emu.mdmreg[18] & si2bit[si1]) && /* SI1 is matching */
+ ((info->emu.mdmreg[19] == si2) || !si2)) { /* SI2 is matching or 0 */
+ idx = isdn_dc2minor(di, ch);
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: match1\n");
+ printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
+ info->flags, info->isdn_driver, info->isdn_channel,
+ dev->usage[idx]);
+#endif
+ if ((info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+ (info->isdn_driver == -1) &&
+ (info->isdn_channel == -1) &&
+ (USG_NONE(dev->usage[idx]))) {
+ info->isdn_driver = di;
+ info->isdn_channel = ch;
+ info->drv_index = idx;
+ dev->m_idx[idx] = info->line;
+ dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[idx] |= (si1==1)?ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
+ strcpy(dev->num[idx], nr);
+ info->emu.mdmreg[20] = si2bit[si1];
+ isdn_info_update();
+ restore_flags(flags);
+ printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+ info->line);
+ return info->line;
+ }
+ }
+ }
+ printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+ dev->drv[di]->reject_bus ? "rejected" : "ignored");
+ restore_flags(flags);
+ return -1;
+}
+
+/*********************************************************************
+ Modem-Emulator-Routines
+ *********************************************************************/
+
+#define cmdchar(c) ((c>' ')&&(c<=0x7f))
+
+/*
+ * Put a message from the AT-emulator into receive-buffer of tty,
+ * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
+ */
+static void isdn_tty_at_cout(char *msg, modem_info * info)
+{
+ struct tty_struct *tty;
+ atemu *m = &info->emu;
+ char *p;
+ char c;
+ ulong flags;
+
+ if (!msg) {
+ printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
+ return;
+ }
+ save_flags(flags);
+ cli();
+ tty = info->tty;
+ for (p = msg; *p; p++) {
+ switch (*p) {
+ case '\r':
+ c = m->mdmreg[3];
+ break;
+ case '\n':
+ c = m->mdmreg[4];
+ break;
+ case '\b':
+ c = m->mdmreg[5];
+ break;
+ default:
+ c = *p;
+ }
+ if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+ restore_flags(flags);
+ return;
+ }
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ break;
+ tty_insert_flip_char(tty, c, 0);
+ }
+ restore_flags(flags);
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+/*
+ * Perform ATH Hangup
+ */
+static void isdn_tty_on_hook(modem_info * info)
+{
+ if (info->isdn_channel >= 0) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
+#endif
+ isdn_tty_modem_result(3, info);
+ isdn_tty_modem_hup(info);
+ }
+}
+
+static void isdn_tty_off_hook(void)
+{
+ printk(KERN_DEBUG "isdn_tty_off_hook\n");
+}
+
+#define PLUSWAIT1 (HZ/2) /* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2) /* 1.5 sec */
+
+/*
+ * Check Buffer for Modem-escape-sequence, activate timer-callback to
+ * isdn_tty_modem_escape() if sequence found.
+ *
+ * Parameters:
+ * p pointer to databuffer
+ * plus escape-character
+ * count length of buffer
+ * pluscount count of valid escape-characters so far
+ * lastplus timestamp of last character
+ */
+static void isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+ int *lastplus, int from_user)
+{
+ char cbuf[3];
+
+ if (plus > 127)
+ return;
+ if (count > 3) {
+ p += count - 3;
+ count = 3;
+ *pluscount = 0;
+ }
+ if (from_user) {
+ copy_from_user(cbuf, p, count);
+ p = cbuf;
+ }
+ while (count > 0) {
+ if (*(p++) == plus) {
+ if ((*pluscount)++) {
+ /* Time since last '+' > 0.5 sec. ? */
+ if ((jiffies - *lastplus) > PLUSWAIT1)
+ *pluscount = 1;
+ } else {
+ /* Time since last non-'+' < 1.5 sec. ? */
+ if ((jiffies - *lastplus) < PLUSWAIT2)
+ *pluscount = 0;
+ }
+ if ((*pluscount == 3) && (count = 1))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
+ if (*pluscount > 3)
+ *pluscount = 1;
+ } else
+ *pluscount = 0;
+ *lastplus = jiffies;
+ count--;
+ }
+}
+
+/*
+ * Return result of AT-emulator to tty-receive-buffer, depending on
+ * modem-register 12, bit 0 and 1.
+ * For CONNECT-messages also switch to online-mode.
+ * For RING-message handle auto-ATA if register 0 != 0
+ */
+void isdn_tty_modem_result(int code, modem_info * info)
+{
+ atemu *m = &info->emu;
+ static char *msg[] =
+ {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
+ "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
+ "RINGING", "NO MSN/EAZ", "VCON"};
+ ulong flags;
+ char s[4];
+
+ switch (code) {
+ case 2:
+ m->mdmreg[1]++; /* RING */
+ if (m->mdmreg[1] == m->mdmreg[0])
+ /* Automatically accept incoming call */
+ isdn_tty_cmd_ATA(info);
+ break;
+ case 3:
+ /* NO CARRIER */
+ save_flags(flags);
+ cli();
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
+ (info->flags & ISDN_ASYNC_CLOSING),
+ (!info->tty));
+#endif
+ if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+ restore_flags(flags);
+ return;
+ }
+ restore_flags(flags);
+ if (info->vonline & 1) {
+ /* voice-recording, add DLE-ETX */
+ isdn_tty_at_cout("\020\003", info);
+ }
+ if (info->vonline & 2) {
+ /* voice-playing, add DLE-DC4 */
+ isdn_tty_at_cout("\020\024", info);
+ }
+ break;
+ case 1:
+ case 5:
+ if (!info->online)
+ info->online = 2;
+ break;
+ case 11:
+ if (!info->online)
+ info->online = 1;
+ break;
+ }
+ if (m->mdmreg[12] & 1) {
+ /* Show results */
+ isdn_tty_at_cout("\r\n", info);
+ if (m->mdmreg[12] & 2) {
+ /* Show numeric results */
+ sprintf(s, "%d", code);
+ isdn_tty_at_cout(s, info);
+ } else {
+ if (code == 2) {
+ isdn_tty_at_cout("CALLER NUMBER: ", info);
+ isdn_tty_at_cout(dev->num[info->drv_index], info);
+ isdn_tty_at_cout("\r\n", info);
+ }
+ isdn_tty_at_cout(msg[code], info);
+ if (code == 5) {
+ /* Append Protocol to CONNECT message */
+ isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
+ if (m->mdmreg[13] & 2)
+ isdn_tty_at_cout("/T.70", info);
+ }
+ }
+ isdn_tty_at_cout("\r\n", info);
+ }
+ if (code == 3) {
+ save_flags(flags);
+ cli();
+ if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+ restore_flags(flags);
+ return;
+ }
+ if (info->tty->ldisc.flush_buffer)
+ info->tty->ldisc.flush_buffer(info->tty);
+ if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+ (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
+ tty_hangup(info->tty);
+ }
+ restore_flags(flags);
+ }
+}
+
+/*
+ * Display a modem-register-value.
+ */
+static void isdn_tty_show_profile(int ridx, modem_info * info)
+{
+ char v[6];
+
+ sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
+ isdn_tty_at_cout(v, info);
+}
+
+/*
+ * Get MSN-string from char-pointer, set pointer to end of number
+ */
+static void isdn_tty_get_msnstr(char *n, char **p)
+{
+ while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
+ *n++ = *p[0]++;
+ *n = '\0';
+}
+
+/*
+ * Get phone-number from modem-commandbuffer
+ */
+static void isdn_tty_getdial(char *p, char *q)
+{
+ int first = 1;
+
+ while (strchr("0123456789,#.*WPTS-", *p) && *p) {
+ if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first))
+ *q++ = *p;
+ p++;
+ first = 0;
+ }
+ *q = 0;
+}
+
+#define PARSE_ERROR { isdn_tty_modem_result(4, info); return; }
+#define PARSE_ERROR1 { isdn_tty_modem_result(4, info); return 1; }
+
+/*
+ * Parse AT&.. commands.
+ */
+static int isdn_tty_cmd_ATand(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ int i;
+ char rb[100];
+
+ switch (*p[0]) {
+ case 'B':
+ /* &B - Set Buffersize */
+ p[0]++;
+ i = isdn_getnum(p);
+ if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
+ PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+ if ((m->mdmreg[18] & 1) && (i > VBUF))
+ PARSE_ERROR1;
+#endif
+ m->mdmreg[16] = i / 16;
+ info->xmit_size = m->mdmreg[16] * 16;
+ break;
+ case 'D':
+ /* &D - Set DCD-Low-behavior */
+ p[0]++;
+ switch (isdn_getnum(p)) {
+ case 0:
+ m->mdmreg[13] &= ~4;
+ m->mdmreg[12] &= ~32;
+ break;
+ case 2:
+ m->mdmreg[13] |= 4;
+ m->mdmreg[12] &= ~32;
+ break;
+ case 3:
+ m->mdmreg[13] |= 4;
+ m->mdmreg[12] |= 32;
+ break;
+ default:
+ PARSE_ERROR1
+ }
+ break;
+ case 'E':
+ /* &E -Set EAZ/MSN */
+ p[0]++;
+ isdn_tty_get_msnstr(m->msn, p);
+ break;
+ case 'F':
+ /* &F -Set Factory-Defaults */
+ p[0]++;
+ isdn_tty_reset_profile(m);
+ isdn_tty_modem_reset_regs(m, 1);
+ break;
+ case 'S':
+ /* &S - Set Windowsize */
+ p[0]++;
+ i = isdn_getnum(p);
+ if ((i > 0) && (i < 9))
+ m->mdmreg[17] = i;
+ else
+ PARSE_ERROR1;
+ break;
+ case 'V':
+ /* &V - Show registers */
+ p[0]++;
+ for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
+ sprintf(rb, "S%d=%d%s", i,
+ m->mdmreg[i], (i == 6) ? "\r\n" : " ");
+ isdn_tty_at_cout(rb, info);
+ }
+ sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
+ strlen(m->msn) ? m->msn : "None");
+ isdn_tty_at_cout(rb, info);
+ break;
+ case 'W':
+ /* &W - Write Profile */
+ p[0]++;
+ switch (*p[0]) {
+ case '0':
+ p[0]++;
+ modem_write_profile(m);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 'X':
+ /* &X - Switch to BTX-Mode */
+ p[0]++;
+ switch (isdn_getnum(p)) {
+ case 0:
+ m->mdmreg[13] &= ~2;
+ info->xmit_size = m->mdmreg[16] * 16;
+ break;
+ case 1:
+ m->mdmreg[13] |= 2;
+ m->mdmreg[14] = 0;
+ info->xmit_size = 112;
+ m->mdmreg[18] = 4;
+ m->mdmreg[19] = 0;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+}
+
+/*
+ * Perform ATS command
+ */
+static int isdn_tty_cmd_ATS(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ int mreg;
+ int mval;
+
+ mreg = isdn_getnum(p);
+ if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
+ PARSE_ERROR1;
+ switch (*p[0]) {
+ case '=':
+ p[0]++;
+ mval = isdn_getnum(p);
+ if (mval < 0 || mval > 255)
+ PARSE_ERROR1;
+ switch (mreg) {
+ /* Some plausibility checks */
+ case 14:
+ if (mval > ISDN_PROTO_L2_TRANS)
+ PARSE_ERROR1;
+ break;
+ case 16:
+ if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
+ PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+ if ((m->mdmreg[18] & 1) && (mval > VBUFX))
+ PARSE_ERROR1;
+#endif
+ info->xmit_size = mval * 16;
+ break;
+ case 20:
+ PARSE_ERROR1;
+ }
+ m->mdmreg[mreg] = mval;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_show_profile(mreg, info);
+ break;
+ default:
+ PARSE_ERROR1;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Perform ATA command
+ */
+static void isdn_tty_cmd_ATA(modem_info * info)
+{
+ atemu *m = &info->emu;
+ isdn_ctrl cmd;
+ int l2;
+
+ if (info->msr & UART_MSR_RI) {
+ /* Accept incoming call */
+ m->mdmreg[1] = 0;
+ info->msr &= ~UART_MSR_RI;
+ l2 = m->mdmreg[14];
+#ifdef CONFIG_ISDN_AUDIO
+ /* If more than one bit set in reg18, autoselect Layer2 */
+ if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18])
+ if (m->mdmreg[20] == 1) l2 = 4;
+#endif
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_ACCEPTD;
+ dev->drv[info->isdn_driver]->interface->command(&cmd);
+ } else
+ isdn_tty_modem_result(8, info);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+/*
+ * Parse AT+F.. commands
+ */
+static int isdn_tty_cmd_PLUSF(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ int par;
+ char rs[20];
+
+ if (!strncmp(p[0],"CLASS",5)) {
+ p[0] += 5;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs,"\r\n%d",
+ (m->mdmreg[18]&1)?8:0);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '0':
+ p[0]++;
+ m->mdmreg[18] = 4;
+ info->xmit_size =
+ m->mdmreg[16] * 16;
+ break;
+ case '8':
+ p[0]++;
+ m->mdmreg[18] = 5;
+ info->xmit_size = VBUF;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n0,8",
+ info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ if (!strncmp(p[0],"AA",2)) {
+ p[0] += 2;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs,"\r\n%d",
+ m->mdmreg[0]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 255))
+ PARSE_ERROR1;
+ m->mdmreg[0]=par;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ PARSE_ERROR1;
+}
+
+/*
+ * Parse AT+V.. commands
+ */
+static int isdn_tty_cmd_PLUSV(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ static char *vcmd[] = {"NH","IP","LS","RX","SD","SM","TX",NULL};
+ int i;
+ int par1;
+ int par2;
+ char rs[20];
+
+ i = 0;
+ while (vcmd[i]) {
+ if (!strncmp(vcmd[i],p[0],2)) {
+ p[0] += 2;
+ break;
+ }
+ i++;
+ }
+ switch (i) {
+ case 0:
+ /* AT+VNH - Auto hangup feature */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n1", info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '1':
+ p[0]++;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n1", info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 1:
+ /* AT+VIP - Reset all voice parameters */
+ isdn_tty_modem_reset_vpar(m);
+ break;
+ case 2:
+ /* AT+VLS - Select device, accept incoming call */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs,"\r\n%d",m->vpar[0]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '0':
+ p[0]++;
+ m->vpar[0] = 0;
+ break;
+ case '2':
+ p[0]++;
+ m->vpar[0] = 2;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n0,2", info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 3:
+ /* AT+VRX - Start recording */
+ if (!m->vpar[0])
+ PARSE_ERROR1;
+ if (info->online != 1) {
+ isdn_tty_modem_result(8, info);
+ return 1;
+ }
+ info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+ if (!info->dtmf_state) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+ PARSE_ERROR1;
+ }
+ if (m->vpar[3] < 5) {
+ info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
+ if (!info->adpcmr) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+ PARSE_ERROR1;
+ }
+ }
+ info->vonline = 1;
+ isdn_tty_modem_result(1, info);
+ return 1;
+ break;
+ case 4:
+ /* AT+VSD - Silence detection */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs,"\r\n<%d>,<%d>",
+ m->vpar[1],
+ m->vpar[2]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ par1 = isdn_getnum(p);
+ if ((par1 < 0) || (par1 > 31))
+ PARSE_ERROR1;
+ if (*p[0] != ',')
+ PARSE_ERROR1;
+ p[0]++;
+ par2 = isdn_getnum(p);
+ if ((par2 < 0) || (par2 > 255))
+ PARSE_ERROR1;
+ m->vpar[1] = par1;
+ m->vpar[2] = par2;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+ info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 5:
+ /* AT+VSM - Select compression */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs,"\r\n<%d>,<%d><8000>",
+ m->vpar[3],
+ m->vpar[1]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ par1 = isdn_getnum(p);
+ if ((par1 < 2) || (par1 > 6))
+ PARSE_ERROR1;
+ m->vpar[3] = par1;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
+ info);
+ isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
+ info);
+ isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
+ info);
+ isdn_tty_at_cout("5;ALAW;8;0;(8000)",
+ info);
+ isdn_tty_at_cout("6;ULAW;8;0;(8000)",
+ info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 6:
+ /* AT+VTX - Start sending */
+ if (!m->vpar[0])
+ PARSE_ERROR1;
+ if (info->online != 1) {
+ isdn_tty_modem_result(8, info);
+ return 1;
+ }
+ info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+ if (!info->dtmf_state) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+ PARSE_ERROR1;
+ }
+ if (m->vpar[3] < 5) {
+ info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
+ if (!info->adpcms) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+ PARSE_ERROR1;
+ }
+ }
+ m->lastDLE = 0;
+ info->vonline = 2;
+ isdn_tty_modem_result(1, info);
+ return 1;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+}
+#endif /* CONFIG_ISDN_AUDIO */
+
+/*
+ * Parse and perform an AT-command-line.
+ */
+static void isdn_tty_parse_at(modem_info * info)
+{
+ atemu *m = &info->emu;
+ char *p;
+ char ds[40];
+
+#ifdef ISDN_DEBUG_AT
+ printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+#endif
+ for (p = &m->mdmcmd[2]; *p;) {
+ switch (*p) {
+ case 'A':
+ /* A - Accept incoming call */
+ p++;
+ isdn_tty_cmd_ATA(info);
+ return;
+ break;
+ case 'D':
+ /* D - Dial */
+ isdn_tty_getdial(++p, ds);
+ p += strlen(p);
+ if (!strlen(m->msn))
+ isdn_tty_modem_result(10, info);
+ else if (strlen(ds))
+ isdn_tty_dial(ds, info, m);
+ else
+ isdn_tty_modem_result(4, info);
+ return;
+ case 'E':
+ /* E - Turn Echo on/off */
+ p++;
+ switch (isdn_getnum(&p)) {
+ case 0:
+ m->mdmreg[12] &= ~4;
+ break;
+ case 1:
+ m->mdmreg[12] |= 4;
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ break;
+ case 'H':
+ /* H - On/Off-hook */
+ p++;
+ switch (*p) {
+ case '0':
+ p++;
+ isdn_tty_on_hook(info);
+ break;
+ case '1':
+ p++;
+ isdn_tty_off_hook();
+ break;
+ default:
+ isdn_tty_on_hook(info);
+ break;
+ }
+ break;
+ case 'I':
+ /* I - Information */
+ p++;
+ isdn_tty_at_cout("\r\nLinux ISDN", info);
+ switch (*p) {
+ case '0':
+ case '1':
+ p++;
+ break;
+ default:
+ }
+ break;
+ case 'O':
+ /* O - Go online */
+ p++;
+ if (info->msr & UART_MSR_DCD)
+ /* if B-Channel is up */
+ isdn_tty_modem_result(5, info);
+ else
+ isdn_tty_modem_result(3, info);
+ return;
+ case 'Q':
+ /* Q - Turn Emulator messages on/off */
+ p++;
+ switch (isdn_getnum(&p)) {
+ case 0:
+ m->mdmreg[12] |= 1;
+ break;
+ case 1:
+ m->mdmreg[12] &= ~1;
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ break;
+ case 'S':
+ /* S - Set/Get Register */
+ p++;
+ if (isdn_tty_cmd_ATS(&p, info))
+ return;
+ break;
+ case 'V':
+ /* V - Numeric or ASCII Emulator-messages */
+ p++;
+ switch (isdn_getnum(&p)) {
+ case 0:
+ m->mdmreg[12] |= 2;
+ break;
+ case 1:
+ m->mdmreg[12] &= ~2;
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ break;
+ case 'Z':
+ /* Z - Load Registers from Profile */
+ p++;
+ isdn_tty_modem_reset_regs(m, 1);
+ break;
+#ifdef CONFIG_ISDN_AUDIO
+ case '+':
+ p++;
+ switch (*p) {
+ case 'F':
+ p++;
+ if (isdn_tty_cmd_PLUSF(&p, info))
+ return;
+ break;
+ case 'V':
+ if (!(m->mdmreg[18] & 1))
+ PARSE_ERROR;
+ p++;
+ if (isdn_tty_cmd_PLUSV(&p, info))
+ return;
+ break;
+ }
+ break;
+#endif /* CONFIG_ISDN_AUDIO */
+ case '&':
+ p++;
+ if (isdn_tty_cmd_ATand(&p, info))
+ return;
+ break;
+ default:
+ isdn_tty_modem_result(4, info);
+ return;
+ }
+ }
+ isdn_tty_modem_result(0, info);
+}
+
+/* Need own toupper() because standard-toupper is not available
+ * within modules.
+ */
+#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
+
+/*
+ * Perform line-editing of AT-commands
+ *
+ * Parameters:
+ * p inputbuffer
+ * count length of buffer
+ * channel index to line (minor-device)
+ * user flag: buffer is in userspace
+ */
+static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
+{
+ atemu *m = &info->emu;
+ int total = 0;
+ u_char c;
+ char eb[2];
+ int cnt;
+
+ for (cnt = count; cnt > 0; p++, cnt--) {
+ if (user)
+ get_user(c, p);
+ else
+ c = *p;
+ total++;
+ if (c == m->mdmreg[3] || c == m->mdmreg[4]) {
+ /* Separator (CR oder LF) */
+ m->mdmcmd[m->mdmcmdl] = 0;
+ if (m->mdmreg[12] & 4) {
+ eb[0] = c;
+ eb[1] = 0;
+ isdn_tty_at_cout(eb, info);
+ }
+ if (m->mdmcmdl >= 2)
+ isdn_tty_parse_at(info);
+ m->mdmcmdl = 0;
+ continue;
+ }
+ if (c == m->mdmreg[5] && m->mdmreg[5] < 128) {
+ /* Backspace-Funktion */
+ if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
+ if (m->mdmcmdl)
+ m->mdmcmdl--;
+ if (m->mdmreg[12] & 4)
+ isdn_tty_at_cout("\b", info);
+ }
+ continue;
+ }
+ if (cmdchar(c)) {
+ if (m->mdmreg[12] & 4) {
+ eb[0] = c;
+ eb[1] = 0;
+ isdn_tty_at_cout(eb, info);
+ }
+ if (m->mdmcmdl < 255) {
+ c = my_toupper(c);
+ switch (m->mdmcmdl) {
+ case 0:
+ if (c == 'A')
+ m->mdmcmd[m->mdmcmdl++] = c;
+ break;
+ case 1:
+ if (c == 'T')
+ m->mdmcmd[m->mdmcmdl++] = c;
+ break;
+ default:
+ m->mdmcmd[m->mdmcmdl++] = c;
+ }
+ }
+ }
+ }
+ return total;
+}
+
+/*
+ * Switch all modem-channels who are online and got a valid
+ * escape-sequence 1.5 seconds ago, to command-mode.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void isdn_tty_modem_escape(void)
+{
+ int ton = 0;
+ int i;
+ int midx;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (USG_MODEM(dev->usage[i]))
+ if ((midx = dev->m_idx[i]) >= 0) {
+ modem_info *info = &dev->mdm.info[midx];
+ if (info->online) {
+ ton = 1;
+ if ((info->emu.pluscount == 3) &&
+ ((jiffies - info->emu.lastplus) > PLUSWAIT2)) {
+ info->emu.pluscount = 0;
+ info->online = 0;
+ isdn_tty_modem_result(0, info);
+ }
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
+}
+
+/*
+ * Put a RING-message to all modem-channels who have the RI-bit set.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void isdn_tty_modem_ring(void)
+{
+ int ton = 0;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+ if (info->msr & UART_MSR_RI) {
+ ton = 1;
+ isdn_tty_modem_result(2, info);
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
+}
+
+/*
+ * For all online tty's, try sending data to
+ * the lower levels.
+ */
+void isdn_tty_modem_xmit(void)
+{
+ int ton = 1;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+ if (info->online) {
+ ton = 1;
+ isdn_tty_senddown(info);
+ isdn_tty_tint(info);
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+/*
+ * A packet has been output successfully.
+ * Search the tty-devices for an appropriate device, decrement its
+ * counter for outstanding packets, and set CTS.
+ */
+void isdn_tty_bsent(int drv, int chan)
+{
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+ if ((info->isdn_driver == drv) &&
+ (info->isdn_channel == chan) ) {
+ info->msr |= UART_MSR_CTS;
+ if (info->send_outstanding)
+ if (!(--info->send_outstanding))
+ info->lsr |= UART_LSR_TEMT;
+ isdn_tty_tint(info);
+ }
+ }
+ return;
+}
diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h
new file mode 100644
index 000000000..f317d23ae
--- /dev/null
+++ b/drivers/isdn/isdn_tty.h
@@ -0,0 +1,51 @@
+/* $Id: isdn_tty.h,v 1.5 1996/05/17 03:52:31 fritz Exp $
+ *
+ * header for Linux ISDN subsystem, tty related functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ *
+ * 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.
+ *
+ * $Log: isdn_tty.h,v $
+ * Revision 1.5 1996/05/17 03:52:31 fritz
+ * Changed DLE handling for audio receive.
+ *
+ * Revision 1.4 1996/05/11 21:52:34 fritz
+ * Changed queue management to use sk_buffs.
+ *
+ * Revision 1.3 1996/05/07 09:16:34 fritz
+ * Changed isdn_try_read parameter.
+ *
+ * Revision 1.2 1996/04/30 21:05:27 fritz
+ * Test commit
+ *
+ * Revision 1.1 1996/01/10 21:39:22 fritz
+ * Initial revision
+ *
+ */
+
+extern void isdn_tty_modem_result(int, modem_info *);
+extern void isdn_tty_modem_escape(void);
+extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_modem_xmit(void);
+extern void isdn_tty_modem_hup(modem_info *);
+extern int isdn_tty_modem_init(void);
+extern void isdn_tty_readmodem(void);
+extern int isdn_tty_try_read(modem_info *, struct sk_buff *);
+extern int isdn_tty_find_icall(int, int, char *);
+extern int isdn_tty_countDLE(unsigned char *, int);
+extern void isdn_tty_bsent(int, int);
+extern void isdn_tty_cleanup_xmit(modem_info *);
diff --git a/drivers/isdn/pcbit/Makefile b/drivers/isdn/pcbit/Makefile
new file mode 100644
index 000000000..50a67269e
--- /dev/null
+++ b/drivers/isdn/pcbit/Makefile
@@ -0,0 +1,15 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := module.o edss1.o drv.o layer2.o capi.o callbacks.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_PCBIT),y)
+ O_TARGET += pcbit.o
+else
+ ifeq ($(CONFIG_ISDN_DRV_PCBIT),m)
+ O_TARGET += pcbit.o
+ M_OBJS += pcbit.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/isdn/pcbit/callbacks.c b/drivers/isdn/pcbit/callbacks.c
new file mode 100644
index 000000000..f3051e2e5
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * callbacks for the FSM
+ */
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/tqueue.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+#include "callbacks.h"
+#include "capi.h"
+
+ushort last_ref_num = 1;
+
+/*
+ * send_conn_req
+ *
+ */
+
+void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *cbdata)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Called Party Number: %s\n",
+ cbdata->data.setup.CalledPN);
+#endif
+ /*
+ * hdr - kmalloc in capi_conn_req
+ * - kfree when msg has been sent
+ */
+
+ if ((len = capi_conn_req(cbdata->data.setup.CalledPN, &skb,
+ chan->proto)) < 0)
+ {
+ printk("capi_conn_req failed\n");
+ return;
+ }
+
+
+ refnum = last_ref_num++ & 0x7fffU;
+
+ chan->callref = 0;
+ chan->layer2link = 0;
+ chan->snum = 0;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_CONN_REQ, refnum, skb, len);
+}
+
+/*
+ * rcv CONNECT
+ * will go into ACTIVE state
+ * send CONN_ACTIVE_RESP
+ * send Select protocol request
+ */
+
+void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ isdn_ctrl ictl;
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+ if ((len=capi_conn_active_resp(chan, &skb)) < 0)
+ {
+ printk("capi_conn_active_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_CONN_ACTV_RESP, refnum, skb, len);
+
+
+ ictl.command = ISDN_STAT_DCONN;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+
+ /* ACTIVE D-channel */
+
+ /* Select protocol */
+
+ if ((len=capi_select_proto_req(chan, &skb, 1 /*outgoing*/)) < 0) {
+ printk("capi_select_proto_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+}
+
+
+/*
+ * Disconnect received (actually RELEASE COMPLETE)
+ * This means we were not able to establish connection with remote
+ * Inform the big boss above
+ */
+void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ isdn_ctrl ictl;
+
+ ictl.command = ISDN_STAT_DHUP;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+
+
+/*
+ * Incoming call received
+ * inform user
+ */
+
+void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *cbdata)
+{
+ isdn_ctrl ictl;
+ unsigned short refnum;
+ struct sk_buff *skb;
+ int len;
+
+
+ ictl.command = ISDN_STAT_ICALL;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+
+ /*
+ * ictl.num >= strlen() + strlen() + 5
+ */
+
+ if (cbdata->data.setup.CalledPN)
+ sprintf(ictl.num, "%s,%d,%d,%s",
+ cbdata->data.setup.CallingPN,
+ 7, 0,
+ cbdata->data.setup.CalledPN);
+
+ else
+ sprintf(ictl.num, "%s,%d,%d,%s",
+ cbdata->data.setup.CallingPN,
+ 7, 0,
+ "0");
+
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "statstr: %s\n", ictl.num);
+#endif
+
+ dev->dev_if->statcallb(&ictl);
+
+
+ if ((len=capi_conn_resp(chan, &skb)) < 0) {
+ printk(KERN_DEBUG "capi_conn_resp failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_CONN_RESP, refnum, skb, len);
+}
+
+/*
+ * user has replied
+ * open the channel
+ * send CONNECT message CONNECT_ACTIVE_REQ in CAPI
+ */
+
+void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ unsigned short refnum;
+ struct sk_buff *skb;
+ int len;
+
+ if ((len = capi_conn_active_req(chan, &skb)) < 0) {
+ printk(KERN_DEBUG "capi_conn_active_req failed\n");
+ return;
+ }
+
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ printk(KERN_DEBUG "sending MSG_CONN_ACTV_REQ\n");
+ pcbit_l2_write(dev, MSG_CONN_ACTV_REQ, refnum, skb, len);
+}
+
+/*
+ * CONN_ACK arrived
+ * start b-proto selection
+ *
+ */
+
+void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ unsigned short refnum;
+ struct sk_buff *skb;
+ int len;
+
+ if ((len = capi_select_proto_req(chan, &skb, 0 /*incoming*/)) < 0)
+ {
+ printk("capi_select_proto_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+
+}
+
+
+/*
+ * Received disconnect ind on active state
+ * send disconnect resp
+ * send msg to user
+ */
+void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+ isdn_ctrl ictl;
+
+ if ((len = capi_disc_resp(chan, &skb)) < 0) {
+ printk("capi_disc_resp failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_DISC_RESP, refnum, skb, len);
+
+ ictl.command = ISDN_STAT_BHUP;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+
+
+/*
+ * User HANGUP on active/call proceeding state
+ * send disc.req
+ */
+void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+ if ((len = capi_disc_req(chan->callref, &skb, CAUSE_NORMAL)) < 0)
+ {
+ printk("capi_disc_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb, len);
+}
+
+/*
+ * Disc confirm received send BHUP
+ * Problem: when the HL driver sends the disc req itself
+ * LL receives BHUP
+ */
+void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ isdn_ctrl ictl;
+
+ ictl.command = ISDN_STAT_BHUP;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+
+void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+}
+
+/*
+ * send activate b-chan protocol
+ */
+void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+ if ((len = capi_activate_transp_req(chan, &skb)) < 0)
+ {
+ printk("capi_conn_activate_transp_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_ACT_TRANSP_REQ, refnum, skb, len);
+}
+
+/*
+ * Inform User that the B-channel is available
+ */
+void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ isdn_ctrl ictl;
+
+ ictl.command = ISDN_STAT_BCONN;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+
+
+
diff --git a/drivers/isdn/pcbit/callbacks.h b/drivers/isdn/pcbit/callbacks.h
new file mode 100644
index 000000000..62e296dee
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * Callbacks prototypes for FSM
+ *
+ */
+
+#ifndef CALLBACKS_H
+#define CALLBACKS_H
+
+
+extern void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+#endif
+
+
diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c
new file mode 100644
index 000000000..1c752835d
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * CAPI encoder/decoder for
+ * Portugal Telecom CAPI 2.0
+ *
+ * Not compatible with the AVM Gmbh. CAPI 2.0
+ */
+
+/*
+ * Documentation:
+ * - "Common ISDN API - Perfil Português - Versão 2.1",
+ * Telecom Portugal, Fev 1992.
+ * - "Common ISDN API - Especificação de protocolos para
+ * acesso aos canais B", Inesc, Jan 1994.
+ */
+
+/*
+ * TODO: better decoding of Information Elements
+ * for debug purposes mainly
+ * encode our number in CallerPN and ConnectedPN
+ */
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <linux/tqueue.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/string.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "capi.h"
+
+
+/*
+ * Encoding of CAPI messages
+ *
+ */
+
+int capi_conn_req(const char * calledPN, struct sk_buff **skb, int proto)
+{
+ ushort len;
+
+ /*
+ * length
+ * AppInfoMask - 2
+ * BC0 - 3
+ * BC1 - 1
+ * Chan - 2
+ * Keypad - 1
+ * CPN - 1
+ * CPSA - 1
+ * CalledPN - 2 + strlen
+ * CalledPSA - 1
+ * rest... - 4
+ * ----------------
+ * Total 18 + strlen
+ */
+
+ len = 18 + strlen(calledPN);
+
+ if (proto == ISDN_PROTO_L2_TRANS)
+ len++;
+
+ if ((*skb = dev_alloc_skb(len)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ /* InfoElmMask */
+ *((ushort*) skb_put(*skb, 2)) = AppInfoMask;
+
+ if (proto == ISDN_PROTO_L2_TRANS)
+ {
+ /* Bearer Capability - Mandatory*/
+ *(skb_put(*skb, 1)) = 3; /* BC0.Length */
+ *(skb_put(*skb, 1)) = 0x80; /* Speech */
+ *(skb_put(*skb, 1)) = 0x10; /* Circuit Mode */
+ *(skb_put(*skb, 1)) = 0x23; /* A-law */
+ }
+ else
+ {
+ /* Bearer Capability - Mandatory*/
+ *(skb_put(*skb, 1)) = 2; /* BC0.Length */
+ *(skb_put(*skb, 1)) = 0x88; /* Digital Information */
+ *(skb_put(*skb, 1)) = 0x90; /* BC0.Octect4 */
+ }
+
+ /* Bearer Capability - Optional*/
+ *(skb_put(*skb, 1)) = 0; /* BC1.Length = 0 */
+
+ *(skb_put(*skb, 1)) = 1; /* ChannelID.Length = 1 */
+ *(skb_put(*skb, 1)) = 0x83; /* Basic Interface - Any Channel */
+
+ *(skb_put(*skb, 1)) = 0; /* Keypad.Length = 0 */
+
+
+ *(skb_put(*skb, 1)) = 0; /* CallingPN.Length = 0 */
+ *(skb_put(*skb, 1)) = 0; /* CallingPSA.Length = 0 */
+
+ /* Called Party Number */
+ *(skb_put(*skb, 1)) = strlen(calledPN) + 1;
+ *(skb_put(*skb, 1)) = 0x81;
+ memcpy(skb_put(*skb, strlen(calledPN)), calledPN, strlen(calledPN));
+
+ /* '#' */
+
+ *(skb_put(*skb, 1)) = 0; /* CalledPSA.Length = 0 */
+
+ /* LLC.Length = 0; */
+ /* HLC0.Length = 0; */
+ /* HLC1.Length = 0; */
+ /* UTUS.Length = 0; */
+ memset(skb_put(*skb, 4), 0, 4);
+
+ return len;
+}
+
+int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+
+ if ((*skb = dev_alloc_skb(5)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+ *(skb_put(*skb, 1)) = 0x01; /* ACCEPT_CALL */
+ *(skb_put(*skb, 1)) = 0;
+ *(skb_put(*skb, 1)) = 0;
+
+ return 5;
+}
+
+int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+ /*
+ * 8 bytes
+ */
+
+ if ((*skb = dev_alloc_skb(8)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_active_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref);
+#endif
+
+ *(skb_put(*skb, 1)) = 0; /* BC.Length = 0; */
+ *(skb_put(*skb, 1)) = 0; /* ConnectedPN.Length = 0 */
+ *(skb_put(*skb, 1)) = 0; /* PSA.Length */
+ *(skb_put(*skb, 1)) = 0; /* LLC.Length = 0; */
+ *(skb_put(*skb, 1)) = 0; /* HLC.Length = 0; */
+ *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */
+
+ return 8;
+}
+
+int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+ /*
+ * 2 bytes
+ */
+
+ if ((*skb = dev_alloc_skb(2)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_active_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+ return 2;
+}
+
+
+int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
+ int outgoing)
+{
+
+ /*
+ * 18 bytes
+ */
+
+ if ((*skb = dev_alloc_skb(18)) == NULL) {
+
+ printk(KERN_WARNING "capi_select_proto_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+ /* Layer2 protocol */
+
+ switch (chan->proto) {
+ case ISDN_PROTO_L2_X75I:
+ *(skb_put(*skb, 1)) = 0x05; /* LAPB */
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ *(skb_put(*skb, 1)) = 0x02;
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ /*
+ * Voice (a-law)
+ */
+ *(skb_put(*skb, 1)) = 0x06;
+ break;
+ default:
+#ifdef DEBUG
+ printk(KERN_DEBUG "Transparent\n");
+#endif
+ *(skb_put(*skb, 1)) = 0x03;
+ break;
+ }
+
+ *(skb_put(*skb, 1)) = (outgoing ? 0x02 : 0x42); /* Don't ask */
+ *(skb_put(*skb, 1)) = 0x00;
+
+ *((ushort *) skb_put(*skb, 2)) = MRU;
+
+
+ *(skb_put(*skb, 1)) = 0x08; /* Modulo */
+ *(skb_put(*skb, 1)) = 0x07; /* Max Window */
+
+ *(skb_put(*skb, 1)) = 0x01; /* No Layer3 Protocol */
+
+ /*
+ * 2 - layer3 MTU [10]
+ * - Modulo [12]
+ * - Window
+ * - layer1 proto [14]
+ * - bitrate
+ * - sub-channel [16]
+ * - layer1dataformat [17]
+ */
+
+ memset(skb_put(*skb, 8), 0, 8);
+
+ return 18;
+}
+
+
+int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+
+ if ((*skb = dev_alloc_skb(7)) == NULL) {
+
+ printk(KERN_WARNING "capi_activate_transp_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+
+ *(skb_put(*skb, 1)) = chan->layer2link; /* Layer2 id */
+ *(skb_put(*skb, 1)) = 0x00; /* Transmit by default */
+
+ *((ushort *) skb_put(*skb, 2)) = MRU;
+
+ *(skb_put(*skb, 1)) = 0x01; /* Enables reception*/
+
+ return 7;
+}
+
+int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb)
+{
+ ushort data_len;
+
+
+ /*
+ * callref - 2
+ * layer2link - 1
+ * wBlockLength - 2
+ * data - 4
+ * sernum - 1
+ */
+
+ data_len = skb->len;
+
+ skb_push(skb, 10);
+
+ *((u16 *) (skb->data)) = chan->callref;
+ skb->data[2] = chan->layer2link;
+ *((u16 *) (skb->data + 3)) = data_len;
+
+ chan->s_refnum = (chan->s_refnum + 1) % 8;
+ *((u32 *) (skb->data + 5)) = chan->s_refnum;
+
+ skb->data[9] = 0; /* HDLC frame number */
+
+ return 10;
+}
+
+int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb)
+
+{
+ if ((*skb = dev_alloc_skb(4)) == NULL) {
+
+ printk(KERN_WARNING "capi_tdata_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+ *(skb_put(*skb, 1)) = chan->layer2link;
+ *(skb_put(*skb, 1)) = chan->r_refnum;
+
+ return (*skb)->len;
+}
+
+int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause)
+{
+
+ if ((*skb = dev_alloc_skb(6)) == NULL) {
+
+ printk(KERN_WARNING "capi_disc_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+ *((ushort*) skb_put(*skb, 2) ) = callref;
+
+ *(skb_put(*skb, 1)) = 2; /* Cause.Length = 2; */
+ *(skb_put(*skb, 1)) = 0x80;
+ *(skb_put(*skb, 1)) = 0x80 | cause;
+
+ /*
+ * Change it: we should send 'Sic transit gloria Mundi' here ;-)
+ */
+
+ *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */
+
+ return 6;
+}
+
+int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+ if ((*skb = dev_alloc_skb(2)) == NULL) {
+
+ printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ (*skb)->free = 1;
+
+ *((ushort*) skb_put(*skb, 2)) = chan->callref;
+
+ return 2;
+}
+
+
+/*
+ * Decoding of CAPI messages
+ *
+ */
+
+int capi_decode_conn_ind(struct pcbit_chan * chan,
+ struct sk_buff *skb,
+ struct callb_data *info)
+{
+ int CIlen, len;
+
+ /* Call Reference [CAPI] */
+ chan->callref = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref);
+#endif
+
+ /* Channel Identification */
+
+ /* Expect
+ Len = 1
+ Octect 3 = 0100 10CC - [ 7 Basic, 4 , 2-1 chan ]
+ */
+
+ CIlen = skb->data[0];
+#ifdef DEBUG
+ if (CIlen == 1) {
+
+ if ( ((skb->data[1]) & 0xFC) == 0x48 )
+ printk(KERN_DEBUG "decode_conn_ind: chan ok\n");
+ printk(KERN_DEBUG "phyChan = %d\n", skb->data[1] & 0x03);
+ }
+ else
+ printk(KERN_DEBUG "conn_ind: CIlen = %d\n", CIlen);
+#endif
+ skb_pull(skb, CIlen + 1);
+
+ /* Calling Party Number */
+ /* An "additional service" as far as Portugal Telecom is concerned */
+
+ len = skb->data[0];
+
+ if (len > 0) {
+ int count = 1;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "CPN: Octect 3 %02x\n", skb->data[1]);
+#endif
+ if ((skb->data[1] & 0x80) == 0)
+ count = 2;
+
+ if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+ return -1;
+
+ memcpy(info->data.setup.CallingPN, skb->data + count + 1,
+ len - count);
+ info->data.setup.CallingPN[len - count] = 0;
+
+ }
+ else {
+ info->data.setup.CallingPN = NULL;
+ printk(KERN_DEBUG "NULL CallingPN\n");
+ }
+
+ skb_pull(skb, len + 1);
+
+ /* Calling Party Subaddress */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* Called Party Number */
+
+ len = skb->data[0];
+
+ if (len > 0) {
+ int count = 1;
+
+ if ((skb->data[1] & 0x80) == 0)
+ count = 2;
+
+ if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+ return -1;
+
+ memcpy(info->data.setup.CalledPN, skb->data + count + 1,
+ len - count);
+ info->data.setup.CalledPN[len - count] = 0;
+
+ }
+ else {
+ info->data.setup.CalledPN = NULL;
+ printk(KERN_DEBUG "NULL CalledPN\n");
+ }
+
+ skb_pull(skb, len + 1);
+
+ /* Called Party Subaddress */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* LLC */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* HLC */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* U2U */
+ skb_pull(skb, skb->data[0] + 1);
+
+ return 0;
+}
+
+/*
+ * returns errcode
+ */
+
+int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+ int *complete)
+{
+ int errcode;
+
+ chan->callref = *((ushort *) skb->data); /* Update CallReference */
+ skb_pull(skb, 2);
+
+ errcode = *((ushort *) skb->data); /* read errcode */
+ skb_pull(skb, 2);
+
+ *complete = *(skb->data);
+ skb_pull(skb, 1);
+
+ /* FIX ME */
+ /* This is actually a firmware bug */
+ if (!*complete)
+ {
+ printk(KERN_DEBUG "complete=%02x\n", *complete);
+ *complete = 1;
+ }
+
+
+ /* Optional Bearer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* Channel Identification */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* High Layer Compatibility follows */
+ skb_pull(skb, *(skb->data) + 1);
+
+ return errcode;
+}
+
+int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+ ushort len;
+#ifdef DEBUG
+ char str[32];
+#endif
+
+ /* Yet Another Bearer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+
+ /* Connected Party Number */
+ len=*(skb->data);
+
+#ifdef DEBUG
+ if (len > 1 && len < 31) {
+ memcpy(str, skb->data + 2, len - 1);
+ str[len] = 0;
+ printk(KERN_DEBUG "Connected Party Number: %s\n", str);
+ }
+ else
+ printk(KERN_DEBUG "actv_ind CPN len = %d\n", len);
+#endif
+
+ skb_pull(skb, len + 1);
+
+ /* Connected Subaddress */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* Low Layer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* High Layer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+ return 0;
+}
+
+int capi_decode_conn_actv_conf(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+ ushort errcode;
+
+ errcode = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ /* Channel Identification
+ skb_pull(skb, skb->data[0] + 1);
+ */
+ return errcode;
+}
+
+
+int capi_decode_sel_proto_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+ ushort errcode;
+
+ chan->layer2link = *(skb->data);
+ skb_pull(skb, 1);
+
+ errcode = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ return errcode;
+}
+
+int capi_decode_actv_trans_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+ ushort errcode;
+
+ if (chan->layer2link != *(skb->data) )
+ printk("capi_decode_actv_trans_conf: layer2link doesn't match\n");
+
+ skb_pull(skb, 1);
+
+ errcode = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ return errcode;
+}
+
+int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+ ushort len;
+#ifdef DEBUG
+ int i;
+#endif
+ /* Cause */
+
+ len = *(skb->data);
+ skb_pull(skb, 1);
+
+#ifdef DEBUG
+
+ for (i=0; i<len; i++)
+ printk(KERN_DEBUG "Cause Octect %d: %02x\n", i+3,
+ *(skb->data + i));
+#endif
+
+ skb_pull(skb, len);
+
+ return 0;
+}
+
+int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+ ushort errcode;
+
+ errcode = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ return errcode;
+}
+
+#ifdef DEBUG
+int capi_decode_debug_188(u_char *hdr, ushort hdrlen)
+{
+ char str[64];
+ int len;
+
+ len = hdr[0];
+
+ if (len < 64 && len == hdrlen - 1) {
+ memcpy(str, hdr + 1, hdrlen - 1);
+ str[hdrlen - 1] = 0;
+ printk("%s\n", str);
+ }
+ else
+ printk("debug message incorrect\n");
+
+ return 0;
+}
+#endif
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/capi.h b/drivers/isdn/pcbit/capi.h
new file mode 100644
index 000000000..10bb6bf91
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * CAPI encode/decode prototypes and defines
+ */
+
+#ifndef CAPI_H
+#define CAPI_H
+
+
+#define REQ_CAUSE 0x01
+#define REQ_DISPLAY 0x04
+#define REQ_USER_TO_USER 0x08
+
+#define AppInfoMask REQ_CAUSE|REQ_DISPLAY|REQ_USER_TO_USER
+
+/* Connection Setup */
+extern int capi_conn_req(const char * calledPN, struct sk_buff **buf,
+ int proto);
+extern int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+ int *complete);
+
+extern int capi_decode_conn_ind(struct pcbit_chan * chan, struct sk_buff *skb,
+ struct callb_data *info);
+extern int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb);
+
+extern int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb);
+extern int capi_decode_conn_actv_conf(struct pcbit_chan * chan,
+ struct sk_buff *skb);
+
+extern int capi_decode_conn_actv_ind(struct pcbit_chan * chan,
+ struct sk_buff *skb);
+extern int capi_conn_active_resp(struct pcbit_chan* chan,
+ struct sk_buff **skb);
+
+/* Data */
+extern int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
+ int outgoing);
+extern int capi_decode_sel_proto_conf(struct pcbit_chan *chan,
+ struct sk_buff *skb);
+
+extern int capi_activate_transp_req(struct pcbit_chan *chan,
+ struct sk_buff **skb);
+extern int capi_decode_actv_trans_conf(struct pcbit_chan *chan,
+ struct sk_buff *skb);
+
+extern int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb);
+extern int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb);
+
+/* Connection Termination */
+extern int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause);
+extern int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb);
+
+extern int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb);
+extern int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb);
+
+#ifdef DEBUG
+extern int capi_decode_debug_188(u_char *hdr, ushort hdrlen);
+#endif
+
+extern __inline__
+struct pcbit_chan *
+capi_channel(struct pcbit_dev *dev, struct sk_buff *skb)
+{
+ ushort callref;
+
+ callref = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ if (dev->b1->callref == callref)
+ return dev->b1;
+ else if (dev->b2->callref == callref)
+ return dev->b2;
+
+ return NULL;
+}
+
+#endif
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c
new file mode 100644
index 000000000..7347fa888
--- /dev/null
+++ b/drivers/isdn/pcbit/drv.c
@@ -0,0 +1,1154 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * PCBIT-D interface with isdn4linux
+ */
+
+/*
+ * Fixes:
+ *
+ * Nuno Grilo <l38486@alfa.ist.utl.pt>
+ * fixed msn_list NULL pointer dereference.
+ *
+ */
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include <asm/string.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "capi.h"
+
+
+extern ushort last_ref_num;
+
+static int pcbit_ioctl(isdn_ctrl* ctl);
+
+static char* pcbit_devname[MAX_PCBIT_CARDS] = {
+ "pcbit0",
+ "pcbit1",
+ "pcbit2",
+ "pcbit3"
+};
+
+/*
+ * prototypes
+ */
+
+int pcbit_command(isdn_ctrl* ctl);
+int pcbit_stat(u_char* buf, int len, int user, int, int);
+int pcbit_xmit(int driver, int chan, struct sk_buff *skb);
+int pcbit_writecmd(const u_char*, int, int, int, int);
+
+static int set_protocol_running(struct pcbit_dev * dev);
+
+static void pcbit_clear_msn(struct pcbit_dev *dev);
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list);
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn);
+
+
+extern void pcbit_deliver(void * data);
+
+int pcbit_init_dev(int board, int mem_base, int irq)
+{
+ struct pcbit_dev *dev;
+ isdn_if *dev_if;
+
+ if ((dev=kmalloc(sizeof(struct pcbit_dev), GFP_KERNEL)) == NULL)
+ {
+ printk("pcbit_init: couldn't malloc pcbit_dev struct\n");
+ return -ENOMEM;
+ }
+
+ dev_pcbit[board] = dev;
+ memset(dev, 0, sizeof(struct pcbit_dev));
+
+ if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF )
+ dev->sh_mem = (unsigned char*) mem_base;
+ else
+ {
+ printk("memory address invalid");
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EACCES;
+ }
+
+ dev->b1 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+ if (!dev->b1) {
+ printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ dev->b2 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+ if (!dev->b2) {
+ printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+ kfree(dev->b1);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ memset(dev->b1, 0, sizeof(struct pcbit_chan));
+ memset(dev->b2, 0, sizeof(struct pcbit_chan));
+ dev->b2->id = 1;
+
+
+ dev->qdelivery.next = NULL;
+ dev->qdelivery.sync = 0;
+ dev->qdelivery.routine = pcbit_deliver;
+ dev->qdelivery.data = dev;
+
+ /*
+ * interrupts
+ */
+
+ if (request_irq(irq, &pcbit_irq_handler, 0, pcbit_devname[board], dev) != 0)
+ {
+ kfree(dev->b1);
+ kfree(dev->b2);
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EIO;
+ }
+
+ dev->irq = irq;
+
+ /* next frame to be received */
+ dev->rcv_seq = 0;
+ dev->send_seq = 0;
+ dev->unack_seq = 0;
+
+ dev->hl_hdrlen = 10;
+
+ dev_if = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+
+ if (!dev_if) {
+ free_irq(irq, dev);
+ kfree(dev->b1);
+ kfree(dev->b2);
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EIO;
+ }
+
+ dev->dev_if = dev_if;
+
+ dev_if->channels = 2;
+
+
+ dev_if->features = (ISDN_FEATURE_P_EURO | ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS );
+
+ dev_if->writebuf_skb = pcbit_xmit;
+ dev_if->writebuf = NULL;
+ dev_if->hl_hdrlen = 10;
+
+ dev_if->maxbufsize = MAXBUFSIZE;
+ dev_if->command = pcbit_command;
+
+ dev_if->writecmd = pcbit_writecmd;
+ dev_if->readstat = pcbit_stat;
+
+
+ strcpy(dev_if->id, pcbit_devname[board]);
+
+ if (!register_isdn(dev_if)) {
+ free_irq(irq, dev);
+ kfree(dev->b1);
+ kfree(dev->b2);
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EIO;
+ }
+
+ dev->id = dev_if->channels;
+
+
+ dev->l2_state = L2_DOWN;
+ dev->free = 511;
+
+ /*
+ * set_protocol_running(dev);
+ */
+
+ return 0;
+}
+
+#ifdef MODULE
+void pcbit_terminate(int board)
+{
+ struct pcbit_dev * dev;
+
+ dev = dev_pcbit[board];
+
+ if (dev) {
+ /* unregister_isdn(dev->dev_if); */
+ free_irq(dev->irq, dev);
+ pcbit_clear_msn(dev);
+ kfree(dev->dev_if);
+ if (dev->b1->fsm_timer.function)
+ del_timer(&dev->b1->fsm_timer);
+ if (dev->b2->fsm_timer.function)
+ del_timer(&dev->b2->fsm_timer);
+ kfree(dev->b1);
+ kfree(dev->b2);
+ kfree(dev);
+ }
+}
+#endif
+
+int pcbit_command(isdn_ctrl* ctl)
+{
+ struct pcbit_dev *dev;
+ struct pcbit_chan *chan;
+ struct callb_data info;
+ char *cp;
+
+ dev = finddev(ctl->driver);
+
+ if (!dev)
+ {
+ printk("pcbit_command: unknown device\n");
+ return -1;
+ }
+
+ chan = (ctl->arg & 0x0F) ? dev->b2 : dev->b1;
+
+
+ switch(ctl->command) {
+ case ISDN_CMD_IOCTL:
+ return pcbit_ioctl(ctl);
+ break;
+ case ISDN_CMD_DIAL:
+ info.type = EV_USR_SETUP_REQ;
+ info.data.setup.CalledPN = (char *) &ctl->num;
+ cp = strchr(info.data.setup.CalledPN, ',');
+ if (cp)
+ *cp = 0;
+ else {
+ printk(KERN_DEBUG "DIAL: error in CalledPN\n");
+ return -1;
+ }
+ pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info);
+ break;
+ case ISDN_CMD_ACCEPTD:
+ pcbit_fsm_event(dev, chan, EV_USR_SETUP_RESP, NULL);
+ break;
+ case ISDN_CMD_ACCEPTB:
+ printk("ISDN_CMD_ACCEPTB - not really needed\n");
+ break;
+ case ISDN_CMD_HANGUP:
+ pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+ break;
+ case ISDN_CMD_SETL2:
+ chan->proto = (ctl->arg >> 8);
+ break;
+ case ISDN_CMD_GETL2:
+ return chan->proto;
+ break;
+ case ISDN_CMD_LOCK:
+ MOD_INC_USE_COUNT;
+ break;
+ case ISDN_CMD_UNLOCK:
+ MOD_DEC_USE_COUNT;
+ break;
+ case ISDN_CMD_CLREAZ:
+ pcbit_clear_msn(dev);
+ break;
+ case ISDN_CMD_SETEAZ:
+ pcbit_set_msn(dev, ctl->num);
+ break;
+ case ISDN_CMD_SETL3:
+ if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS)
+ printk(KERN_DEBUG "L3 protocol unknown\n");
+ break;
+ case ISDN_CMD_GETL3:
+ return ISDN_PROTO_L3_TRANS;
+ break;
+ case ISDN_CMD_GETEAZ:
+ case ISDN_CMD_SETSIL:
+ case ISDN_CMD_GETSIL:
+ printk(KERN_DEBUG "pcbit_command: code %d not implemented yet\n", ctl->command);
+ break;
+ default:
+ printk(KERN_DEBUG "pcbit_command: unknown command\n");
+ break;
+ };
+
+ return 0;
+}
+
+/*
+ * Another Hack :-(
+ * on some conditions the board stops sending TDATA_CONFs
+ * let's see if we can turn around the problem
+ */
+
+#ifdef BLOCK_TIMER
+static void pcbit_block_timer(unsigned long data)
+{
+ struct pcbit_chan *chan;
+ struct pcbit_dev * dev;
+ isdn_ctrl ictl;
+
+ chan = (struct pcbit_chan *) data;
+
+ dev = chan2dev(chan);
+
+ if (dev == NULL) {
+ printk(KERN_DEBUG "pcbit: chan2dev failed\n");
+ return;
+ }
+
+ del_timer(&chan->block_timer);
+ chan->block_timer.function = NULL;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "pcbit_block_timer\n");
+#endif
+ chan->queued = 0;
+ ictl.driver = dev->id;
+ ictl.command = ISDN_STAT_BSENT;
+ ictl.arg = chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+#endif
+
+int pcbit_xmit(int driver, int chnum, struct sk_buff *skb)
+{
+ ushort hdrlen;
+ int refnum, len;
+ struct pcbit_chan * chan;
+ struct pcbit_dev *dev;
+
+ dev = finddev(driver);
+ if (dev == NULL)
+ {
+ printk("finddev returned NULL");
+ return -1;
+ }
+
+ chan = chnum ? dev->b2 : dev->b1;
+
+
+ if (chan->fsm_state != ST_ACTIVE)
+ return -1;
+
+ if (chan->queued >= MAX_QUEUED )
+ {
+#ifdef DEBUG_QUEUE
+ printk(KERN_DEBUG
+ "pcbit: %d packets already in queue - write fails\n",
+ chan->queued);
+#endif
+ /*
+ * packet stays on the head of the device queue
+ * since dev_start_xmit will fail
+ * see net/core/dev.c
+ */
+#ifdef BLOCK_TIMER
+ if (chan->block_timer.function == NULL) {
+ init_timer(&chan->block_timer);
+ chan->block_timer.function = &pcbit_block_timer;
+ chan->block_timer.data = (long) chan;
+ chan->block_timer.expires = jiffies + 1 * HZ;
+ add_timer(&chan->block_timer);
+ }
+#endif
+ return 0;
+ }
+
+
+ chan->queued++;
+
+ len = skb->len;
+
+ hdrlen = capi_tdata_req(chan, skb);
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_TDATA_REQ, refnum, skb, hdrlen);
+
+ return len;
+}
+
+
+int pcbit_writecmd(const u_char* buf, int len, int user, int driver, int channel)
+{
+ struct pcbit_dev * dev;
+ int i, j;
+ const u_char * loadbuf;
+ u_char * ptr = NULL;
+
+ int errstat;
+
+ dev = finddev(driver);
+
+ if (!dev)
+ {
+ printk("pcbit_writecmd: couldn't find device");
+ return -ENODEV;
+ }
+
+ switch(dev->l2_state) {
+ case L2_LWMODE:
+ /* check (size <= rdp_size); write buf into board */
+ if (len > BANK4 + 1)
+ {
+ printk("pcbit_writecmd: invalid length %d\n", len);
+ return -EFAULT;
+ }
+
+ if (user)
+ {
+ u_char cbuf[1024];
+
+ if (copy_from_user(cbuf, buf, len))
+ return -EFAULT;
+ for (i=0; i<len; i++)
+ writeb(cbuf[i], dev->sh_mem + i);
+ }
+ else
+ memcpy_toio(dev->sh_mem, buf, len);
+ return len;
+ break;
+ case L2_FWMODE:
+ /* this is the hard part */
+ /* dumb board */
+ if (len < 0)
+ return -EINVAL;
+
+ if (user) {
+ /* get it into kernel space */
+ if ((ptr = kmalloc(len, GFP_KERNEL))==NULL)
+ return -ENOMEM;
+ if (copy_from_user(ptr, buf, len))
+ {
+ kfree(ptr);
+ return -EFAULT;
+ }
+ loadbuf = ptr;
+ }
+ else
+ loadbuf = buf;
+
+ errstat = 0;
+
+ for (i=0; i < len; i++)
+ {
+ for(j=0; j < LOAD_RETRY; j++)
+ {
+ __volatile__ unsigned char * ptr;
+
+ ptr = dev->sh_mem + dev->loadptr;
+ if (*ptr == 0)
+ break;
+
+ }
+
+ if (j == LOAD_RETRY)
+ {
+ errstat = -ETIME;
+ printk("TIMEOUT i=%d\n", i);
+ break;
+ }
+ writeb(loadbuf[i], dev->sh_mem + dev->loadptr + 1);
+ writeb(0x01, dev->sh_mem + dev->loadptr);
+
+ dev->loadptr += 2;
+ if (dev->loadptr > LOAD_ZONE_END)
+ dev->loadptr = LOAD_ZONE_START;
+ }
+
+ if (user)
+ kfree(ptr);
+
+ return errstat ? errstat : len;
+
+ break;
+ default:
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/*
+ * demultiplexing of messages
+ *
+ */
+
+void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg,
+ struct sk_buff * skb,
+ ushort hdr_len, ushort refnum)
+{
+ struct pcbit_chan *chan;
+ struct sk_buff *skb2;
+ unsigned short len;
+ struct callb_data cbdata;
+ int complete, err;
+ isdn_ctrl ictl;
+#ifdef DEBUG
+ struct msg_fmt * fmsg;
+#endif
+
+ switch(msg) {
+
+ case MSG_TDATA_IND:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+ chan->r_refnum = skb->data[7];
+ skb_pull(skb, 8);
+
+ dev->dev_if->rcvcallb_skb(dev->id, chan->id, skb);
+
+ if (capi_tdata_resp(chan, &skb2) > 0)
+ pcbit_l2_write(dev, MSG_TDATA_RESP, refnum,
+ skb2, skb2->len);
+ return;
+ break;
+ case MSG_TDATA_CONF:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+#ifdef DEBUG
+ if ( (*((ushort *) (skb->data + 2) )) != 0) {
+ printk(KERN_DEBUG "TDATA_CONF error\n");
+ }
+#endif
+#ifdef BLOCK_TIMER
+ if (chan->queued == MAX_QUEUED) {
+ del_timer(&chan->block_timer);
+ chan->block_timer.function = NULL;
+ }
+
+#endif
+ chan->queued--;
+
+ ictl.driver = dev->id;
+ ictl.command = ISDN_STAT_BSENT;
+ ictl.arg = chan->id;
+ dev->dev_if->statcallb(&ictl);
+ break;
+
+ case MSG_CONN_IND:
+ /*
+ * channel: 1st not used will do
+ * if both are used we're in trouble
+ */
+
+ if (!dev->b1->fsm_state)
+ chan = dev->b1;
+ else if (!dev->b2->fsm_state)
+ chan = dev->b2;
+ else {
+ printk(KERN_INFO
+ "Incoming connection: no channels available");
+
+ if ((len = capi_disc_req(*(ushort*)(skb->data), &skb2, CAUSE_NOCHAN)) > 0)
+ pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb2, len);
+ break;
+ }
+
+ cbdata.data.setup.CalledPN = NULL;
+ cbdata.data.setup.CallingPN = NULL;
+
+ capi_decode_conn_ind(chan, skb, &cbdata);
+ cbdata.type = EV_NET_SETUP;
+
+ pcbit_fsm_event(dev, chan, EV_NET_SETUP, NULL);
+
+ if (pcbit_check_msn(dev, cbdata.data.setup.CallingPN))
+ pcbit_fsm_event(dev, chan, EV_USR_PROCED_REQ, &cbdata);
+ else
+ pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+
+ if (cbdata.data.setup.CalledPN)
+ kfree(cbdata.data.setup.CalledPN);
+ if (cbdata.data.setup.CallingPN)
+ kfree(cbdata.data.setup.CallingPN);
+ break;
+
+ case MSG_CONN_CONF:
+ /*
+ * We should be able to find the channel by the message
+ * reference number. The current version of the firmware
+ * doesn't sent the ref number correctly.
+ */
+#ifdef DEBUG
+ printk(KERN_DEBUG "refnum=%04x b1=%04x b2=%04x\n", refnum,
+ dev->b1->s_refnum,
+ dev->b2->s_refnum);
+#endif
+#if 0
+ if (dev->b1->s_refnum == refnum)
+ chan = dev->b1;
+ else {
+
+ if (dev->b2->s_refnum == refnum)
+ chan = dev->b2;
+ else {
+ chan = NULL;
+ printk(KERN_WARNING "Connection Confirm - refnum doesn't match chan\n");
+ break;
+ }
+ }
+#else
+ /* We just try to find a channel in the right state */
+
+ if (dev->b1->fsm_state == ST_CALL_INIT)
+ chan = dev->b1;
+ else {
+ if (dev->b2->s_refnum == ST_CALL_INIT)
+ chan = dev->b2;
+ else {
+ chan = NULL;
+ printk(KERN_WARNING "Connection Confirm - no channel in Call Init state\n");
+ break;
+ }
+ }
+#endif
+ if (capi_decode_conn_conf(chan, skb, &complete)) {
+ printk(KERN_DEBUG "conn_conf indicates error\n");
+ pcbit_fsm_event(dev, chan, EV_ERROR, NULL);
+ }
+ else
+ if (complete)
+ pcbit_fsm_event(dev, chan, EV_NET_CALL_PROC, NULL);
+ else
+ pcbit_fsm_event(dev, chan, EV_NET_SETUP_ACK, NULL);
+ break;
+ case MSG_CONN_ACTV_IND:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (capi_decode_conn_actv_ind(chan, skb)) {
+ printk("error in capi_decode_conn_actv_ind\n");
+ /* pcbit_fsm_event(dev, chan, EV_ERROR, NULL); */
+ break;
+ }
+ chan->r_refnum = refnum;
+ pcbit_fsm_event(dev, chan, EV_NET_CONN, NULL);
+ break;
+ case MSG_CONN_ACTV_CONF:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (capi_decode_conn_actv_conf(chan, skb) == 0)
+ pcbit_fsm_event(dev, chan, EV_NET_CONN_ACK, NULL);
+
+ else
+ printk(KERN_DEBUG "decode_conn_actv_conf failed\n");
+ break;
+
+ case MSG_SELP_CONF:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!(err = capi_decode_sel_proto_conf(chan, skb)))
+ pcbit_fsm_event(dev, chan, EV_NET_SELP_RESP, NULL);
+ else {
+ /* Error */
+ printk("error %d - capi_decode_sel_proto_conf\n", err);
+ }
+ break;
+ case MSG_ACT_TRANSP_CONF:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!capi_decode_actv_trans_conf(chan, skb))
+ pcbit_fsm_event(dev, chan, EV_NET_ACTV_RESP, NULL);
+ break;
+
+ case MSG_DISC_IND:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!capi_decode_disc_ind(chan, skb))
+ pcbit_fsm_event(dev, chan, EV_NET_DISC, NULL);
+ else
+ printk(KERN_WARNING "capi_decode_disc_ind - error\n");
+ break;
+ case MSG_DISC_CONF:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!capi_decode_disc_ind(chan, skb))
+ pcbit_fsm_event(dev, chan, EV_NET_RELEASE, NULL);
+ else
+ printk(KERN_WARNING "capi_decode_disc_conf - error\n");
+ break;
+ case MSG_INFO_IND:
+#ifdef DEBUG
+ printk(KERN_DEBUG "received Info Indication - discarded\n");
+#endif
+ break;
+#ifdef DEBUG
+ case MSG_DEBUG_188:
+ capi_decode_debug_188(skb->data, skb->len);
+ break;
+
+ default:
+ printk(KERN_DEBUG "pcbit_l3_receive: unknown message %08lx\n",
+ msg);
+ fmsg = (struct msg_fmt *) &msg;
+ printk(KERN_DEBUG "cmd=%02x sub=%02x\n",
+ fmsg->cmd, fmsg->scmd);
+ break;
+#endif
+ }
+
+ skb->free = 1;
+
+ kfree_skb(skb, FREE_READ);
+
+}
+
+/*
+ * Single statbuf
+ * should be a statbuf per device
+ */
+
+static char statbuf[STATBUF_LEN];
+static int stat_st = 0;
+static int stat_end = 0;
+
+
+extern inline int memcpy_to_COND(int flag, void *d, void *s, int len)
+{
+ if (flag)
+ return copy_to_user(d, s, len);
+ memcpy(d, s, len);
+ return 0;
+}
+
+
+int pcbit_stat(u_char* buf, int len, int user, int driver, int channel)
+{
+ int stat_count;
+ stat_count = stat_end - stat_st;
+
+ if (stat_count < 0)
+ stat_count = STATBUF_LEN - stat_st + stat_end;
+
+ /* FIXME: should we sleep and wait for more cookies ? */
+ if (len > stat_count)
+ len = stat_count;
+
+ if (stat_st < stat_end)
+ {
+ if (memcpy_to_COND(user, buf, statbuf + stat_st, len))
+ return -EFAULT;
+ stat_st += len;
+ }
+ else
+ {
+ if (len > STATBUF_LEN - stat_st)
+ {
+ if (memcpy_to_COND(user, buf, statbuf + stat_st,
+ STATBUF_LEN - stat_st))
+ return -EFAULT;
+ if (memcpy_to_COND(user, buf, statbuf,
+ len - (STATBUF_LEN - stat_st)))
+ return -EFAULT;
+ stat_st = len - (STATBUF_LEN - stat_st);
+ }
+ else
+ {
+ if (memcpy_to_COND(user, buf, statbuf + stat_st,
+ len))
+ return -EFAULT;
+
+ stat_st += len;
+
+ if (stat_st == STATBUF_LEN)
+ stat_st = 0;
+ }
+ }
+
+ if (stat_st == stat_end)
+ stat_st = stat_end = 0;
+
+ return len;
+}
+
+static void pcbit_logstat(struct pcbit_dev *dev, char *str)
+{
+ int i;
+ isdn_ctrl ictl;
+
+ for (i=stat_end; i<strlen(str); i++)
+ {
+ statbuf[i]=str[i];
+ stat_end = (stat_end + 1) % STATBUF_LEN;
+ if (stat_end == stat_st)
+ stat_st = (stat_st + 1) % STATBUF_LEN;
+ }
+
+ ictl.command=ISDN_STAT_STAVAIL;
+ ictl.driver=dev->id;
+ ictl.arg=strlen(str);
+ dev->dev_if->statcallb(&ictl);
+}
+
+extern char * isdn_state_table[];
+extern char * strisdnevent(unsigned short);
+
+
+void pcbit_state_change(struct pcbit_dev * dev, struct pcbit_chan * chan,
+ unsigned short i, unsigned short ev, unsigned short f)
+{
+ char buf[256];
+
+ sprintf(buf, "change on device: %d channel:%d\n%s -> %s -> %s\n",
+ dev->id, chan->id,
+ isdn_state_table[i], strisdnevent(ev), isdn_state_table[f]
+ );
+
+#ifdef DEBUG
+ printk("%s", buf);
+#endif
+
+ pcbit_logstat(dev, buf);
+}
+
+static void set_running_timeout(unsigned long ptr)
+{
+ struct pcbit_dev * dev;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "set_running_timeout\n");
+#endif
+ dev = (struct pcbit_dev *) ptr;
+
+ wake_up_interruptible(&dev->set_running_wq);
+}
+
+static int set_protocol_running(struct pcbit_dev * dev)
+{
+ isdn_ctrl ctl;
+
+ init_timer(&dev->set_running_timer);
+
+ dev->set_running_timer.function = &set_running_timeout;
+ dev->set_running_timer.data = (ulong) dev;
+ dev->set_running_timer.expires = jiffies + SET_RUN_TIMEOUT;
+
+ /* kick it */
+
+ dev->l2_state = L2_STARTING;
+
+ writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
+ dev->sh_mem + BANK4);
+
+ add_timer(&dev->set_running_timer);
+
+ interruptible_sleep_on(&dev->set_running_wq);
+
+ del_timer(&dev->set_running_timer);
+
+ if (dev->l2_state == L2_RUNNING)
+ {
+ printk(KERN_DEBUG "pcbit: running\n");
+
+ dev->unack_seq = dev->send_seq;
+
+ dev->writeptr = dev->sh_mem;
+ dev->readptr = dev->sh_mem + BANK2;
+
+ /* tell the good news to the upper layer */
+ ctl.driver = dev->id;
+ ctl.command = ISDN_STAT_RUN;
+
+ dev->dev_if->statcallb(&ctl);
+ }
+ else
+ {
+ printk(KERN_DEBUG "pcbit: initialization failed\n");
+ printk(KERN_DEBUG "pcbit: firmware not loaded\n");
+
+ dev->l2_state = L2_DOWN;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Bank3 = %02x\n",
+ readb(dev->sh_mem + BANK3));
+#endif
+ *(dev->sh_mem + BANK4) = 0x40U;
+
+ /* warn the upper layer */
+ ctl.driver = dev->id;
+ ctl.command = ISDN_STAT_STOP;
+
+ dev->dev_if->statcallb(&ctl);
+
+ return -EL2HLT; /* Level 2 halted */
+ }
+
+ return 0;
+}
+
+static int pcbit_ioctl(isdn_ctrl* ctl)
+{
+ struct pcbit_dev * dev;
+ struct pcbit_ioctl *cmd;
+
+ dev = finddev(ctl->driver);
+
+ if (!dev)
+ {
+ printk(KERN_DEBUG "pcbit_ioctl: unknown device\n");
+ return -ENODEV;
+ }
+
+ cmd = (struct pcbit_ioctl *) ctl->num;
+
+ switch(ctl->arg) {
+ case PCBIT_IOCTL_GETSTAT:
+ cmd->info.l2_status = dev->l2_state;
+ break;
+
+ case PCBIT_IOCTL_STRLOAD:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+
+ dev->unack_seq = dev->send_seq = dev->rcv_seq = 0;
+
+ dev->writeptr = dev->sh_mem;
+ dev->readptr = dev->sh_mem + BANK2;
+
+ dev->l2_state = L2_LOADING;
+ break;
+
+ case PCBIT_IOCTL_LWMODE:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+
+ dev->l2_state = L2_LWMODE;
+ break;
+
+ case PCBIT_IOCTL_FWMODE:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+ dev->loadptr = LOAD_ZONE_START;
+ dev->l2_state = L2_FWMODE;
+
+ break;
+ case PCBIT_IOCTL_ENDLOAD:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+ dev->l2_state = L2_DOWN;
+ break;
+
+ case PCBIT_IOCTL_SETBYTE:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+
+ /* check addr */
+ if (cmd->info.rdp_byte.addr > BANK4)
+ return -EFAULT;
+
+ writeb(cmd->info.rdp_byte.value, dev->sh_mem + cmd->info.rdp_byte.addr);
+ break;
+ case PCBIT_IOCTL_GETBYTE:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+
+ /* check addr */
+
+ if (cmd->info.rdp_byte.addr > BANK4)
+ {
+ printk("getbyte: invalid addr %04x\n", cmd->info.rdp_byte.addr);
+ return -EFAULT;
+ }
+
+ cmd->info.rdp_byte.value = readb(dev->sh_mem + cmd->info.rdp_byte.addr);
+ break;
+ case PCBIT_IOCTL_RUNNING:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+ return set_protocol_running(dev);
+ break;
+ case PCBIT_IOCTL_WATCH188:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+ pcbit_l2_write(dev, MSG_WATCH188, 0x0001, NULL, 0);
+ break;
+ case PCBIT_IOCTL_PING188:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+ pcbit_l2_write(dev, MSG_PING188_REQ, 0x0001, NULL, 0);
+ break;
+ case PCBIT_IOCTL_APION:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+ pcbit_l2_write(dev, MSG_API_ON, 0x0001, NULL, 0);
+ break;
+ case PCBIT_IOCTL_STOP:
+ dev->l2_state = L2_DOWN;
+ writeb(0x40, dev->sh_mem + BANK4);
+ dev->rcv_seq = 0;
+ dev->send_seq = 0;
+ dev->unack_seq = 0;
+ break;
+ default:
+ printk("error: unknown ioctl\n");
+ break;
+ };
+ return 0;
+}
+
+/*
+ * MSN list handling
+ *
+ * if null reject all calls
+ * if first entry has null MSN accept all calls
+ */
+
+static void pcbit_clear_msn(struct pcbit_dev *dev)
+{
+ struct msn_entry *ptr, *back;
+
+ for (ptr=dev->msn_list; ptr; )
+ {
+ back = ptr->next;
+ kfree(ptr);
+ ptr = back;
+ }
+
+ dev->msn_list = NULL;
+}
+
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list)
+{
+ struct msn_entry *ptr;
+ struct msn_entry *back = NULL;
+ char *cp, *sp;
+ int len;
+
+ if (strlen(list) == 0) {
+ ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+ if (!ptr) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ return;
+ }
+
+ ptr->msn = NULL;
+
+ ptr->next = dev->msn_list;
+ dev->msn_list = ptr;
+
+ return;
+ }
+
+ if (dev->msn_list)
+ for (back=dev->msn_list; back->next; back=back->next);
+
+ sp = list;
+
+ do {
+ cp=strchr(sp, ',');
+ if (cp)
+ len = cp - sp;
+ else
+ len = strlen(sp);
+
+ ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+
+ if (!ptr) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ return;
+ }
+ ptr->next = NULL;
+
+ ptr->msn = kmalloc(len, GFP_ATOMIC);
+ if (!ptr->msn) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ return;
+ }
+
+ memcpy(ptr->msn, sp, len - 1);
+ ptr->msn[len] = 0;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "msn: %s\n", ptr->msn);
+#endif
+ if (dev->msn_list == NULL)
+ dev->msn_list = ptr;
+ else
+ back->next = ptr;
+ back = ptr;
+ sp += len;
+ } while(cp);
+}
+
+/*
+ * check if we do signal or reject an incoming call
+ */
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn)
+{
+ struct msn_entry *ptr;
+
+ for (ptr=dev->msn_list; ptr; ptr=ptr->next) {
+
+ if (ptr->msn == NULL)
+ return 1;
+
+ if (strcmp(ptr->msn, msn) == 0)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/drivers/isdn/pcbit/edss1.c b/drivers/isdn/pcbit/edss1.c
new file mode 100644
index 000000000..1b76b3462
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * DSS.1 Finite State Machine
+ * base: ITU-T Rec Q.931
+ */
+
+/*
+ * TODO: complete the FSM
+ * move state/event descriptions to a user space logger
+ */
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/tqueue.h>
+#include <linux/skbuff.h>
+
+#include <linux/timer.h>
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "callbacks.h"
+
+
+extern void pcbit_state_change(struct pcbit_dev *, struct pcbit_chan *,
+ unsigned short i, unsigned short ev,
+ unsigned short f);
+
+extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+char * isdn_state_table[] = {
+ "Closed",
+ "Call initiated",
+ "Overlap sending",
+ "Outgoing call proceeding",
+ "NOT DEFINED",
+ "Call delivered",
+ "Call present",
+ "Call received",
+ "Connect request",
+ "Incoming call proceeding",
+ "Active",
+ "Disconnect request",
+ "Disconnect indication",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "Suspend request",
+ "NOT DEFINED",
+ "Resume request",
+ "NOT DEFINED",
+ "Release Request",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "Overlap receiving",
+ "Select protocol on B-Channel",
+ "Activate B-channel protocol"
+};
+
+#ifdef DEBUG_ERRS
+static
+struct CauseValue {
+ byte nr;
+ char *descr;
+} cvlist[]={
+ {0x01,"Unallocated (unassigned) number"},
+ {0x02,"No route to specified transit network"},
+ {0x03,"No route to destination"},
+ {0x04,"Send special information tone"},
+ {0x05,"Misdialled trunk prefix"},
+ {0x06,"Channel unacceptable"},
+ {0x07,"Channel awarded and being delivered in an established channel"},
+ {0x08,"Preemption"},
+ {0x09,"Preemption - circuit reserved for reuse"},
+ {0x10,"Normal call clearing"},
+ {0x11,"User busy"},
+ {0x12,"No user responding"},
+ {0x13,"No answer from user (user alerted)"},
+ {0x14,"Subscriber absent"},
+ {0x15,"Call rejected"},
+ {0x16,"Number changed"},
+ {0x1a,"non-selected user clearing"},
+ {0x1b,"Destination out of order"},
+ {0x1c,"Invalid number format (address incomplete)"},
+ {0x1d,"Facility rejected"},
+ {0x1e,"Response to Status enquiry"},
+ {0x1f,"Normal, unspecified"},
+ {0x22,"No circuit/channel available"},
+ {0x26,"Network out of order"},
+ {0x27,"Permanent frame mode connection out-of-service"},
+ {0x28,"Permanent frame mode connection operational"},
+ {0x29,"Temporary failure"},
+ {0x2a,"Switching equipment congestion"},
+ {0x2b,"Access information discarded"},
+ {0x2c,"Requested circuit/channel not available"},
+ {0x2e,"Precedence call blocked"},
+ {0x2f,"Resource unavailable, unspecified"},
+ {0x31,"Quality of service unavailable"},
+ {0x32,"Requested facility not subscribed"},
+ {0x35,"Outgoing calls barred within CUG"},
+ {0x37,"Incoming calls barred within CUG"},
+ {0x39,"Bearer capability not authorized"},
+ {0x3a,"Bearer capability not presently available"},
+ {0x3e,"Inconsistency in designated outgoing access information and subscriber class"},
+ {0x3f,"Service or option not available, unspecified"},
+ {0x41,"Bearer capability not implemented"},
+ {0x42,"Channel type not implemented"},
+ {0x43,"Requested facility not implemented"},
+ {0x44,"Only restricted digital information bearer capability is available"},
+ {0x4f,"Service or option not implemented"},
+ {0x51,"Invalid call reference value"},
+ {0x52,"Identified channel does not exist"},
+ {0x53,"A suspended call exists, but this call identity does not"},
+ {0x54,"Call identity in use"},
+ {0x55,"No call suspended"},
+ {0x56,"Call having the requested call identity has been cleared"},
+ {0x57,"User not member of CUG"},
+ {0x58,"Incompatible destination"},
+ {0x5a,"Non-existent CUG"},
+ {0x5b,"Invalid transit network selection"},
+ {0x5f,"Invalid message, unspecified"},
+ {0x60,"Mandatory information element is missing"},
+ {0x61,"Message type non-existent or not implemented"},
+ {0x62,"Message not compatible with call state or message type non-existent or not implemented"},
+ {0x63,"Information element/parameter non-existent or not implemented"},
+ {0x64,"Invalid information element contents"},
+ {0x65,"Message not compatible with call state"},
+ {0x66,"Recovery on timer expiry"},
+ {0x67,"Parameter non-existent or not implemented - passed on"},
+ {0x6e,"Message with unrecognized parameter discarded"},
+ {0x6f,"Protocol error, unspecified"},
+ {0x7f,"Interworking, unspecified"}
+};
+
+#endif
+
+static struct isdn_event_desc {
+ unsigned short ev;
+ char * desc;
+} isdn_event_table [] = {
+ {EV_USR_SETUP_REQ, "CC->L3: Setup Request"},
+ {EV_USR_SETUP_RESP, "CC->L3: Setup Response"},
+ {EV_USR_PROCED_REQ, "CC->L3: Proceeding Request"},
+ {EV_USR_RELEASE_REQ, "CC->L3: Release Request"},
+
+ {EV_NET_SETUP, "NET->TE: setup "},
+ {EV_NET_CALL_PROC, "NET->TE: call proceeding"},
+ {EV_NET_SETUP_ACK, "NET->TE: setup acknowledge (more info needed)"},
+ {EV_NET_CONN, "NET->TE: connect"},
+ {EV_NET_CONN_ACK, "NET->TE: connect acknowledge"},
+ {EV_NET_DISC, "NET->TE: disconnect indication"},
+ {EV_NET_RELEASE, "NET->TE: release"},
+ {EV_NET_RELEASE_COMP, "NET->TE: release complete"},
+ {EV_NET_SELP_RESP, "Board: Select B-channel protocol ack"},
+ {EV_NET_ACTV_RESP, "Board: Activate B-channel protocol ack"},
+ {EV_TIMER, "Timeout"},
+ {0, "NULL"}
+};
+
+char * strisdnevent(ushort ev)
+{
+ struct isdn_event_desc * entry;
+
+ for (entry = isdn_event_table; entry->ev; entry++)
+ if (entry->ev == ev)
+ break;
+
+ return entry->desc;
+}
+
+/*
+ * Euro ISDN finite state machine
+ */
+
+static struct fsm_timer_entry fsm_timers[] = {
+ {ST_CALL_PROC, 10},
+ {ST_DISC_REQ, 2},
+ {ST_ACTIVE_SELP, 5},
+ {ST_ACTIVE_ACTV, 5},
+ {ST_INCM_PROC, 10},
+ {ST_CONN_REQ, 2},
+ {0xff, 0}
+};
+
+static struct fsm_entry fsm_table[] = {
+/* Connect Phase */
+ /* Outgoing */
+ {ST_NULL, ST_CALL_INIT, EV_USR_SETUP_REQ, cb_out_1},
+
+ {ST_CALL_INIT, ST_OVER_SEND, EV_NET_SETUP_ACK, cb_notdone},
+ {ST_CALL_INIT, ST_CALL_PROC, EV_NET_CALL_PROC, NULL},
+ {ST_CALL_INIT, ST_NULL, EV_NET_DISC, cb_out_2},
+
+ {ST_CALL_PROC, ST_ACTIVE_SELP, EV_NET_CONN, cb_out_2},
+ {ST_CALL_PROC, ST_NULL, EV_NET_DISC, cb_disc_1},
+ {ST_CALL_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ /* Incoming */
+ {ST_NULL, ST_CALL_PRES, EV_NET_SETUP, NULL},
+
+ {ST_CALL_PRES, ST_INCM_PROC, EV_USR_PROCED_REQ, cb_in_1},
+ {ST_CALL_PRES, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ {ST_INCM_PROC, ST_CONN_REQ, EV_USR_SETUP_RESP, cb_in_2},
+ {ST_INCM_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ {ST_CONN_REQ, ST_ACTIVE_SELP, EV_NET_CONN_ACK, cb_in_3},
+
+ /* Active */
+ {ST_ACTIVE, ST_NULL, EV_NET_DISC, cb_disc_1},
+ {ST_ACTIVE, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+ {ST_ACTIVE, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+ /* Disconnect */
+
+ {ST_DISC_REQ, ST_NULL, EV_NET_DISC, cb_disc_1},
+ {ST_DISC_REQ, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+ /* protocol selection */
+ {ST_ACTIVE_SELP, ST_ACTIVE_ACTV, EV_NET_SELP_RESP, cb_selp_1},
+ {ST_ACTIVE_SELP, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ {ST_ACTIVE_ACTV, ST_ACTIVE, EV_NET_ACTV_RESP, cb_open},
+ {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ /* Timers */
+ {ST_CALL_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_DISC_REQ, ST_NULL, EV_TIMER, cb_disc_3},
+ {ST_ACTIVE_SELP, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_INCM_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_CONN_REQ, ST_CONN_REQ, EV_TIMER, cb_in_2},
+
+ {0xff, 0, 0, NULL}
+};
+
+
+static void pcbit_fsm_timer(unsigned long data)
+{
+ struct pcbit_dev *dev;
+ struct pcbit_chan *chan;
+
+ chan = (struct pcbit_chan *) data;
+
+ del_timer(&chan->fsm_timer);
+ chan->fsm_timer.function = NULL;
+
+ dev = chan2dev(chan);
+
+ if (dev == NULL) {
+ printk(KERN_WARNING "pcbit: timer for unknown device\n");
+ return;
+ }
+
+ pcbit_fsm_event(dev, chan, EV_TIMER, NULL);
+}
+
+
+void pcbit_fsm_event(struct pcbit_dev *dev, struct pcbit_chan *chan,
+ unsigned short event, struct callb_data *data)
+{
+ struct fsm_entry * action;
+ struct fsm_timer_entry *tentry;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+
+ for (action = fsm_table; action->init != 0xff; action++)
+ if (action->init == chan->fsm_state && action->event == event)
+ break;
+
+ if (action->init == 0xff) {
+
+ printk(KERN_DEBUG "fsm error: event %x on state %x\n",
+ event, chan->fsm_state);
+ restore_flags(flags);
+ return;
+ }
+
+ if (chan->fsm_timer.function) {
+ del_timer(&chan->fsm_timer);
+ chan->fsm_timer.function = NULL;
+ }
+
+ chan->fsm_state = action->final;
+
+ pcbit_state_change(dev, chan, action->init, event, action->final);
+
+ for (tentry = fsm_timers; tentry->init != 0xff; tentry++)
+ if (tentry->init == chan->fsm_state)
+ break;
+
+ if (tentry->init != 0xff) {
+ init_timer(&chan->fsm_timer);
+ chan->fsm_timer.function = &pcbit_fsm_timer;
+ chan->fsm_timer.data = (ulong) chan;
+ chan->fsm_timer.expires = jiffies + tentry->timeout * HZ;
+ add_timer(&chan->fsm_timer);
+ }
+
+ restore_flags(flags);
+
+ if (action->callb)
+ action->callb(dev, chan, data);
+
+}
+
+
+
+
diff --git a/drivers/isdn/pcbit/edss1.h b/drivers/isdn/pcbit/edss1.h
new file mode 100644
index 000000000..1bf554dd5
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * DSS.1 module definitions
+ */
+
+#ifndef EDSS1_H
+#define EDSS1_H
+
+/* ISDN states */
+
+#define ST_NULL 0
+#define ST_CALL_INIT 1 /* Call initiated */
+#define ST_OVER_SEND 2 /* Overlap sending - Requests More Info 4 call */
+#define ST_CALL_PROC 3 /* Call Proceeding */
+#define ST_CALL_DELV 4
+#define ST_CALL_PRES 6 /* Call Present - Received CONN.IND */
+#define ST_CALL_RECV 7 /* Alerting sent */
+#define ST_CONN_REQ 8 /* Answered - waiting 4 CONN.CONF */
+#define ST_INCM_PROC 9
+#define ST_ACTIVE 10
+#define ST_DISC_REQ 11
+#define ST_DISC_IND 12
+#define ST_SUSP_REQ 15
+#define ST_RESM_REQ 17
+#define ST_RELS_REQ 19
+#define ST_OVER_RECV 25
+
+#define ST_ACTIVE_SELP 26 /* Select protocol on B-Channel */
+#define ST_ACTIVE_ACTV 27 /* Activate B-channel protocol */
+
+#define MAX_STATE ST_ACTIVE_ACTV
+
+#define EV_NULL 0
+#define EV_USR_SETUP_REQ 1
+#define EV_USR_SETUP_RESP 2
+#define EV_USR_PROCED_REQ 3
+#define EV_USR_RELEASE_REQ 4
+#define EV_USR_REJECT_REQ 4
+
+#define EV_NET_SETUP 16
+#define EV_NET_CALL_PROC 17
+#define EV_NET_SETUP_ACK 18
+#define EV_NET_CONN 19
+#define EV_NET_CONN_ACK 20
+
+#define EV_NET_SELP_RESP 21
+#define EV_NET_ACTV_RESP 22
+
+#define EV_NET_DISC 23
+#define EV_NET_RELEASE 24
+#define EV_NET_RELEASE_COMP 25
+
+#define EV_TIMER 26
+#define EV_ERROR 32
+
+/*
+ * Cause values
+ * only the ones we use
+ */
+
+#define CAUSE_NORMAL 0x10U
+#define CAUSE_NOCHAN 0x22U
+
+struct callb_data {
+ unsigned short type;
+ union {
+ struct ConnInfo {
+ char *CalledPN;
+ char *CallingPN;
+ } setup;
+ unsigned short cause;
+ } data;
+};
+
+struct fsm_entry {
+ unsigned short init;
+ unsigned short final;
+ unsigned short event;
+ void (*callb)(struct pcbit_dev *, struct pcbit_chan *, struct callb_data*);
+};
+
+struct fsm_timer_entry {
+ unsigned short init;
+ unsigned long timeout; /* in seconds */
+};
+
+
+extern void pcbit_fsm_event(struct pcbit_dev *, struct pcbit_chan *,
+ unsigned short event, struct callb_data *);
+#endif
+
+
+
diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c
new file mode 100644
index 000000000..4410d1e0c
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.c
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * PCBIT-D low-layer interface
+ */
+
+/*
+ * Based on documentation provided by Inesc:
+ * - "Interface com bus do PC para o PCBIT e PCBIT-D", Inesc, Jan 93
+ */
+
+/*
+ * TODO: better handling of errors
+ * re-write/remove debug printks
+ */
+
+#define __NO_VERSION__
+
+
+#ifdef MODULE
+#define INCLUDE_INLINE_FUNCS
+#endif
+
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/tqueue.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+
+#undef DEBUG_FRAG
+
+
+
+/*
+ * task queue struct
+ */
+
+
+
+/*
+ * Layer 3 packet demultiplexer
+ * drv.c
+ */
+
+extern void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg,
+ struct sk_buff * skb,
+ ushort hdr_len, ushort refnum);
+
+/*
+ * Prototypes
+ */
+
+void pcbit_deliver(void * data);
+static void pcbit_transmit(struct pcbit_dev * dev);
+
+static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack);
+
+static void pcbit_l2_error(struct pcbit_dev *dev);
+static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info);
+static void pcbit_l2_err_recover(unsigned long data);
+
+static void pcbit_firmware_bug(struct pcbit_dev * dev);
+
+static __inline__ void pcbit_sched_delivery(struct pcbit_dev *dev)
+{
+ queue_task(&dev->qdelivery, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+
+/*
+ * Called from layer3
+ */
+
+int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum,
+ struct sk_buff *skb, unsigned short hdr_len)
+
+{
+ struct frame_buf * frame, * ptr;
+ unsigned long flags;
+
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return -1;
+ }
+
+ if ( (frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf),
+ GFP_ATOMIC)) == NULL ) {
+ printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
+ dev_kfree_skb(skb, FREE_WRITE);
+ return -1;
+ }
+
+ frame->msg = msg;
+ frame->refnum = refnum;
+ frame->copied = 0;
+ frame->hdr_len = hdr_len;
+
+ if (skb) {
+ frame->dt_len = skb->len - hdr_len;
+ if (frame->dt_len == 0)
+ skb->lock++;
+ }
+ else
+ frame->dt_len = 0;
+
+ frame->skb = skb;
+
+ frame->next = NULL;
+
+ save_flags(flags);
+ cli();
+
+ if (dev->write_queue == NULL) {
+ dev->write_queue = frame;
+ restore_flags(flags);
+ pcbit_transmit(dev);
+ }
+ else {
+ for(ptr=dev->write_queue; ptr->next; ptr=ptr->next);
+ ptr->next = frame;
+
+ restore_flags(flags);
+ }
+ return 0;
+}
+
+static __inline__ void pcbit_tx_update(struct pcbit_dev *dev, ushort len)
+{
+ u_char info;
+
+ dev->send_seq = (dev->send_seq + 1) % 8;
+
+ dev->fsize[dev->send_seq] = len;
+ info = 0;
+ info |= dev->rcv_seq << 3;
+ info |= dev->send_seq;
+
+ writeb(info, dev->sh_mem + BANK4);
+
+}
+
+/*
+ * called by interrupt service routine or by write_2
+ */
+
+static void pcbit_transmit(struct pcbit_dev * dev)
+{
+ struct frame_buf * frame = NULL;
+ unsigned char unacked;
+ int flen; /* fragment frame length including all headers */
+ int totlen; /* non-fragmented frame length */
+ int free;
+ int count, cp_len;
+ unsigned long flags;
+ unsigned short tt;
+
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+ return;
+
+ unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07;
+
+ save_flags(flags);
+ cli();
+
+ if (dev->free > 16 && dev->write_queue && unacked < 7) {
+
+ if (!dev->w_busy)
+ dev->w_busy = 1;
+ else
+ {
+ restore_flags(flags);
+ return;
+ }
+
+
+ frame = dev->write_queue;
+ free = dev->free;
+
+ restore_flags(flags);
+
+ if (frame->copied == 0) {
+
+ /* Type 0 frame */
+
+ struct msg_fmt * msg;
+
+ if (frame->skb)
+ totlen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len;
+ else
+ totlen = FRAME_HDR_LEN + PREHDR_LEN;
+
+ flen = MIN(totlen, free);
+
+ msg = (struct msg_fmt *) &(frame->msg);
+
+ /*
+ * Board level 2 header
+ */
+
+ pcbit_writew(dev, flen - FRAME_HDR_LEN);
+
+ pcbit_writeb(dev, msg->cpu);
+
+ pcbit_writeb(dev, msg->proc);
+
+ /* TH */
+ pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+ /* TD */
+ pcbit_writew(dev, frame->dt_len);
+
+
+ /*
+ * Board level 3 fixed-header
+ */
+
+ /* LEN = TH */
+ pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+ /* XX */
+ pcbit_writew(dev, 0);
+
+ /* C + S */
+ pcbit_writeb(dev, msg->cmd);
+ pcbit_writeb(dev, msg->scmd);
+
+ /* NUM */
+ pcbit_writew(dev, frame->refnum);
+
+ count = FRAME_HDR_LEN + PREHDR_LEN;
+ }
+ else {
+ /* Type 1 frame */
+
+ totlen = 2 + (frame->skb->len - frame->copied);
+
+ flen = MIN(totlen, free);
+
+ /* TT */
+ tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */
+ pcbit_writew(dev, tt);
+
+ count = 2;
+ }
+
+ if (frame->skb) {
+ cp_len = MIN(frame->skb->len - frame->copied,
+ flen - count);
+
+ memcpy_topcbit(dev, frame->skb->data + frame->copied,
+ cp_len);
+ frame->copied += cp_len;
+ }
+
+ /* bookkeeping */
+ dev->free -= flen;
+ pcbit_tx_update(dev, flen);
+
+ save_flags(flags);
+ cli();
+
+
+ if (frame->skb == NULL || frame->copied == frame->skb->len) {
+
+ dev->write_queue = frame->next;
+
+ if (frame->skb != NULL) {
+ /* free frame */
+ dev_kfree_skb(frame->skb, FREE_WRITE);
+ }
+
+ kfree(frame);
+ }
+
+ dev->w_busy = 0;
+ restore_flags(flags);
+ }
+ else
+ {
+ restore_flags(flags);
+#ifdef DEBUG
+ printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
+ unacked, dev->free, dev->write_queue ? "not empty" :
+ "empty");
+#endif
+ }
+}
+
+
+/*
+ * deliver a queued frame to the upper layer
+ */
+
+void pcbit_deliver(void * data)
+{
+ struct frame_buf *frame;
+ unsigned long flags;
+ struct msg_fmt msg;
+ struct pcbit_dev *dev = (struct pcbit_dev *) data;
+
+ save_flags(flags);
+ cli();
+
+ while((frame=dev->read_queue))
+ {
+ dev->read_queue = frame->next;
+ restore_flags(flags);
+
+ msg.cpu = 0;
+ msg.proc = 0;
+ msg.cmd = frame->skb->data[2];
+ msg.scmd = frame->skb->data[3];
+
+ frame->refnum = *((ushort*) frame->skb->data + 4);
+ frame->msg = *((ulong*) &msg);
+
+ skb_pull(frame->skb, 6);
+
+ pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len,
+ frame->refnum);
+
+ kfree(frame);
+
+ save_flags(flags);
+ cli();
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * Reads BANK 2 & Reassembles
+ */
+
+static void pcbit_receive(struct pcbit_dev * dev)
+{
+ unsigned short tt;
+ u_char cpu, proc;
+ struct frame_buf * frame = NULL;
+ unsigned long flags;
+ u_char type1;
+
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+ return;
+
+ tt = pcbit_readw(dev);
+
+ if ((tt & 0x7fffU) > 511) {
+ printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n",
+ tt);
+ pcbit_l2_error(dev);
+ return;
+ }
+
+ if (!(tt & 0x8000U))
+ { /* Type 0 */
+ type1 = 0;
+
+ if (dev->read_frame) {
+ printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
+#if 0
+ pcbit_l2_error(dev);
+ return;
+#else
+ /* discard previous queued frame */
+ if (dev->read_frame->skb) {
+ dev->read_frame->skb->free = 1;
+ kfree_skb(dev->read_frame->skb, FREE_READ);
+ }
+ kfree(dev->read_frame);
+ dev->read_frame = NULL;
+#endif
+ }
+
+ frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC);
+
+ if (frame == NULL) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ return;
+ }
+ memset(frame, 0, sizeof(struct frame_buf));
+
+ cpu = pcbit_readb(dev);
+ proc = pcbit_readb(dev);
+
+
+ if (cpu != 0x06 && cpu != 0x02)
+ {
+ printk (KERN_DEBUG "pcbit: invalid cpu value\n");
+ kfree(frame);
+ pcbit_l2_error(dev);
+ return;
+ }
+
+ /*
+ * we discard cpu & proc on receiving
+ * but we read it to update the pointer
+ */
+
+ frame->hdr_len = pcbit_readw(dev);
+ frame->dt_len = pcbit_readw(dev);
+
+ /*
+ * 0 sized packet
+ * I don't know if they are an error or not...
+ * But they are very frequent
+ * Not documented
+ */
+
+ if (frame->hdr_len == 0) {
+ kfree(frame);
+#ifdef DEBUG
+ printk(KERN_DEBUG "0 sized frame\n");
+#endif
+ pcbit_firmware_bug(dev);
+ return;
+ }
+
+ /* sanity check the length values */
+ if (frame->hdr_len > 1024 || frame->dt_len > 2048)
+ {
+#ifdef DEBUG
+ printk(KERN_DEBUG "length problem: ");
+ printk(KERN_DEBUG "TH=%04x TD=%04x\n",
+ frame->hdr_len,
+ frame->dt_len);
+#endif
+ pcbit_l2_error(dev);
+ kfree(frame);
+ return;
+ }
+
+ /* minimum frame read */
+
+ frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len +
+ ((frame->hdr_len + 15) & ~15));
+
+ if (!frame->skb) {
+ printk(KERN_DEBUG "pcbit_receive: out of memory\n");
+ kfree(frame);
+ return;
+ }
+
+ /* 16 byte alignment for IP */
+ if (frame->dt_len)
+ skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
+
+ }
+ else {
+ /* Type 1 */
+ type1 = 1;
+ tt &= 0x7fffU;
+
+ if (!(frame = dev->read_frame)) {
+ printk("Type 1 frame and no frame queued\n");
+#if 1
+ /* usually after an error: toss frame */
+ dev->readptr += tt;
+ if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN)
+ dev->readptr -= BANKLEN;
+#else
+ pcbit_l2_error(dev);
+#endif
+ return;
+
+ }
+ }
+
+ memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt);
+
+ frame->copied += tt;
+
+ if (frame->copied == frame->hdr_len + frame->dt_len) {
+
+ save_flags(flags);
+ cli();
+
+ if (type1) {
+ dev->read_frame = NULL;
+ }
+
+ if (dev->read_queue) {
+ struct frame_buf *ptr;
+ for(ptr=dev->read_queue;ptr->next;ptr=ptr->next);
+ ptr->next = frame;
+ }
+ else
+ dev->read_queue = frame;
+
+ restore_flags(flags);
+
+ }
+ else {
+ save_flags(flags);
+ cli();
+ dev->read_frame = frame;
+ restore_flags(flags);
+ }
+}
+
+/*
+ * The board sends 0 sized frames
+ * They are TDATA_CONFs that get messed up somehow
+ * gotta send a fake acknowledgment to the upper layer somehow
+ */
+
+static __inline__ void pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan * chan)
+{
+ isdn_ctrl ictl;
+
+ if (chan->queued) {
+ chan->queued--;
+
+ ictl.driver = dev->id;
+ ictl.command = ISDN_STAT_BSENT;
+ ictl.arg = chan->id;
+ dev->dev_if->statcallb(&ictl);
+ }
+}
+
+static void pcbit_firmware_bug(struct pcbit_dev * dev)
+{
+ struct pcbit_chan *chan;
+
+ chan = dev->b1;
+
+ if (chan->fsm_state == ST_ACTIVE) {
+ pcbit_fake_conf(dev, chan);
+ }
+
+ chan = dev->b2;
+
+ if (chan->fsm_state == ST_ACTIVE) {
+ pcbit_fake_conf(dev, chan);
+ }
+
+}
+
+void pcbit_irq_handler(int interrupt, void * devptr, struct pt_regs *regs)
+{
+ struct pcbit_dev * dev;
+ u_char info, ack_seq, read_seq;
+
+ dev = (struct pcbit_dev *) devptr;
+
+ if (!dev)
+ {
+ printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
+ return;
+ }
+
+ if (dev->interrupt) {
+ printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
+ return;
+ }
+
+ dev->interrupt = 1;
+
+ info = readb(dev->sh_mem + BANK3);
+
+ if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR)
+ {
+ pcbit_l2_active_conf(dev, info);
+ dev->interrupt = 0;
+ return;
+ }
+
+ if (info & 0x40U) /* E bit set */
+ {
+#ifdef DEBUG
+ printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n");
+#endif
+ pcbit_l2_error(dev);
+ dev->interrupt = 0;
+ return;
+ }
+
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+ {
+ dev->interrupt = 0;
+ return;
+ }
+
+ ack_seq = (info >> 3) & 0x07U;
+ read_seq = (info & 0x07U);
+
+ dev->interrupt = 0;
+
+ if (read_seq != dev->rcv_seq)
+ {
+ while (read_seq != dev->rcv_seq)
+ {
+ pcbit_receive(dev);
+ dev->rcv_seq = (dev->rcv_seq + 1) % 8;
+ }
+ pcbit_sched_delivery(dev);
+ }
+
+ if (ack_seq != dev->unack_seq)
+ {
+ pcbit_recv_ack(dev, ack_seq);
+ }
+
+
+ info = dev->rcv_seq << 3;
+ info |= dev->send_seq;
+
+ writeb(info, dev->sh_mem + BANK4);
+}
+
+
+static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
+{
+ u_char state;
+
+ state = dev->l2_state;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "layer2_active_confirm\n");
+#endif
+
+
+ if (info & 0x80U ) {
+ dev->rcv_seq = info & 0x07U;
+ dev->l2_state = L2_RUNNING;
+ }
+ else
+ dev->l2_state = L2_DOWN;
+
+ if (state == L2_STARTING)
+ wake_up_interruptible(&dev->set_running_wq);
+
+ if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
+ pcbit_transmit(dev);
+ }
+
+}
+
+static void pcbit_l2_err_recover(unsigned long data)
+{
+
+ struct pcbit_dev * dev;
+ struct frame_buf *frame;
+
+ dev = (struct pcbit_dev *) data;
+
+ del_timer(&dev->error_recover_timer);
+ if (dev->w_busy || dev->r_busy)
+ {
+ init_timer(&dev->error_recover_timer);
+ dev->error_recover_timer.expires = jiffies + ERRTIME;
+ add_timer(&dev->error_recover_timer);
+ return;
+ }
+
+ dev->w_busy = dev->r_busy = 1;
+
+ if (dev->read_frame)
+ {
+ if (dev->read_frame->skb)
+ {
+ dev->read_frame->skb->free = 1;
+ kfree_skb(dev->read_frame->skb, FREE_READ);
+ }
+ kfree(dev->read_frame);
+ dev->read_frame = NULL;
+ }
+
+
+ if (dev->write_queue)
+ {
+ frame = dev->write_queue;
+#ifdef FREE_ON_ERROR
+ dev->write_queue = dev->write_queue->next;
+
+ if (frame->skb) {
+ dev_kfree_skb(frame->skb, FREE_WRITE);
+ }
+
+ kfree(frame);
+#else
+ frame->copied = 0;
+#endif
+ }
+
+ dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
+ dev->free = 511;
+ dev->l2_state = L2_ERROR;
+
+ /* this is an hack... */
+ pcbit_firmware_bug(dev);
+
+ dev->writeptr = dev->sh_mem;
+ dev->readptr = dev->sh_mem + BANK2;
+
+ writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
+ dev->sh_mem + BANK4);
+ dev->w_busy = dev->r_busy = 0;
+
+}
+
+static void pcbit_l2_error(struct pcbit_dev *dev)
+{
+ if (dev->l2_state == L2_RUNNING) {
+
+ printk(KERN_INFO "pcbit: layer 2 error\n");
+
+#ifdef DEBUG
+ log_state(dev);
+#endif
+
+ dev->l2_state = L2_DOWN;
+
+ init_timer(&dev->error_recover_timer);
+ dev->error_recover_timer.function = &pcbit_l2_err_recover;
+ dev->error_recover_timer.data = (ulong) dev;
+ dev->error_recover_timer.expires = jiffies + ERRTIME;
+ add_timer(&dev->error_recover_timer);
+ }
+}
+
+/*
+ * Description:
+ * if board acks frames
+ * update dev->free
+ * call pcbit_transmit to write possible queued frames
+ */
+
+static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
+{
+ int i, count;
+ int unacked;
+
+ unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07;
+
+ /* dev->unack_seq < ack <= dev->send_seq; */
+
+ if (unacked)
+ {
+
+ if (dev->send_seq > dev->unack_seq)
+ if (ack <= dev->unack_seq || ack > dev->send_seq)
+ {
+ printk(KERN_DEBUG
+ "layer 2 ack unacceptable - dev %d",
+ dev->id);
+
+ pcbit_l2_error(dev);
+ }
+ else
+ if (ack > dev->send_seq && ack <= dev->unack_seq)
+ {
+ printk(KERN_DEBUG
+ "layer 2 ack unacceptable - dev %d",
+ dev->id);
+ pcbit_l2_error(dev);
+ }
+
+ /* ack is acceptable */
+
+
+ i = dev->unack_seq;
+
+ do {
+ dev->unack_seq = i = (i + 1) % 8;
+ dev->free += dev->fsize[i];
+ } while (i != ack);
+
+ count = 0;
+ while (count < 7 && dev->write_queue)
+ {
+ u8 lsend_seq = dev->send_seq;
+
+ pcbit_transmit(dev);
+
+ if (dev->send_seq == lsend_seq)
+ break;
+ count++;
+ }
+ }
+ else
+ printk(KERN_DEBUG "recv_ack: unacked = 0\n");
+}
diff --git a/drivers/isdn/pcbit/layer2.h b/drivers/isdn/pcbit/layer2.h
new file mode 100644
index 000000000..2f56a5844
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * PCBIT-D low-layer interface definitions
+ */
+
+#ifndef LAYER2_H
+#define LAYER2_H
+
+#include <asm/byteorder.h>
+
+#define BANK1 0x0000U /* PC -> Board */
+#define BANK2 0x01ffU /* Board -> PC */
+#define BANK3 0x03feU /* Att Board */
+#define BANK4 0x03ffU /* Att PC */
+
+#define BANKLEN 0x01FFU
+
+#define LOAD_ZONE_START 0x03f8U
+#define LOAD_ZONE_END 0x03fdU
+
+#define LOAD_RETRY 18000000
+
+
+
+/* TAM - XX - C - S - NUM */
+#define PREHDR_LEN 8
+/* TT - M - I - TH - TD */
+#define FRAME_HDR_LEN 8
+
+#define MSG_CONN_REQ 0x08000100
+#define MSG_CONN_CONF 0x00000101
+#define MSG_CONN_IND 0x00000102
+#define MSG_CONN_RESP 0x08000103
+
+#define MSG_CONN_ACTV_REQ 0x08000300
+#define MSG_CONN_ACTV_CONF 0x00000301
+#define MSG_CONN_ACTV_IND 0x00000302
+#define MSG_CONN_ACTV_RESP 0x08000303
+
+#define MSG_DISC_REQ 0x08000400
+#define MSG_DISC_CONF 0x00000401
+#define MSG_DISC_IND 0x00000402
+#define MSG_DISC_RESP 0x08000403
+
+#define MSG_TDATA_REQ 0x0908E200
+#define MSG_TDATA_CONF 0x0000E201
+#define MSG_TDATA_IND 0x0000E202
+#define MSG_TDATA_RESP 0x0908E203
+
+#define MSG_SELP_REQ 0x09004000
+#define MSG_SELP_CONF 0x00004001
+
+#define MSG_ACT_TRANSP_REQ 0x0908E000
+#define MSG_ACT_TRANSP_CONF 0x0000E001
+
+#define MSG_STPROT_REQ 0x09004100
+#define MSG_STPROT_CONF 0x00004101
+
+#define MSG_PING188_REQ 0x09030500
+#define MSG_PING188_CONF 0x000005bc
+
+#define MSG_WATCH188 0x09030400
+
+#define MSG_API_ON 0x08020102
+#define MSG_POOL_PCBIT 0x08020400
+#define MSG_POOL_PCBIT_CONF 0x00000401
+
+#define MSG_INFO_IND 0x00002602
+#define MSG_INFO_RESP 0x08002603
+
+#define MSG_DEBUG_188 0x0000ff00
+
+/*
+
+ long 4 3 2 1
+ Intel 1 2 3 4
+*/
+
+struct msg_fmt {
+#ifdef __LITTLE_ENDIAN /* Little Endian */
+ u_char scmd;
+ u_char cmd;
+ u_char proc;
+ u_char cpu;
+#else
+#error "Non-Intel CPU"
+ u_char cpu;
+ u_char proc;
+ u_char cmd;
+ u_char scmd;
+#endif
+};
+
+
+#define MAX_QUEUED 7
+
+#define SCHED_READ 0x01
+#define SCHED_WRITE 0x02
+
+#define SET_RUN_TIMEOUT 2*HZ /* 2 seconds */
+
+
+struct frame_buf {
+ ulong msg;
+ unsigned short refnum;
+ unsigned short dt_len;
+ unsigned short hdr_len;
+ struct sk_buff *skb;
+ unsigned short copied;
+ struct frame_buf * next;
+};
+
+#define MIN(a,b) ((a<b)?a:b)
+
+extern int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum,
+ struct sk_buff *skb, unsigned short hdr_len);
+
+extern void pcbit_irq_handler(int interrupt, void *, struct pt_regs *regs);
+
+extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+#ifdef DEBUG
+static __inline__ void log_state(struct pcbit_dev *dev) {
+ printk(KERN_DEBUG "writeptr = %ld\n",
+ (ulong) (dev->writeptr - dev->sh_mem));
+ printk(KERN_DEBUG "readptr = %ld\n",
+ (ulong) (dev->readptr - (dev->sh_mem + BANK2)));
+ printk(KERN_DEBUG "{rcv_seq=%01x, send_seq=%01x, unack_seq=%01x}\n",
+ dev->rcv_seq, dev->send_seq, dev->unack_seq);
+}
+#endif
+
+static __inline__ struct pcbit_dev * chan2dev(struct pcbit_chan * chan)
+{
+ struct pcbit_dev * dev;
+ int i;
+
+
+ for (i=0; i<MAX_PCBIT_CARDS; i++)
+ if ((dev=dev_pcbit[i]))
+ if (dev->b1 == chan || dev->b2 == chan)
+ return dev;
+ return NULL;
+
+}
+
+static __inline__ struct pcbit_dev * finddev(int id)
+{
+ struct pcbit_dev * dev;
+ int i;
+
+ for (i=0; i<MAX_PCBIT_CARDS; i++)
+ if ((dev=dev_pcbit[i]))
+ if (dev->id == id)
+ return dev;
+ return NULL;
+}
+
+
+/*
+ * Support routines for reading and writing in the board
+ */
+
+static __inline__ void pcbit_writeb(struct pcbit_dev *dev, unsigned char dt)
+{
+ writeb(dt, dev->writeptr++);
+ if (dev->writeptr == dev->sh_mem + BANKLEN)
+ dev->writeptr = dev->sh_mem;
+}
+
+static __inline__ void pcbit_writew(struct pcbit_dev *dev, unsigned short dt)
+{
+ int dist;
+
+ dist = BANKLEN - (dev->writeptr - dev->sh_mem);
+ switch (dist) {
+ case 2:
+ writew(dt, dev->writeptr);
+ dev->writeptr = dev->sh_mem;
+ break;
+ case 1:
+ writeb((u_char) (dt & 0x00ffU), dev->writeptr);
+ dev->writeptr = dev->sh_mem;
+ writeb((u_char) (dt >> 8), dev->writeptr++);
+ break;
+ default:
+ writew(dt, dev->writeptr);
+ dev->writeptr += 2;
+ break;
+ };
+}
+
+static __inline__ void memcpy_topcbit(struct pcbit_dev * dev, u_char * data,
+ int len)
+{
+ int diff;
+
+ diff = len - (BANKLEN - (dev->writeptr - dev->sh_mem) );
+
+ if (diff > 0)
+ {
+ memcpy_toio(dev->writeptr, data, len - diff);
+ memcpy_toio(dev->sh_mem, data + (len - diff), diff);
+ dev->writeptr = dev->sh_mem + diff;
+ }
+ else
+ {
+ memcpy_toio(dev->writeptr, data, len);
+
+ dev->writeptr += len;
+ if (diff == 0)
+ dev->writeptr = dev->sh_mem;
+ }
+}
+
+static __inline__ unsigned char pcbit_readb(struct pcbit_dev *dev)
+{
+ unsigned char val;
+
+ val = readb(dev->readptr++);
+ if (dev->readptr == dev->sh_mem + BANK2 + BANKLEN)
+ dev->readptr = dev->sh_mem + BANK2;
+
+ return val;
+}
+
+static __inline__ unsigned short pcbit_readw(struct pcbit_dev *dev)
+{
+ int dist;
+ unsigned short val;
+
+ dist = BANKLEN - ( dev->readptr - (dev->sh_mem + BANK2 ) );
+ switch (dist) {
+ case 2:
+ val = readw(dev->readptr);
+ dev->readptr = dev->sh_mem + BANK2;
+ break;
+ case 1:
+ val = readb(dev->readptr);
+ dev->readptr = dev->sh_mem + BANK2;
+ val = (readb(dev->readptr++) << 8) | val;
+ break;
+ default:
+ val = readw(dev->readptr);
+ dev->readptr += 2;
+ break;
+ };
+ return val;
+}
+
+static __inline__ void memcpy_frompcbit(struct pcbit_dev * dev, u_char * data, int len)
+{
+ int diff;
+
+ diff = len - (BANKLEN - (dev->readptr - (dev->sh_mem + BANK2) ) );
+ if (diff > 0)
+ {
+ memcpy_fromio(data, dev->readptr, len - diff);
+ memcpy_fromio(data + (len - diff), dev->sh_mem + BANK2 , diff);
+ dev->readptr = dev->sh_mem + BANK2 + diff;
+ }
+ else
+ {
+ memcpy_fromio(data, dev->readptr, len);
+ dev->readptr += len;
+ if (diff == 0)
+ dev->readptr = dev->sh_mem + BANK2;
+ }
+}
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c
new file mode 100644
index 000000000..2f2aa374f
--- /dev/null
+++ b/drivers/isdn/pcbit/module.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * PCBIT-D module support
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/tqueue.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include "pcbit.h"
+
+int mem[MAX_PCBIT_CARDS] = {0, };
+int irq[MAX_PCBIT_CARDS] = {0, };
+
+int num_boards;
+struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS] = {0, 0, 0, 0};
+
+int init_module(void);
+void cleanup_module(void);
+
+extern void pcbit_terminate(int board);
+extern int pcbit_init_dev(int board, int mem_base, int irq);
+
+#ifdef MODULE
+#define pcbit_init init_module
+#endif
+
+int pcbit_init(void)
+{
+ int board;
+
+ num_boards = 0;
+
+ printk(KERN_INFO
+ "PCBIT-D device driver v 0.5 - "
+ "Copyright (C) 1996 Universidade de Lisboa\n");
+
+ if (mem[0] || irq[0])
+ {
+ for (board=0; board < MAX_PCBIT_CARDS && mem[board] && irq[board]; board++)
+ {
+ if (!mem[board])
+ mem[board] = 0xD0000;
+ if (!irq[board])
+ irq[board] = 5;
+
+ if (pcbit_init_dev(board, mem[board], irq[board]) == 0)
+ num_boards++;
+
+ else
+ {
+ printk(KERN_WARNING
+ "pcbit_init failed for dev %d",
+ board + 1);
+ return -EIO;
+ }
+ }
+ }
+
+ /* Hardcoded default settings detection */
+
+ if (!num_boards)
+ {
+ printk(KERN_INFO
+ "Trying to detect board using default settings\n");
+ if (pcbit_init_dev(0, 0xD0000, 5) == 0)
+ num_boards++;
+ else
+ return -EIO;
+ }
+
+ /* No symbols to export, hide all symbols */
+ register_symtab(NULL);
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ int board;
+
+ for (board = 0; board < num_boards; board++)
+ pcbit_terminate(board);
+ printk(KERN_INFO
+ "PCBIT-D module unloaded\n");
+}
+
+#else
+void pcbit_setup(char *str, int *ints)
+{
+ int i, j, argc;
+
+ argc = ints[0];
+ i = 0;
+ j = 1;
+
+ while (argc && (i<MAX_PCBIT_CARDS)) {
+
+ if (argc) {
+ mem[i] = ints[j];
+ j++; argc--;
+ }
+
+ if (argc) {
+ irq[i] = ints[j];
+ j++; argc--;
+ }
+
+ i++;
+ }
+}
+#endif
+
+
+
diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h
new file mode 100644
index 000000000..89a608904
--- /dev/null
+++ b/drivers/isdn/pcbit/pcbit.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU Public License, incorporated herein by reference.
+ */
+
+/*
+ * PCBIT-D device driver definitions
+ */
+
+#ifndef PCBIT_H
+#define PCBIT_H
+
+#define MAX_PCBIT_CARDS 4
+
+
+#define BLOCK_TIMER
+
+#ifdef __KERNEL__
+
+struct pcbit_chan {
+ unsigned short id;
+ unsigned short callref; /* Call Reference */
+ unsigned char proto; /* layer2protocol */
+ unsigned char queued; /* unacked data messages */
+ unsigned char layer2link; /* used in TData */
+ unsigned char snum; /* used in TData */
+ unsigned short s_refnum;
+ unsigned short r_refnum;
+ unsigned short fsm_state;
+ struct timer_list fsm_timer;
+#ifdef BLOCK_TIMER
+ struct timer_list block_timer;
+#endif
+};
+
+struct msn_entry {
+ char *msn;
+ struct msn_entry * next;
+};
+
+struct pcbit_dev {
+ /* board */
+
+ volatile unsigned char* sh_mem; /* RDP address */
+ unsigned int irq;
+ unsigned int id;
+ unsigned int interrupt; /* set during interrupt
+ processing */
+
+ /* isdn4linux */
+
+ struct msn_entry * msn_list; /* ISDN address list */
+
+ isdn_if * dev_if;
+
+ ushort ll_hdrlen;
+ ushort hl_hdrlen;
+
+ /* link layer */
+ unsigned char l2_state;
+
+ struct frame_buf *read_queue;
+ struct frame_buf *read_frame;
+ struct frame_buf *write_queue;
+
+ /* Protocol start */
+ struct wait_queue *set_running_wq;
+ struct timer_list set_running_timer;
+
+ struct timer_list error_recover_timer;
+
+ struct tq_struct qdelivery;
+
+ u_char w_busy;
+ u_char r_busy;
+
+ volatile unsigned char *readptr;
+ volatile unsigned char *writeptr;
+
+ ushort loadptr;
+
+ unsigned short fsize[8]; /* sent layer2 frames size */
+
+ unsigned char send_seq;
+ unsigned char rcv_seq;
+ unsigned char unack_seq;
+
+ unsigned short free;
+
+ /* channels */
+
+ struct pcbit_chan *b1;
+ struct pcbit_chan *b2;
+};
+
+#define STATS_TIMER (10*HZ)
+#define ERRTIME (0.1*HZ)
+
+/* MRU */
+#define MAXBUFSIZE 1534
+#define MRU MAXBUFSIZE
+
+#define STATBUF_LEN 2048
+/*
+ *
+ */
+
+#endif /* __KERNEL__ */
+
+/* isdn_ctrl only allows a long sized argument */
+
+struct pcbit_ioctl {
+ union {
+ struct byte_op {
+ ushort addr;
+ ushort value;
+ } rdp_byte;
+ unsigned long l2_status;
+ } info;
+};
+
+
+
+#define PCBIT_IOCTL_GETSTAT 0x01 /* layer2 status */
+#define PCBIT_IOCTL_LWMODE 0x02 /* linear write mode */
+#define PCBIT_IOCTL_STRLOAD 0x03 /* start load mode */
+#define PCBIT_IOCTL_ENDLOAD 0x04 /* end load mode */
+#define PCBIT_IOCTL_SETBYTE 0x05 /* set byte */
+#define PCBIT_IOCTL_GETBYTE 0x06 /* get byte */
+#define PCBIT_IOCTL_RUNNING 0x07 /* set protocol running */
+#define PCBIT_IOCTL_WATCH188 0x08 /* set watch 188 */
+#define PCBIT_IOCTL_PING188 0x09 /* ping 188 */
+#define PCBIT_IOCTL_FWMODE 0x0A /* firmware write mode */
+#define PCBIT_IOCTL_STOP 0x0B /* stop protocol */
+#define PCBIT_IOCTL_APION 0x0C /* issue API_ON */
+
+#ifndef __KERNEL__
+
+#define PCBIT_GETSTAT (PCBIT_IOCTL_GETSTAT + IIOCDRVCTL)
+#define PCBIT_LWMODE (PCBIT_IOCTL_LWMODE + IIOCDRVCTL)
+#define PCBIT_STRLOAD (PCBIT_IOCTL_STRLOAD + IIOCDRVCTL)
+#define PCBIT_ENDLOAD (PCBIT_IOCTL_ENDLOAD + IIOCDRVCTL)
+#define PCBIT_SETBYTE (PCBIT_IOCTL_SETBYTE + IIOCDRVCTL)
+#define PCBIT_GETBYTE (PCBIT_IOCTL_GETBYTE + IIOCDRVCTL)
+#define PCBIT_RUNNING (PCBIT_IOCTL_RUNNING + IIOCDRVCTL)
+#define PCBIT_WATCH188 (PCBIT_IOCTL_WATCH188 + IIOCDRVCTL)
+#define PCBIT_PING188 (PCBIT_IOCTL_PING188 + IIOCDRVCTL)
+#define PCBIT_FWMODE (PCBIT_IOCTL_FWMODE + IIOCDRVCTL)
+#define PCBIT_STOP (PCBIT_IOCTL_STOP + IIOCDRVCTL)
+#define PCBIT_APION (PCBIT_IOCTL_APION + IIOCDRVCTL)
+
+#define MAXSUPERLINE 3000
+
+#endif
+
+#define L2_DOWN 0
+#define L2_LOADING 1
+#define L2_LWMODE 2
+#define L2_FWMODE 3
+#define L2_STARTING 4
+#define L2_RUNNING 5
+#define L2_ERROR 6
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/isdn/teles/Makefile b/drivers/isdn/teles/Makefile
new file mode 100644
index 000000000..a252f46ba
--- /dev/null
+++ b/drivers/isdn/teles/Makefile
@@ -0,0 +1,17 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := mod.o card.o config.o buffers.o tei.o isdnl2.o isdnl3.o \
+llglue.o q931.o callc.o fsm.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_TELES),y)
+ O_TARGET += teles.o
+else
+ ifeq ($(CONFIG_ISDN_DRV_TELES),m)
+ O_TARGET += teles.o
+ M_OBJS += teles.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/isdn/teles/buffers.c b/drivers/isdn/teles/buffers.c
new file mode 100644
index 000000000..b01a5ab06
--- /dev/null
+++ b/drivers/isdn/teles/buffers.c
@@ -0,0 +1,326 @@
+/* $Id: buffers.c,v 1.3 1996/05/31 00:56:53 fritz Exp $
+ *
+ * $Log: buffers.c,v $
+ * Revision 1.3 1996/05/31 00:56:53 fritz
+ * removed cli() from BufPoolAdd, since it is called
+ * with interrupts off anyway.
+ *
+ * Revision 1.2 1996/04/29 22:48:14 fritz
+ * Removed compatibility-macros. No longer needed.
+ *
+ * Revision 1.1 1996/04/13 10:19:28 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "teles.h"
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+
+void
+BufPoolInit(struct BufPool *bp, int order, int bpps,
+ int maxpages)
+{
+#ifdef DEBUG_MAGIC
+ generateerror
+ bp->magic = 010167;
+#endif
+
+#if 0
+ printk(KERN_DEBUG "BufPoolInit bp %x\n", bp);
+#endif
+
+ bp->freelist = NULL;
+ bp->pageslist = NULL;
+ bp->pageorder = order;
+ bp->pagescount = 0;
+ bp->bpps = bpps;
+ bp->bufsize = BUFFER_SIZE(order, bpps);
+ bp->maxpages = maxpages;
+}
+
+int
+BufPoolAdd(struct BufPool *bp, int priority)
+{
+ struct Pages *ptr;
+ byte *bptr;
+ int i;
+ struct BufHeader *bh = NULL, *prev, *first;
+
+#if 0
+ printk(KERN_DEBUG "BufPoolAdd bp %x\n", bp);
+#endif
+
+ ptr = (struct Pages *) __get_free_pages(priority, bp->pageorder, 0);
+ if (!ptr) {
+ printk(KERN_WARNING "BufPoolAdd couldn't get pages!\n");
+ return (-1);
+ }
+#if 0
+ printk(KERN_DEBUG "Order %d pages allocated at %x\n", bp->pageorder, ptr);
+#endif
+
+ ptr->next = bp->pageslist;
+ bp->pageslist = ptr;
+ bp->pagescount++;
+
+ bptr = (byte *) ptr + sizeof(struct Pages *);
+
+ i = bp->bpps;
+ first = (struct BufHeader *) bptr;
+ prev = NULL;
+ while (i--) {
+ bh = (struct BufHeader *) bptr;
+#ifdef DEBUG_MAGIC
+ bh->magic = 020167;
+#endif
+ bh->next = prev;
+ prev = bh;
+ bh->bp = bp;
+ bptr += PART_SIZE(bp->pageorder, bp->bpps);
+ }
+
+ first->next = bp->freelist;
+ bp->freelist = bh;
+ return (0);
+}
+
+void
+BufPoolFree(struct BufPool *bp)
+{
+ struct Pages *p;
+
+#if 0
+ printk(KERN_DEBUG "BufPoolFree bp %x\n", bp);
+#endif
+
+ while (bp->pagescount--) {
+ p = bp->pageslist->next;
+ free_pages((unsigned long) bp->pageslist, bp->pageorder);
+#if 0
+ printk(KERN_DEBUG "Free pages %x order %d\n", bp->pageslist, bp->pageorder);
+#endif
+ bp->pageslist = p;
+ }
+}
+
+int
+BufPoolGet(struct BufHeader **bh,
+ struct BufPool *bp, int priority, void *heldby, int where)
+{
+ long flags;
+ int i;
+
+#ifdef DEBUG_MAGIC
+ if (bp->magic != 010167) {
+ printk(KERN_DEBUG "BufPoolGet: not a BufHeader\n");
+ return (-1);
+ }
+#endif
+
+ save_flags(flags);
+ cli();
+ i = 0;
+ while (!0) {
+ if (bp->freelist) {
+ *bh = bp->freelist;
+ bp->freelist = bp->freelist->next;
+ (*bh)->heldby = heldby;
+ (*bh)->where = where;
+ restore_flags(flags);
+ return (0);
+ }
+ if ((i == 0) && (bp->pagescount < bp->maxpages)) {
+ if (BufPoolAdd(bp, priority)) {
+ restore_flags(flags);
+ return -1;
+ }
+ i++;
+ } else {
+ *bh = NULL;
+ restore_flags(flags);
+ return (-1);
+ }
+ }
+
+}
+
+void
+BufPoolRelease(struct BufHeader *bh)
+{
+ struct BufPool *bp;
+ long flags;
+
+#ifdef DEBUG_MAGIC
+ if (bh->magic != 020167) {
+ printk(KERN_DEBUG "BufPoolRelease: not a BufHeader\n");
+ printk(KERN_DEBUG "called from %x\n", return_address());
+ return;
+ }
+#endif
+
+ bp = bh->bp;
+
+#ifdef DEBUG_MAGIC
+ if (bp->magic != 010167) {
+ printk(KERN_DEBUG "BufPoolRelease: not a BufPool\n");
+ return;
+ }
+#endif
+
+ save_flags(flags);
+ cli();
+ bh->next = bp->freelist;
+ bp->freelist = bh;
+ restore_flags(flags);
+}
+
+void
+BufQueueLink(struct BufQueue *bq,
+ struct BufHeader *bh)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (!bq->head)
+ bq->head = bh;
+ if (bq->tail)
+ bq->tail->next = bh;
+ bq->tail = bh;
+ bh->next = NULL;
+ restore_flags(flags);
+}
+
+void
+BufQueueLinkFront(struct BufQueue *bq,
+ struct BufHeader *bh)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ bh->next = bq->head;
+ bq->head = bh;
+ if (!bq->tail)
+ bq->tail = bh;
+ restore_flags(flags);
+}
+
+int
+BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (bq->head) {
+ if (bq->tail == bq->head)
+ bq->tail = NULL;
+ *bh = bq->head;
+ bq->head = (*bh)->next;
+ restore_flags(flags);
+ return (0);
+ } else {
+ restore_flags(flags);
+ return (-1);
+ }
+}
+
+void
+BufQueueInit(struct BufQueue *bq)
+{
+#ifdef DEBUG_MAGIC
+ bq->magic = 030167;
+#endif
+ bq->head = NULL;
+ bq->tail = NULL;
+}
+
+void
+BufQueueRelease(struct BufQueue *bq)
+{
+ struct BufHeader *bh;
+
+ while (bq->head) {
+ BufQueueUnlink(&bh, bq);
+ BufPoolRelease(bh);
+ }
+}
+
+int
+BufQueueLength(struct BufQueue *bq)
+{
+ int i = 0;
+ struct BufHeader *bh;
+
+ bh = bq->head;
+ while (bh) {
+ i++;
+ bh = bh->next;
+ }
+ return (i);
+}
+
+void
+BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+ int releasetoo)
+{
+ long flags;
+ struct BufHeader *sp;
+
+ save_flags(flags);
+ cli();
+
+ while (!0) {
+ sp = q->head;
+ if (!sp)
+ break;
+ if ((sp->primitive == pr) && (sp->heldby == heldby)) {
+ q->head = sp->next;
+ if (q->tail == sp)
+ q->tail = NULL;
+ if (releasetoo)
+ BufPoolRelease(sp);
+ } else
+ break;
+ }
+
+ sp = q->head;
+ if (sp)
+ while (sp->next) {
+ if ((sp->next->primitive == pr) && (sp->next->heldby == heldby)) {
+ if (q->tail == sp->next)
+ q->tail = sp;
+ if (releasetoo)
+ BufPoolRelease(sp->next);
+ sp->next = sp->next->next;
+ } else
+ sp = sp->next;
+ }
+ restore_flags(flags);
+}
+
+void
+Sfree(byte * ptr)
+{
+#if 0
+ printk(KERN_DEBUG "Sfree %x\n", ptr);
+#endif
+ kfree(ptr);
+}
+
+byte *
+Smalloc(int size, int pr, char *why)
+{
+ byte *p;
+
+ p = (byte *) kmalloc(size, pr);
+#if 0
+ printk(KERN_DEBUG "Smalloc %s size %d res %x\n", why, size, p);
+#endif
+ return (p);
+}
diff --git a/drivers/isdn/teles/callc.c b/drivers/isdn/teles/callc.c
new file mode 100644
index 000000000..ead53cb36
--- /dev/null
+++ b/drivers/isdn/teles/callc.c
@@ -0,0 +1,1481 @@
+/* $Id: callc.c,v 1.13 1996/06/24 17:15:55 fritz Exp $
+ *
+ * $Log: callc.c,v $
+ * Revision 1.13 1996/06/24 17:15:55 fritz
+ * corrected return code of teles_writebuf()
+ *
+ * Revision 1.12 1996/06/12 16:15:33 fritz
+ * Extended user-configurable debugging flags.
+ *
+ * Revision 1.11 1996/06/07 12:32:20 fritz
+ * More changes to support suspend/resume.
+ *
+ * Revision 1.10 1996/06/06 21:24:21 fritz
+ * Started adding support for suspend/resume.
+ *
+ * Revision 1.9 1996/05/31 12:23:57 jdenoud
+ * Jan: added channel open check to teles_writebuf
+ *
+ * Revision 1.8 1996/05/31 01:00:38 fritz
+ * Changed return code of teles_writebuf, when out of memory.
+ *
+ * Revision 1.7 1996/05/17 03:40:37 fritz
+ * General cleanup.
+ *
+ * Revision 1.6 1996/05/10 22:42:07 fritz
+ * Added entry for EV_RELEASE_CNF in ST_OUT (if no D-Channel avail.)
+ *
+ * Revision 1.5 1996/05/06 10:16:15 fritz
+ * Added voice stuff.
+ *
+ * Revision 1.4 1996/04/30 22:04:05 isdn4dev
+ * improved callback Karsten Keil
+ *
+ * Revision 1.3 1996/04/30 10:04:19 fritz
+ * Started voice support.
+ * Added printk() to debug-switcher for easier
+ * synchronization between printk()'s and output
+ * of /dev/isdnctrl.
+ *
+ * Revision 1.2 1996/04/20 16:42:29 fritz
+ * Changed statemachine to allow reject of incoming calls.
+ *
+ * Revision 1.1 1996/04/13 10:20:59 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "teles.h"
+#include <asm/uaccess.h>
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+extern int drid;
+extern isdn_if iif;
+extern void teles_mod_dec_use_count(void);
+extern void teles_mod_inc_use_count(void);
+
+static int init_ds(int chan, int incoming);
+static void release_ds(int chan);
+static char *strcpyupto(char *dest, char *src, char upto);
+
+static struct Fsm callcfsm =
+{NULL, 0, 0}, lcfsm =
+{NULL, 0, 0};
+
+struct Channel *chanlist;
+static int chancount = 0;
+unsigned int debugflags = 0;
+
+#define TMR_DCHAN_EST 2000
+
+static void
+stat_debug(struct Channel *chanp, char *s)
+{
+ char tmp[100], tm[32];
+
+ jiftime(tm, jiffies);
+ sprintf(tmp, "%s Channel %d HL->LL %s\n", tm, chanp->chan, s);
+ teles_putstatus(tmp);
+}
+
+enum {
+ ST_NULL, /* 0 inactive */
+ ST_OUT, /* 1 outgoing, awaiting SETUP confirm */
+ ST_CLEAR, /* 2 call release, awaiting RELEASE confirm */
+ ST_OUT_W, /* 3 outgoing, awaiting d-channel establishment */
+ ST_REL_W, /* 4 awaiting d-channel release */
+ ST_IN_W, /* 5 incoming, awaiting d-channel establishment */
+ ST_IN, /* 6 incoming call received */
+ ST_IN_SETUP, /* 7 incoming, SETUP response sent */
+ ST_IN_DACT, /* 8 incoming connected, no b-channel prot. */
+ ST_OUT_ESTB, /* 10 outgoing connected, awaiting b-channel prot. estbl. */
+ ST_ACTIVE, /* 11 active, b channel prot. established */
+ ST_BC_HANGUP, /* 12 call clear. (initiator), awaiting b channel prot. rel. */
+ ST_PRO_W, /* 13 call clear. (initiator), DISCONNECT req. sent */
+ ST_ANT_W, /* 14 call clear. (receiver), awaiting DISCONNECT ind. */
+ ST_DISC_BC_HANGUP, /* d channel gone, wait for b channel deactivation */
+ ST_OUT_W_HANGUP, /* Outgoing waiting for D-Channel hangup received */
+ ST_D_ERR, /* d channel released while active */
+};
+
+#define STATE_COUNT (ST_D_ERR+1)
+
+static char *strState[] =
+{
+ "ST_NULL",
+ "ST_OUT",
+ "ST_CLEAR",
+ "ST_OUT_W",
+ "ST_REL_W",
+ "ST_IN_W",
+ "ST_IN",
+ "ST_IN_SETUP",
+ "ST_IN_DACT",
+ "ST_OUT_ESTB",
+ "ST_ACTIVE",
+ "ST_BC_HANGUP",
+ "ST_PRO_W",
+ "ST_ANT_W",
+ "ST_DISC_BC_HANGUP",
+ "ST_OUT_W_HANGUP",
+ "ST_D_ERR",
+};
+
+enum {
+ EV_DIAL, /* 0 */
+ EV_SETUP_CNF, /* 1 */
+ EV_ACCEPTB, /* 2 */
+ EV_DISCONNECT_CNF, /* 5 */
+ EV_DISCONNECT_IND, /* 6 */
+ EV_RELEASE_CNF, /* 7 */
+ EV_DLEST, /* 8 */
+ EV_DLRL, /* 9 */
+ EV_SETUP_IND, /* 10 */
+ EV_RELEASE_IND, /* 11 */
+ EV_ACCEPTD, /* 12 */
+ EV_SETUP_CMPL_IND, /* 13 */
+ EV_BC_EST, /* 14 */
+ EV_WRITEBUF, /* 15 */
+ EV_DATAIN, /* 16 */
+ EV_HANGUP, /* 17 */
+ EV_BC_REL, /* 18 */
+ EV_CINF, /* 19 */
+ EV_SUSPEND, /* 20 */
+ EV_RESUME, /* 21 */
+};
+
+#define EVENT_COUNT (EV_CINF+1)
+
+static char *strEvent[] =
+{
+ "EV_DIAL",
+ "EV_SETUP_CNF",
+ "EV_ACCEPTB",
+ "EV_DISCONNECT_CNF",
+ "EV_DISCONNECT_IND",
+ "EV_RELEASE_CNF",
+ "EV_DLEST",
+ "EV_DLRL",
+ "EV_SETUP_IND",
+ "EV_RELEASE_IND",
+ "EV_ACCEPTD",
+ "EV_SETUP_CMPL_IND",
+ "EV_BC_EST",
+ "EV_WRITEBUF",
+ "EV_DATAIN",
+ "EV_HANGUP",
+ "EV_BC_REL",
+ "EV_CINF",
+ "EV_SUSPEND",
+ "EV_RESUME",
+};
+
+enum {
+ ST_LC_NULL,
+ ST_LC_ACTIVATE_WAIT,
+ ST_LC_DELAY,
+ ST_LC_ESTABLISH_WAIT,
+ ST_LC_CONNECTED,
+ ST_LC_RELEASE_WAIT,
+};
+
+#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
+
+static char *strLcState[] =
+{
+ "ST_LC_NULL",
+ "ST_LC_ACTIVATE_WAIT",
+ "ST_LC_DELAY",
+ "ST_LC_ESTABLISH_WAIT",
+ "ST_LC_CONNECTED",
+ "ST_LC_RELEASE_WAIT",
+};
+
+enum {
+ EV_LC_ESTABLISH,
+ EV_LC_PH_ACTIVATE,
+ EV_LC_PH_DEACTIVATE,
+ EV_LC_DL_ESTABLISH,
+ EV_LC_TIMER,
+ EV_LC_DL_RELEASE,
+ EV_LC_RELEASE,
+};
+
+#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
+
+static char *strLcEvent[] =
+{
+ "EV_LC_ESTABLISH",
+ "EV_LC_PH_ACTIVATE",
+ "EV_LC_PH_DEACTIVATE",
+ "EV_LC_DL_ESTABLISH",
+ "EV_LC_TIMER",
+ "EV_LC_DL_RELEASE",
+ "EV_LC_RELEASE",
+};
+
+#define LC_D 0
+#define LC_B 1
+
+static int
+my_atoi(char *s)
+{
+ int i, n;
+
+ n = 0;
+ if (!s)
+ return -1;
+ for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
+ n = 10 * n + (*s - '0');
+ return n;
+}
+
+/*
+ * Dial out
+ */
+static void
+r1(struct FsmInst *fi, int event, void *arg)
+{
+ isdn_ctrl *ic = arg;
+ struct Channel *chanp = fi->userdata;
+ char *ptr;
+ char sis[3];
+
+ /* Destination Phone-Number */
+ ptr = strcpyupto(chanp->para.called, ic->num, ',');
+ /* Source Phone-Number */
+ ptr = strcpyupto(chanp->para.calling, ptr + 1, ',');
+ if (!strcmp(chanp->para.calling, "0"))
+ chanp->para.calling[0] = '\0';
+
+ /* Service-Indicator 1 */
+ ptr = strcpyupto(sis, ptr + 1, ',');
+ chanp->para.info = my_atoi(sis);
+
+ /* Service-Indicator 2 */
+ ptr = strcpyupto(sis, ptr + 1, '\0');
+ chanp->para.info2 = my_atoi(sis);
+
+ chanp->l2_active_protocol = chanp->l2_protocol;
+ chanp->incoming = 0;
+ chanp->lc_b.l2_start = !0;
+
+ switch (chanp->l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ chanp->lc_b.l2_establish = !0;
+ break;
+ case (ISDN_PROTO_L2_HDLC):
+ case (ISDN_PROTO_L2_TRANS):
+ chanp->lc_b.l2_establish = 0;
+ break;
+ default:
+ printk(KERN_WARNING "r1 unknown protocol\n");
+ break;
+ }
+
+ FsmChangeState(fi, ST_OUT_W);
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+ll_hangup(struct Channel *chanp, int bchantoo)
+{
+ isdn_ctrl ic;
+
+ if (bchantoo) {
+ if (chanp->debug & 1)
+ stat_debug(chanp, "STAT_BHUP");
+ ic.driver = drid;
+ ic.command = ISDN_STAT_BHUP;
+ ic.arg = chanp->chan;
+ iif.statcallb(&ic);
+ }
+ if (chanp->debug & 1)
+ stat_debug(chanp, "STAT_DHUP");
+ ic.driver = drid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ iif.statcallb(&ic);
+}
+
+static void
+r2(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+ FsmChangeState(fi, ST_CLEAR);
+ ll_hangup(chanp, 0);
+}
+
+
+static void
+r2_1(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+
+ FsmChangeState(fi, ST_OUT_W_HANGUP);
+}
+
+
+static void
+r2_2(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_REL_W);
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+ ll_hangup(chanp, 0);
+}
+
+
+static void
+r3(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+ FsmChangeState(fi, ST_REL_W);
+}
+
+
+static void
+r3_1(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
+
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+ FsmChangeState(fi, ST_REL_W);
+ ll_hangup(chanp, 0);
+}
+
+
+static void
+r4(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp=fi->userdata;
+
+ chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
+ FsmChangeState(fi, ST_NULL);
+}
+
+static void
+r5(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->para.callref = chanp->outcallref;
+
+ chanp->outcallref++;
+ if (chanp->outcallref == 128)
+ chanp->outcallref = 64;
+
+ chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
+
+ FsmChangeState(fi, ST_OUT);
+}
+
+static void
+r6(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_IN_W);
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r7(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+
+ /*
+ * Report incoming calls only once to linklevel, use octet 3 of
+ * channel identification information element. (it's value
+ * is copied to chanp->para.bchannel in l3s12(), file isdnl3.c)
+ */
+ if (((chanp->chan & 1) + 1) & chanp->para.bchannel) {
+ chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
+ FsmChangeState(fi, ST_IN);
+ if (chanp->debug & 1)
+ stat_debug(chanp, "STAT_ICALL");
+ ic.driver = drid;
+ ic.command = ISDN_STAT_ICALL;
+ ic.arg = chanp->chan;
+ /*
+ * No need to return "unknown" for calls without OAD,
+ * cause that's handled in linklevel now (replaced by '0')
+ */
+ sprintf(ic.num, "%s,%d,0,%s", chanp->para.calling, chanp->para.info,
+ chanp->para.called);
+ iif.statcallb(&ic);
+ } else {
+ chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+ FsmChangeState(fi, ST_REL_W);
+ }
+}
+
+static void
+r8(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_IN_SETUP);
+ chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+
+}
+
+static void
+r9(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_IN_DACT);
+
+ chanp->l2_active_protocol = chanp->l2_protocol;
+ chanp->incoming = !0;
+ chanp->lc_b.l2_start = 0;
+
+ switch (chanp->l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ chanp->lc_b.l2_establish = !0;
+ break;
+ case (ISDN_PROTO_L2_HDLC):
+ case (ISDN_PROTO_L2_TRANS):
+ chanp->lc_b.l2_establish = 0;
+ break;
+ default:
+ printk(KERN_WARNING "r9 unknown protocol\n");
+ break;
+ }
+
+ init_ds(chanp->chan, !0);
+
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r10(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_OUT_ESTB);
+
+ init_ds(chanp->chan, 0);
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+
+}
+
+static void
+r12(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+
+ FsmChangeState(fi, ST_ACTIVE);
+ chanp->data_open = !0;
+
+ if (chanp->debug & 1)
+ stat_debug(chanp, "STAT_DCONN");
+ ic.driver = drid;
+ ic.command = ISDN_STAT_DCONN;
+ ic.arg = chanp->chan;
+ iif.statcallb(&ic);
+
+ if (chanp->debug & 1)
+ stat_debug(chanp, "STAT_BCONN");
+ ic.driver = drid;
+ ic.command = ISDN_STAT_BCONN;
+ ic.arg = chanp->chan;
+ iif.statcallb(&ic);
+}
+
+static void
+r15(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ FsmChangeState(fi, ST_BC_HANGUP);
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r16(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_ds(chanp->chan);
+
+ FsmChangeState(fi, ST_PRO_W);
+ chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r17(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ release_ds(chanp->chan);
+
+ FsmChangeState(fi, ST_ANT_W);
+}
+
+
+static void
+r17_1(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ release_ds(chanp->chan);
+
+ chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
+
+ FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL);
+
+ FsmChangeState(fi, ST_NULL);
+
+ ll_hangup(chanp,!0);
+}
+
+static void
+r18(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_REL_W);
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+
+ ll_hangup(chanp, !0);
+}
+
+static void
+r19(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_CLEAR);
+
+ chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+ ll_hangup(chanp, !0);
+}
+
+static void
+r20(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
+
+ FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL);
+
+ FsmChangeState(fi, ST_NULL);
+
+ ll_hangup(chanp, 0);
+}
+
+
+static void
+r21(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ FsmChangeState(fi, ST_DISC_BC_HANGUP);
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r22(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_ds(chanp->chan);
+
+ FsmChangeState(fi, ST_CLEAR);
+
+ chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+ ll_hangup(chanp, !0);
+}
+
+static void
+r23(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_ds(chanp->chan);
+
+ FsmChangeState(fi, ST_PRO_W);
+ chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r23_1(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_ds(chanp->chan);
+
+ chanp->is.l4.l4l3(&chanp->is, CC_DLRL,NULL);
+
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE,NULL);
+
+ FsmChangeState(fi, ST_NULL);
+
+ ll_hangup(chanp,!0);
+}
+
+static void
+r24(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ FsmChangeState(fi, ST_D_ERR);
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r25(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_ds(chanp->chan);
+
+ FsmChangeState(fi, ST_NULL);
+
+ ll_hangup(chanp, !0);
+}
+
+static void
+r26(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+
+
+ ic.driver = drid;
+ ic.command = ISDN_STAT_CINF;
+ ic.arg = chanp->chan;
+ sprintf(ic.num, "%d", chanp->para.chargeinfo);
+ iif.statcallb(&ic);
+}
+
+
+
+static struct FsmNode fnlist[] =
+{
+ {ST_NULL, EV_DIAL, r1},
+ {ST_OUT_W, EV_DLEST, r5},
+ {ST_OUT_W, EV_DLRL, r20},
+ {ST_OUT_W, EV_RELEASE_CNF, r2_2 },
+ {ST_OUT, EV_DISCONNECT_IND, r2},
+ {ST_OUT, EV_SETUP_CNF, r10},
+ {ST_OUT, EV_HANGUP, r2_1},
+ {ST_OUT, EV_RELEASE_IND, r20},
+ {ST_OUT, EV_RELEASE_CNF, r20},
+ {ST_OUT, EV_DLRL, r2_2},
+ {ST_OUT_W_HANGUP, EV_RELEASE_IND, r2_2},
+ {ST_OUT_W_HANGUP, EV_DLRL, r20},
+ {ST_CLEAR, EV_RELEASE_CNF, r3},
+ {ST_CLEAR, EV_DLRL, r20},
+ {ST_REL_W, EV_DLRL, r4},
+ {ST_NULL, EV_SETUP_IND, r6},
+ {ST_IN_W, EV_DLEST, r7},
+ {ST_IN_W, EV_DLRL, r3_1},
+ {ST_IN, EV_DLRL, r3_1},
+ {ST_IN, EV_HANGUP, r2_1},
+ {ST_IN, EV_RELEASE_IND, r2_2},
+ {ST_IN, EV_RELEASE_CNF, r2_2},
+ {ST_IN, EV_ACCEPTD, r8},
+ {ST_IN_SETUP, EV_HANGUP, r2_1},
+ {ST_IN_SETUP, EV_SETUP_CMPL_IND, r9},
+ {ST_IN_SETUP, EV_RELEASE_IND, r2_2},
+ {ST_IN_SETUP, EV_DISCONNECT_IND, r2},
+ {ST_IN_SETUP, EV_DLRL, r20},
+ {ST_OUT_ESTB, EV_BC_EST, r12},
+ {ST_OUT_ESTB, EV_BC_REL, r23},
+ {ST_OUT_ESTB, EV_DLRL, r23_1},
+ {ST_IN_DACT, EV_BC_EST, r12},
+ {ST_IN_DACT, EV_BC_REL, r17},
+ {ST_IN_DACT, EV_DLRL, r17_1},
+ {ST_ACTIVE, EV_HANGUP, r15},
+ {ST_ACTIVE, EV_BC_REL, r17},
+ {ST_ACTIVE, EV_DISCONNECT_IND, r21},
+ {ST_ACTIVE, EV_DLRL, r24},
+ {ST_ACTIVE, EV_CINF, r26},
+ {ST_ACTIVE, EV_RELEASE_IND, r17},
+ {ST_BC_HANGUP, EV_BC_REL, r16},
+ {ST_BC_HANGUP, EV_DISCONNECT_IND, r21},
+ {ST_PRO_W, EV_RELEASE_IND, r18},
+ {ST_ANT_W, EV_DISCONNECT_IND, r19},
+ {ST_DISC_BC_HANGUP, EV_BC_REL, r22},
+ {ST_D_ERR, EV_BC_REL, r25},
+};
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+static void
+lc_r1(struct FsmInst *fi, int event, void *arg)
+{
+ struct LcFsm *lf = fi->userdata;
+
+ FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
+ FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50);
+ lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL);
+
+}
+
+static void
+lc_r6(struct FsmInst *fi, int event, void *arg)
+{
+ struct LcFsm *lf = fi->userdata;
+
+ FsmDelTimer(&lf->act_timer, 50);
+ FsmChangeState(fi, ST_LC_DELAY);
+ FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51);
+}
+
+static void
+lc_r2(struct FsmInst *fi, int event, void *arg)
+{
+ struct LcFsm *lf = fi->userdata;
+
+ if (lf->l2_establish) {
+ FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
+ if (lf->l2_start)
+ lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
+ } else {
+ FsmChangeState(fi, ST_LC_CONNECTED);
+ lf->lccall(lf, LC_ESTABLISH, NULL);
+ }
+}
+
+static void
+lc_r3(struct FsmInst *fi, int event, void *arg)
+{
+ struct LcFsm *lf = fi->userdata;
+
+ FsmChangeState(fi, ST_LC_CONNECTED);
+ lf->lccall(lf, LC_ESTABLISH, NULL);
+}
+
+static void
+lc_r4(struct FsmInst *fi, int event, void *arg)
+{
+ struct LcFsm *lf = fi->userdata;
+
+ if (lf->l2_establish) {
+ FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+ lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
+ } else {
+ FsmChangeState(fi, ST_LC_NULL);
+ lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+ lf->lccall(lf, LC_RELEASE, NULL);
+ }
+}
+
+static void
+lc_r5(struct FsmInst *fi, int event, void *arg)
+{
+ struct LcFsm *lf = fi->userdata;
+
+ FsmChangeState(fi, ST_LC_NULL);
+ lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+ lf->lccall(lf, LC_RELEASE, NULL);
+}
+
+static struct FsmNode LcFnList[] =
+{
+ {ST_LC_NULL, EV_LC_ESTABLISH, lc_r1},
+ {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_r6},
+ {ST_LC_DELAY, EV_LC_TIMER, lc_r2},
+ {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3},
+ {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4},
+ {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5},
+ {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5},
+ {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5},
+ {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5},
+};
+
+#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
+
+void
+CallcNew(void)
+{
+ callcfsm.state_count = STATE_COUNT;
+ callcfsm.event_count = EVENT_COUNT;
+ callcfsm.strEvent = strEvent;
+ callcfsm.strState = strState;
+ FsmNew(&callcfsm, fnlist, FNCOUNT);
+
+ lcfsm.state_count = LC_STATE_COUNT;
+ lcfsm.event_count = LC_EVENT_COUNT;
+ lcfsm.strEvent = strLcEvent;
+ lcfsm.strState = strLcState;
+ FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
+}
+
+void
+CallcFree(void)
+{
+ FsmFree(&lcfsm);
+ FsmFree(&callcfsm);
+}
+
+static void
+release_ds(int chan)
+{
+ struct PStack *st = &chanlist[chan].ds;
+ struct IsdnCardState *sp;
+ struct HscxState *hsp;
+
+ sp = st->l1.hardware;
+ hsp = sp->hs + chanlist[chan].hscx;
+
+ close_hscxstate(hsp);
+
+ switch (chanlist[chan].l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ releasestack_isdnl2(st);
+ break;
+ case (ISDN_PROTO_L2_HDLC):
+ case (ISDN_PROTO_L2_TRANS):
+ releasestack_transl2(st);
+ break;
+ }
+}
+
+static void
+cc_l1man(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+ switch (pr) {
+ case (PH_ACTIVATE):
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
+ break;
+ case (PH_DEACTIVATE):
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+ break;
+ }
+}
+
+static void
+cc_l2man(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+ switch (pr) {
+ case (DL_ESTABLISH):
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
+ break;
+ case (DL_RELEASE):
+ FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL);
+ break;
+ }
+}
+
+static void
+dcc_l1man(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+ switch (pr) {
+ case (PH_ACTIVATE):
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
+ break;
+ case (PH_DEACTIVATE):
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+ break;
+ }
+}
+
+static void
+dcc_l2man(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+ switch (pr) {
+ case (DL_ESTABLISH):
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
+ break;
+ case (DL_RELEASE):
+ FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+ break;
+ }
+}
+
+static void
+ll_handler(struct PStack *st, int pr,
+ struct BufHeader *ibh)
+{
+ struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+ switch (pr) {
+ case (CC_DISCONNECT_IND):
+ FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+ break;
+ case (CC_RELEASE_CNF):
+ FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+ break;
+ case (CC_SETUP_IND):
+ FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+ break;
+ case (CC_RELEASE_IND):
+ FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
+ break;
+ case (CC_SETUP_COMPLETE_IND):
+ FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+ break;
+ case (CC_SETUP_CNF):
+ FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+ break;
+ case (CC_INFO_CHARGE):
+ FsmEvent(&chanp->fi, EV_CINF, NULL);
+ break;
+ }
+}
+
+static void
+init_is(int chan, unsigned int ces)
+{
+ struct PStack *st = &(chanlist[chan].is);
+ struct IsdnCardState *sp = chanlist[chan].sp;
+ char tmp[128];
+
+ setstack_teles(st, sp);
+
+ st->l2.sap = 0;
+
+ st->l2.tei = 255;
+
+ st->l2.ces = ces;
+ st->l2.extended = !0;
+ st->l2.laptype = LAPD;
+ st->l2.window = 1;
+ st->l2.orig = !0;
+ st->l2.t200 = 1000; /* 1000 milliseconds */
+ if (st->protocol == ISDN_PTYPE_1TR6) {
+ st->l2.n200 = 3; /* try 3 times */
+ st->l2.t203 = 10000; /* 10000 milliseconds */
+ } else {
+ st->l2.n200 = 4; /* try 4 times */
+ st->l2.t203 = 5000; /* 5000 milliseconds */
+ }
+
+ sprintf(tmp, "Channel %d q.921", chan);
+ setstack_isdnl2(st, tmp);
+ setstack_isdnl3(st);
+ st->l2.debug = 2;
+ st->l3.debug = 2;
+ st->l2.debug = 0xff;
+ st->l3.debug = 0xff;
+ st->l4.userdata = chanlist + chan;
+ st->l4.l2writewakeup = NULL;
+
+ st->l3.l3l4 = ll_handler;
+ st->l1.l1man = cc_l1man;
+ st->l2.l2man = cc_l2man;
+
+ st->pa = &chanlist[chan].para;
+ teles_addlist(sp, st);
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *s)
+{
+ char str[80], tm[32];
+ struct Channel *chanp = fi->userdata;
+
+ jiftime(tm, jiffies);
+ sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
+ teles_putstatus(str);
+}
+
+static void
+lc_debug(struct FsmInst *fi, char *s)
+{
+ char str[256], tm[32];
+ struct LcFsm *lf = fi->userdata;
+
+ jiftime(tm, jiffies);
+ sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s);
+ teles_putstatus(str);
+}
+
+static void
+dlc_debug(struct FsmInst *fi, char *s)
+{
+ char str[256], tm[32];
+ struct LcFsm *lf = fi->userdata;
+
+ jiftime(tm, jiffies);
+ sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s);
+ teles_putstatus(str);
+}
+
+static void
+lccall_d(struct LcFsm *lf, int pr, void *arg)
+{
+ struct Channel *chanp = lf->ch;
+
+ switch (pr) {
+ case (LC_ESTABLISH):
+ FsmEvent(&chanp->fi, EV_DLEST, NULL);
+ break;
+ case (LC_RELEASE):
+ FsmEvent(&chanp->fi, EV_DLRL, NULL);
+ break;
+ }
+}
+
+static void
+lccall_b(struct LcFsm *lf, int pr, void *arg)
+{
+ struct Channel *chanp = lf->ch;
+
+ switch (pr) {
+ case (LC_ESTABLISH):
+ FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+ break;
+ case (LC_RELEASE):
+ FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+ break;
+ }
+}
+
+static void
+init_chan(int chan, int cardnr, int hscx,
+ unsigned int ces)
+{
+ struct IsdnCard *card = cards + cardnr;
+ struct Channel *chanp = chanlist + chan;
+
+ chanp->sp = card->sp;
+ chanp->hscx = hscx;
+ chanp->chan = chan;
+ chanp->incoming = 0;
+ chanp->debug = 0;
+ init_is(chan, ces);
+
+ chanp->fi.fsm = &callcfsm;
+ chanp->fi.state = ST_NULL;
+ chanp->fi.debug = 0;
+ chanp->fi.userdata = chanp;
+ chanp->fi.printdebug = callc_debug;
+
+ chanp->lc_d.lcfi.fsm = &lcfsm;
+ chanp->lc_d.lcfi.state = ST_LC_NULL;
+ chanp->lc_d.lcfi.debug = 0;
+ chanp->lc_d.lcfi.userdata = &chanp->lc_d;
+ chanp->lc_d.lcfi.printdebug = lc_debug;
+ chanp->lc_d.type = LC_D;
+ chanp->lc_d.ch = chanp;
+ chanp->lc_d.st = &chanp->is;
+ chanp->lc_d.l2_establish = !0;
+ chanp->lc_d.l2_start = !0;
+ chanp->lc_d.lccall = lccall_d;
+ FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
+
+ chanp->lc_b.lcfi.fsm = &lcfsm;
+ chanp->lc_b.lcfi.state = ST_LC_NULL;
+ chanp->lc_b.lcfi.debug = 0;
+ chanp->lc_b.lcfi.userdata = &chanp->lc_b;
+ chanp->lc_b.lcfi.printdebug = dlc_debug;
+ chanp->lc_b.type = LC_B;
+ chanp->lc_b.ch = chanp;
+ chanp->lc_b.st = &chanp->ds;
+ chanp->lc_b.l2_establish = !0;
+ chanp->lc_b.l2_start = !0;
+ chanp->lc_b.lccall = lccall_b;
+ FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
+
+ chanp->outcallref = 64;
+ chanp->data_open = 0;
+}
+
+int
+CallcNewChan(void)
+{
+ int i, ces, c;
+
+ chancount = 0;
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].sp)
+ chancount += 2;
+
+ chanlist = (struct Channel *) Smalloc(sizeof(struct Channel) *
+ chancount, GFP_KERNEL, "chanlist");
+
+ c = 0;
+ ces = randomces();
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].sp) {
+ init_chan(c++, i, 1, ces++);
+ ces %= 0xffff;
+ init_chan(c++, i, 0, ces++);
+ ces %= 0xffff;
+ }
+ printk(KERN_INFO "channels %d\n", chancount);
+ return (chancount);
+
+}
+
+static void
+release_is(int chan)
+{
+ struct PStack *st = &chanlist[chan].is;
+
+ releasestack_isdnl2(st);
+ teles_rmlist(st->l1.hardware, st);
+ BufQueueRelease(&st->l2.i_queue);
+}
+
+void
+CallcFreeChan(void)
+{
+ int i;
+
+ for (i = 0; i < chancount; i++)
+ release_is(i);
+ Sfree((void *) chanlist);
+}
+
+static void
+lldata_handler(struct PStack *st, int pr,
+ void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->l4.userdata;
+ byte *ptr;
+ int size;
+ struct BufHeader *ibh = arg;
+
+ switch (pr) {
+ case (DL_DATA):
+ if (chanp->data_open) {
+ ptr = DATAPTR(ibh);
+ ptr += chanp->ds.l2.ihsize;
+ size = ibh->datasize - chanp->ds.l2.ihsize;
+ iif.rcvcallb(drid, chanp->chan, ptr, size);
+ }
+ BufPoolRelease(ibh);
+ break;
+ default:
+ printk(KERN_WARNING "lldata_handler unknown primitive\n");
+ break;
+ }
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr,
+ struct BufHeader *ibh)
+{
+ struct Channel *chanp = (struct Channel *) st->l4.userdata;
+ byte *ptr;
+
+ switch (pr) {
+ case (PH_DATA):
+ if (chanp->data_open) {
+ ptr = DATAPTR(ibh);
+ iif.rcvcallb(drid, chanp->chan, ptr, ibh->datasize);
+ }
+ BufPoolRelease(ibh);
+ break;
+ default:
+ printk(KERN_WARNING "lltrans_handler unknown primitive\n");
+ break;
+ }
+}
+
+static void
+ll_writewakeup(struct PStack *st)
+{
+ struct Channel *chanp = st->l4.userdata;
+ isdn_ctrl ic;
+
+ ic.driver = drid;
+ ic.command = ISDN_STAT_BSENT;
+ ic.arg = chanp->chan;
+ iif.statcallb(&ic);
+}
+
+static int
+init_ds(int chan, int incoming)
+{
+ struct PStack *st = &(chanlist[chan].ds);
+ struct IsdnCardState *sp = (struct IsdnCardState *)
+ chanlist[chan].is.l1.hardware;
+ struct HscxState *hsp = sp->hs + chanlist[chan].hscx;
+ char tmp[128];
+
+ st->l1.hardware = sp;
+
+ hsp->mode = 2;
+ hsp->transbufsize = 4000;
+
+ if (setstack_hscx(st, hsp))
+ return (-1);
+
+ st->l2.extended = 0;
+ st->l2.laptype = LAPB;
+ st->l2.orig = !incoming;
+ st->l2.t200 = 1000; /* 1000 milliseconds */
+ st->l2.window = 3;
+ st->l2.n200 = 4; /* try 4 times */
+ st->l2.t203 = 5000; /* 5000 milliseconds */
+
+ st->l2.debug = 0xff;
+ st->l3.debug = 0xff;
+ switch (chanlist[chan].l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ sprintf(tmp, "Channel %d x.75", chan);
+ setstack_isdnl2(st, tmp);
+ st->l2.l2l3 = lldata_handler;
+ st->l1.l1man = dcc_l1man;
+ st->l2.l2man = dcc_l2man;
+ st->l4.userdata = chanlist + chan;
+ st->l4.l1writewakeup = NULL;
+ st->l4.l2writewakeup = ll_writewakeup;
+ st->l2.l2m.debug = debugflags & 16;
+ st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+ st->l1.hscxmode = 2; /* Packet-Mode ? */
+ st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+ break;
+ case (ISDN_PROTO_L2_HDLC):
+ st->l1.l1l2 = lltrans_handler;
+ st->l1.l1man = dcc_l1man;
+ st->l4.userdata = chanlist + chan;
+ st->l4.l1writewakeup = ll_writewakeup;
+ st->l1.hscxmode = 2;
+ st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+ break;
+ case (ISDN_PROTO_L2_TRANS):
+ st->l1.l1l2 = lltrans_handler;
+ st->l1.l1man = dcc_l1man;
+ st->l4.userdata = chanlist + chan;
+ st->l4.l1writewakeup = ll_writewakeup;
+ st->l1.hscxmode = 1;
+ st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+ break;
+ }
+
+ return (0);
+
+}
+
+static void
+channel_report(int i)
+{
+}
+
+static void
+command_debug(struct Channel *chanp, char *s)
+{
+ char tmp[64], tm[32];
+
+ jiftime(tm, jiffies);
+ sprintf(tmp, "%s Channel %d LL->HL %s\n", tm, chanp->chan, s);
+ teles_putstatus(tmp);
+}
+
+static void
+distr_debug(void)
+{
+ int i;
+
+ for (i = 0; i < chancount; i++) {
+ chanlist[i].debug = debugflags & 1;
+ chanlist[i].fi.debug = debugflags & 2;
+ chanlist[i].is.l2.l2m.debug = debugflags & 8;
+ chanlist[i].ds.l2.l2m.debug = debugflags & 16;
+ }
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].sp) {
+ cards[i].sp->dlogflag = debugflags & 4;
+ cards[i].sp->debug = debugflags & 32;
+ }
+}
+
+int
+teles_command(isdn_ctrl * ic)
+{
+ struct Channel *chanp;
+ char tmp[64];
+ int i;
+ unsigned int num;
+
+ switch (ic->command) {
+ case (ISDN_CMD_SETEAZ):
+ chanp = chanlist + ic->arg;
+ if (chanp->debug & 1)
+ command_debug(chanp, "SETEAZ");
+ return (0);
+ case (ISDN_CMD_DIAL):
+ chanp = chanlist + (ic->arg & 0xff);
+ if (chanp->debug & 1) {
+ sprintf(tmp, "DIAL %s", ic->num);
+ command_debug(chanp, tmp);
+ }
+ FsmEvent(&chanp->fi, EV_DIAL, ic);
+ return (0);
+ case (ISDN_CMD_ACCEPTB):
+ chanp = chanlist + ic->arg;
+ if (chanp->debug & 1)
+ command_debug(chanp, "ACCEPTB");
+ FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+ break;
+ case (ISDN_CMD_ACCEPTD):
+ chanp = chanlist + ic->arg;
+ if (chanp->debug & 1)
+ command_debug(chanp, "ACCEPTD");
+ FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+ break;
+ case (ISDN_CMD_HANGUP):
+ chanp = chanlist + ic->arg;
+ if (chanp->debug & 1)
+ command_debug(chanp, "HANGUP");
+ FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+ break;
+ case (ISDN_CMD_SUSPEND):
+ chanp = chanlist + ic->arg;
+ if (chanp->debug & 1) {
+ sprintf(tmp, "SUSPEND %s", ic->num);
+ command_debug(chanp, tmp);
+ }
+ FsmEvent(&chanp->fi, EV_SUSPEND, ic);
+ break;
+ case (ISDN_CMD_RESUME):
+ chanp = chanlist + ic->arg;
+ if (chanp->debug & 1) {
+ sprintf(tmp, "RESUME %s", ic->num);
+ command_debug(chanp, tmp);
+ }
+ FsmEvent(&chanp->fi, EV_RESUME, ic);
+ break;
+ case (ISDN_CMD_LOCK):
+ teles_mod_inc_use_count();
+ break;
+ case (ISDN_CMD_UNLOCK):
+ teles_mod_dec_use_count();
+ break;
+ case (ISDN_CMD_IOCTL):
+ switch (ic->arg) {
+ case (0):
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].sp)
+ teles_reportcard(i);
+ for (i = 0; i < chancount; i++)
+ channel_report(i);
+ break;
+ case (1):
+ debugflags = *(unsigned int *) ic->num;
+ distr_debug();
+ sprintf(tmp, "debugging flags set to %x\n", debugflags);
+ teles_putstatus(tmp);
+ printk(KERN_DEBUG "%s", tmp);
+ break;
+ case (2):
+ num = *(unsigned int *) ic->num;
+ i = num >> 8;
+ if (i >= chancount)
+ break;
+ chanp = chanlist + i;
+ chanp->impair = num & 0xff;
+ if (chanp->debug & 1) {
+ sprintf(tmp, "IMPAIR %x", chanp->impair);
+ command_debug(chanp, tmp);
+ }
+ break;
+ }
+ break;
+ case (ISDN_CMD_SETL2):
+ chanp = chanlist + (ic->arg & 0xff);
+ if (chanp->debug & 1) {
+ sprintf(tmp, "SETL2 %ld", ic->arg >> 8);
+ command_debug(chanp, tmp);
+ }
+ chanp->l2_protocol = ic->arg >> 8;
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+int
+teles_writebuf(int id, int chan, const u_char * buf, int count, int user)
+{
+ struct Channel *chanp = chanlist + chan;
+ struct PStack *st = &chanp->ds;
+ struct BufHeader *ibh;
+ int err, i;
+ byte *ptr;
+
+ if (!chanp->data_open) {
+ printk(KERN_DEBUG "teles_writebuf: channel not open\n");
+ return -EIO;
+ }
+
+ err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21);
+ if (err)
+ /* Must return 0 here, since this is not an error
+ * but a temporary lack of resources.
+ */
+ return 0;
+
+ ptr = DATAPTR(ibh);
+ if (chanp->lc_b.l2_establish)
+ i = st->l2.ihsize;
+ else
+ i = 0;
+
+ if ((count+i) > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) {
+ printk(KERN_WARNING "teles_writebuf: packet too large!\n");
+ return (-EINVAL);
+ }
+
+ ptr += i;
+
+ if (user) {
+ if (copy_from_user(ptr, buf, count))
+ return -EFAULT;
+ } else
+ memcpy(ptr, buf, count);
+ ibh->datasize = count + i;
+
+ if (chanp->data_open) {
+ if (chanp->lc_b.l2_establish)
+ chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, ibh);
+ else
+ chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, ibh);
+ return (count);
+ } else {
+ BufPoolRelease(ibh);
+ return (0);
+ }
+
+}
+
+static char *
+strcpyupto(char *dest, char *src, char upto)
+{
+ while (*src && (*src != upto) && (*src != '\0'))
+ *dest++ = *src++;
+ *dest = '\0';
+ return (src);
+}
diff --git a/drivers/isdn/teles/card.c b/drivers/isdn/teles/card.c
new file mode 100644
index 000000000..c0595e862
--- /dev/null
+++ b/drivers/isdn/teles/card.c
@@ -0,0 +1,1892 @@
+/* $Id: card.c,v 1.13 1996/07/18 11:21:24 jdenoud Exp $
+ *
+ * card.c low level stuff for the Teles S0 isdn card
+ *
+ * Author Jan den Ouden
+ *
+ * Beat Doebeli log all D channel traffic
+ *
+ * $Log: card.c,v $
+ * Revision 1.13 1996/07/18 11:21:24 jdenoud
+ * Use small buffers for incoming audio data
+ *
+ * Revision 1.12 1996/06/24 17:16:52 fritz
+ * Added check for misconfigured membase.
+ *
+ * Revision 1.11 1996/06/14 03:30:37 fritz
+ * Added recovery from EXIR 40 interrupt.
+ * Some cleanup.
+ *
+ * Revision 1.10 1996/06/11 14:57:20 hipp
+ * minor changes to ensure, that SKBs are sent in the right order
+ *
+ * Revision 1.9 1996/06/06 14:42:09 fritz
+ * Bugfix: forgot hsp-> in last change.
+ *
+ * Revision 1.7 1996/05/31 01:02:21 fritz
+ * Cosmetic changes.
+ *
+ * Revision 1.6 1996/05/26 14:58:10 fritz
+ * Bugfix: Did not show port correctly, when no card found.
+ *
+ * Revision 1.5 1996/05/17 03:45:02 fritz
+ * Made error messages more clearly.
+ * Bugfix: Only 31 bytes of 32-byte audio frames
+ * have been transfered to upper layers.
+ *
+ * Revision 1.4 1996/05/06 10:17:57 fritz
+ * Added voice-send stuff
+ * (Not reporting EXIR when in voice-mode, since it's normal).
+ *
+ * Revision 1.3 1996/04/30 22:02:40 isdn4dev
+ * Bugfixes for 16.3
+ * -improved IO allocation
+ * -fix second B channel problem
+ * -correct ph_command patch
+ *
+ * Revision 1.2 1996/04/30 10:00:59 fritz
+ * Bugfix: Added ph_command(8) for 16.3.
+ * Bugfix: Ports did not get registered correctly
+ * when using a 16.3.
+ * Started voice support.
+ * Some experimental changes of waitforXFW().
+ *
+ * Revision 1.1 1996/04/13 10:22:42 fritz
+ * Initial revision
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "teles.h"
+
+#define INCLUDE_INLINE_FUNCS
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+
+#undef DCHAN_VERBOSE
+
+extern void tei_handler(struct PStack *st, byte pr,
+ struct BufHeader *ibh);
+extern struct IsdnCard cards[];
+extern int nrcards;
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline byte
+readisac_0(unsigned int cardm, byte offset)
+{
+ return readb(cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
+}
+
+static inline byte
+readisac_3(int iobase, byte offset)
+{
+ return (bytein(iobase - 0x420 + offset));
+}
+
+#define READISAC(mbase,ibase,ofs) \
+ ((mbase)?readisac_0(mbase,ofs):readisac_3(ibase,ofs))
+
+static inline void
+writeisac_0(unsigned int cardm, byte offset, byte value)
+{
+ writeb(value, cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
+}
+
+static inline void
+writeisac_3(int iobase, byte offset, byte value)
+{
+ byteout(iobase - 0x420 + offset, value);
+}
+
+#define WRITEISAC(mbase,ibase,ofs,val) \
+ ((mbase)?writeisac_0(mbase,ofs,val):writeisac_3(ibase,ofs,val))
+
+static inline void
+readisac_s(int iobase, byte offset, byte * dest, int count)
+{
+ insb(iobase - 0x420 + offset, dest, count);
+}
+
+static inline void
+writeisac_s(int iobase, byte offset, byte * src, int count)
+{
+ outsb(iobase - 0x420 + offset, src, count);
+}
+
+static inline byte
+readhscx_0(unsigned int base, byte hscx, byte offset)
+{
+ return readb(base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
+ ((hscx & 1) ? 0x40 : 0) + offset);
+}
+
+static inline byte
+readhscx_3(int iobase, byte hscx, byte offset)
+{
+ return (bytein(iobase - (hscx ? 0x820 : 0xc20) + offset));
+}
+
+#define READHSCX(mbase,ibase,hscx,ofs) \
+ ((mbase)?readhscx_0(mbase,hscx,ofs):readhscx_3(ibase,hscx,ofs))
+
+static inline void
+writehscx_0(unsigned int base, byte hscx, byte offset, byte data)
+{
+ writeb(data, base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
+ ((hscx & 1) ? 0x40 : 0) + offset);
+}
+
+static inline void
+writehscx_3(int iobase, byte hscx, byte offset, byte data)
+{
+ byteout(iobase - (hscx ? 0x820 : 0xc20) + offset, data);
+}
+
+static inline void
+readhscx_s(int iobase, byte hscx, byte offset, byte * dest, int count)
+{
+ insb(iobase - (hscx ? 0x820 : 0xc20) + offset, dest, count);
+}
+
+static inline void
+writehscx_s(int iobase, byte hscx, byte offset, byte * src, int count)
+{
+ outsb(iobase - (hscx ? 0x820 : 0xc20) + offset, src, count);
+}
+
+#define ISAC_MASK 0x20
+#define ISAC_ISTA 0x20
+#define ISAC_STAR 0x21
+#define ISAC_CMDR 0x21
+#define ISAC_EXIR 0x24
+
+#define ISAC_RBCH 0x2a
+
+#define ISAC_ADF2 0x39
+#define ISAC_SPCR 0x30
+#define ISAC_ADF1 0x38
+#define ISAC_CIX0 0x31
+#define ISAC_STCR 0x37
+#define ISAC_MODE 0x22
+#define ISAC_RSTA 0x27
+#define ISAC_RBCL 0x25
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+
+#define HSCX_ISTA 0x20
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+#define HSCX_CMDR 0x21
+#define HSCX_EXIR 0x24
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_RSTA 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+static inline void
+waitforCEC_0(unsigned int base, byte hscx)
+{
+ long to = 10;
+
+ while ((readhscx_0(base, hscx, HSCX_STAR) & 0x04) && to) {
+ udelay(5);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "waitforCEC timeout\n");
+}
+
+static inline void
+waitforCEC_3(int iobase, byte hscx)
+{
+ long to = 10;
+
+ while ((readhscx_3(iobase, hscx, HSCX_STAR) & 0x04) && to) {
+ udelay(5);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "waitforCEC timeout\n");
+}
+
+static inline void
+waitforXFW_0(unsigned int base, byte hscx)
+{
+ long to = 20;
+
+ while ((!(readhscx_0(base, hscx, HSCX_STAR) & 0x44)==0x40) && to) {
+ udelay(5);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "waitforXFW timeout\n");
+}
+
+static inline void
+waitforXFW_3(int iobase, byte hscx)
+{
+ long to = 20;
+
+ while ((!(readhscx_3(iobase, hscx, HSCX_STAR) & 0x44)==0x40) && to) {
+ udelay(5);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR_0(unsigned int base, byte hscx, byte data)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ waitforCEC_0(base, hscx);
+ writehscx_0(base, hscx, HSCX_CMDR, data);
+ restore_flags(flags);
+}
+
+static inline void
+writehscxCMDR_3(int iobase, byte hscx, byte data)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ waitforCEC_3(iobase, hscx);
+ writehscx_3(iobase, hscx, HSCX_CMDR, data);
+ restore_flags(flags);
+}
+
+#define WRITEHSCX_CMDR(mbase,ibase,hscx,data) \
+ ((mbase)?writehscxCMDR_0(mbase,hscx,data):writehscxCMDR_3(ibase,hscx,data))
+
+/*
+ * fast interrupt here
+ */
+
+#define ISAC_RCVBUFREADY 0
+#define ISAC_XMTBUFREADY 1
+#define ISAC_PHCHANGE 2
+
+#define HSCX_RCVBUFREADY 0
+#define HSCX_XMTBUFREADY 1
+
+void
+teles_hscxreport(struct IsdnCardState *sp, int hscx)
+{
+ printk(KERN_DEBUG "HSCX %d\n", hscx);
+ if (sp->membase) {
+ printk(KERN_DEBUG " ISTA %x\n", readhscx_0(sp->membase,
+ hscx, HSCX_ISTA));
+ printk(KERN_DEBUG " STAR %x\n", readhscx_0(sp->membase,
+ hscx, HSCX_STAR));
+ printk(KERN_DEBUG " EXIR %x\n", readhscx_0(sp->membase,
+ hscx, HSCX_EXIR));
+ } else {
+ printk(KERN_DEBUG " ISTA %x\n", readhscx_3(sp->iobase,
+ hscx, HSCX_ISTA));
+ printk(KERN_DEBUG " STAR %x\n", readhscx_3(sp->iobase,
+ hscx, HSCX_STAR));
+ printk(KERN_DEBUG " EXIR %x\n", readhscx_3(sp->iobase,
+ hscx, HSCX_EXIR));
+ }
+}
+
+void
+teles_report(struct IsdnCardState *sp)
+{
+ printk(KERN_DEBUG "ISAC\n");
+ if (sp->membase) {
+ printk(KERN_DEBUG " ISTA %x\n", readisac_0(sp->membase,
+ ISAC_ISTA));
+ printk(KERN_DEBUG " STAR %x\n", readisac_0(sp->membase,
+ ISAC_STAR));
+ printk(KERN_DEBUG " EXIR %x\n", readisac_0(sp->membase,
+ ISAC_EXIR));
+ } else {
+ printk(KERN_DEBUG " ISTA %x\n", readisac_3(sp->iobase,
+ ISAC_ISTA));
+ printk(KERN_DEBUG " STAR %x\n", readisac_3(sp->iobase,
+ ISAC_STAR));
+ printk(KERN_DEBUG " EXIR %x\n", readisac_3(sp->iobase,
+ ISAC_EXIR));
+ }
+ teles_hscxreport(sp, 0);
+ teles_hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_sched_event(struct HscxState *hsp, int event)
+{
+ hsp->event |= 1 << event;
+ queue_task_irq_off(&hsp->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+ byte *ptr;
+ struct BufHeader *ibh = hsp->rcvibh;
+
+ if (hsp->sp->debug)
+ printk(KERN_DEBUG "hscx_empty_fifo\n");
+
+ if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+ HSCX_RBUF_BPPS)) {
+ printk(KERN_WARNING
+ "hscx_empty_fifo: incoming packet too large\n");
+ WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx, 0x80);
+ return;
+ }
+ ptr = DATAPTR(ibh);
+ ptr += hsp->rcvptr;
+
+ hsp->rcvptr += count;
+ if (hsp->membase) {
+ while (count--)
+ *ptr++ = readhscx_0(hsp->membase, hsp->hscx, 0x0);
+ writehscxCMDR_0(hsp->membase, hsp->hscx, 0x80);
+ } else {
+ readhscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
+ writehscxCMDR_3(hsp->iobase, hsp->hscx, 0x80);
+ }
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+ struct BufHeader *ibh;
+ int more, count;
+ byte *ptr;
+
+ if (hsp->sp->debug)
+ printk(KERN_DEBUG "hscx_fill_fifo\n");
+
+ ibh = hsp->xmtibh;
+ if (!ibh)
+ return;
+
+ count = ibh->datasize - hsp->sendptr;
+ if (count <= 0)
+ return;
+
+ more = (hsp->mode == 1)?1:0;
+ if (count > 32) {
+ more = !0;
+ count = 32;
+ }
+ ptr = DATAPTR(ibh);
+ ptr += hsp->sendptr;
+ hsp->sendptr += count;
+
+#ifdef BCHAN_VERBOSE
+ {
+ int i;
+ printk(KERN_DEBUG "hscx_fill_fifo ");
+ for (i = 0; i < count; i++)
+ printk(" %2x", ptr[i]);
+ printk("\n");
+ }
+#endif
+ if (hsp->membase) {
+ waitforXFW_0(hsp->membase, hsp->hscx);
+ while (count--)
+ writehscx_0(hsp->membase, hsp->hscx, 0x0, *ptr++);
+ writehscxCMDR_0(hsp->membase, hsp->hscx, more ? 0x8 : 0xa);
+ } else {
+ waitforXFW_3(hsp->iobase, hsp->hscx);
+ writehscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
+ writehscxCMDR_3(hsp->iobase, hsp->hscx, more ? 0x8 : 0xa);
+ }
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, byte val, byte hscx)
+{
+ byte r;
+ struct HscxState *hsp = sp->hs + hscx;
+ int count, err;
+
+ if (!hsp->init)
+ return;
+
+ if (val & 0x80) { /* RME */
+
+ r = READHSCX(hsp->membase, sp->iobase, hsp->hscx, HSCX_RSTA);
+ if ((r & 0xf0) != 0xa0) {
+ if (!r & 0x80)
+ printk(KERN_WARNING
+ "Teles: HSCX invalid frame\n");
+ if ((r & 0x40) && hsp->mode)
+ printk(KERN_WARNING "Teles: HSCX RDO mode=%d\n",hsp->mode);
+ if (!r & 0x20)
+ printk(KERN_WARNING "Teles: HSCX CRC error\n");
+ if (hsp->rcvibh)
+ BufPoolRelease(hsp->rcvibh);
+ hsp->rcvibh = NULL;
+ WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx,
+ 0x80);
+ goto afterRME;
+ }
+ if (!hsp->rcvibh)
+ if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+ GFP_ATOMIC, (void *) 1, 1)) {
+ printk(KERN_WARNING
+ "HSCX RME out of buffers at %ld\n",
+ jiffies);
+ WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+ hsp->hscx, 0x80);
+ goto afterRME;
+ } else
+ hsp->rcvptr = 0;
+
+ count = READHSCX(hsp->membase, sp->iobase, hsp->hscx,
+ HSCX_RBCL) & 0x1f;
+ if (count == 0)
+ count = 32;
+ hscx_empty_fifo(hsp, count);
+ hsp->rcvibh->datasize = hsp->rcvptr - 1;
+ BufQueueLink(&hsp->rq, hsp->rcvibh);
+ hsp->rcvibh = NULL;
+ hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+ }
+ afterRME:
+ if (val & 0x40) { /* RPF */
+ if (!hsp->rcvibh) {
+ if (hsp->mode == 1)
+ err=BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
+ GFP_ATOMIC, (void *)1, 2);
+ else
+ err=BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+ GFP_ATOMIC, (void *)1, 2);
+
+ if (err) {
+ printk(KERN_WARNING
+ "HSCX RPF out of buffers at %ld\n",
+ jiffies);
+ WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+ hsp->hscx, 0x80);
+ goto afterRPF;
+ } else
+ hsp->rcvptr = 0;
+ }
+
+ hscx_empty_fifo(hsp, 32);
+ if (hsp->mode == 1) {
+ /* receive audio data */
+ hsp->rcvibh->datasize = hsp->rcvptr;
+ BufQueueLink(&hsp->rq, hsp->rcvibh);
+ hsp->rcvibh = NULL;
+ hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+ }
+
+ }
+ afterRPF:
+ if (val & 0x10) { /* XPR */
+ if (hsp->xmtibh)
+ if (hsp->xmtibh->datasize > hsp->sendptr) {
+ hscx_fill_fifo(hsp);
+ goto afterXPR;
+ } else {
+ if (hsp->releasebuf)
+ BufPoolRelease(hsp->xmtibh);
+ hsp->sendptr = 0;
+ if (hsp->st->l4.l1writewakeup)
+ hsp->st->l4.l1writewakeup(hsp->st);
+ hsp->xmtibh = NULL;
+ }
+ if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+ hsp->releasebuf = !0;
+ hscx_fill_fifo(hsp);
+ } else
+ hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+ }
+ afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_sched_event(struct IsdnCardState *sp, int event)
+{
+ sp->event |= 1 << event;
+ queue_task_irq_off(&sp->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static void
+empty_fifo(struct IsdnCardState *sp, int count)
+{
+ byte *ptr;
+ struct BufHeader *ibh = sp->rcvibh;
+
+ if (sp->debug)
+ printk(KERN_DEBUG "empty_fifo\n");
+
+ if (sp->rcvptr >= 3072) {
+ printk(KERN_WARNING "empty_fifo rcvptr %d\n", sp->rcvptr);
+ return;
+ }
+ ptr = DATAPTR(ibh);
+ ptr += sp->rcvptr;
+ sp->rcvptr += count;
+
+ if (sp->membase) {
+#ifdef DCHAN_VERBOSE
+ printk(KERN_DEBUG "empty_fifo ");
+ while (count--) {
+ *ptr = readisac_0(sp->membase, 0x0);
+ printk("%2x ", *ptr);
+ ptr++;
+ }
+ printk("\n");
+#else
+ while (count--)
+ *ptr++ = readisac_0(sp->membase, 0x0);
+#endif
+ writeisac_0(sp->membase, ISAC_CMDR, 0x80);
+ } else {
+#ifdef DCHAN_VERBOSE
+ int i;
+ printk(KERN_DEBUG "empty_fifo ");
+ readisac_s(sp->iobase, 0x3e, ptr, count);
+ for (i = 0; i < count; i++)
+ printk("%2x ", ptr[i]);
+ printk("\n");
+#else
+ readisac_s(sp->iobase, 0x3e, ptr, count);
+#endif
+ writeisac_3(sp->iobase, ISAC_CMDR, 0x80);
+ }
+}
+
+static void
+fill_fifo(struct IsdnCardState *sp)
+{
+ struct BufHeader *ibh;
+ int count, more;
+ byte *ptr;
+
+ if (sp->debug)
+ printk(KERN_DEBUG "fill_fifo\n");
+
+ ibh = sp->xmtibh;
+ if (!ibh)
+ return;
+
+ count = ibh->datasize - sp->sendptr;
+ if (count <= 0)
+ return;
+ if (count >= 3072)
+ return;
+
+ more = 0;
+ if (count > 32) {
+ more = !0;
+ count = 32;
+ }
+ ptr = DATAPTR(ibh);
+ ptr += sp->sendptr;
+ sp->sendptr += count;
+
+ if (sp->membase) {
+#ifdef DCHAN_VERBOSE
+ printk(KERN_DEBUG "fill_fifo ");
+ while (count--) {
+ writeisac_0(sp->membase, 0x0, *ptr);
+ printk("%2x ", *ptr);
+ ptr++;
+ }
+ printk("\n");
+#else
+ while (count--)
+ writeisac_0(sp->membase, 0x0, *ptr++);
+#endif
+ writeisac_0(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa);
+ } else {
+#ifdef DCHAN_VERBOSE
+ int i;
+ writeisac_s(sp->iobase, 0x3e, ptr, count);
+ printk(KERN_DEBUG "fill_fifo ");
+ for (i = 0; i < count; i++)
+ printk("%2x ", ptr[i]);
+ printk("\n");
+#else
+ writeisac_s(sp->iobase, 0x3e, ptr, count);
+#endif
+ writeisac_3(sp->iobase, ISAC_CMDR, more ? 0x8 : 0xa);
+ }
+}
+
+static int
+act_wanted(struct IsdnCardState *sp)
+{
+ struct PStack *st;
+
+ st = sp->stlist;
+ while (st)
+ if (st->l1.act_state)
+ return (!0);
+ else
+ st = st->next;
+ return (0);
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+ printk(KERN_DEBUG "ph_command %d\n", command);
+ WRITEISAC(sp->membase, sp->iobase, ISAC_CIX0, (command << 2) | 3);
+}
+
+static void
+isac_new_ph(struct IsdnCardState *sp)
+{
+ int enq;
+
+ enq = act_wanted(sp);
+
+ switch (sp->ph_state) {
+ case (0):
+ case (6):
+ if (enq)
+ ph_command(sp, 0);
+ else
+ ph_command(sp, 15);
+ break;
+ case (7):
+ if (enq)
+ ph_command(sp, 9);
+ break;
+ case (12):
+ ph_command(sp, 8);
+ sp->ph_active = 5;
+ isac_sched_event(sp, ISAC_PHCHANGE);
+ if (!sp->xmtibh)
+ if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
+ sp->sendptr = 0;
+ if (sp->xmtibh)
+ fill_fifo(sp);
+ break;
+ case (13):
+ ph_command(sp, 9);
+ sp->ph_active = 5;
+ isac_sched_event(sp, ISAC_PHCHANGE);
+ if (!sp->xmtibh)
+ if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
+ sp->sendptr = 0;
+ if (sp->xmtibh)
+ fill_fifo(sp);
+ break;
+ case (4):
+ case (8):
+ break;
+ default:
+ sp->ph_active = 0;
+ break;
+ }
+}
+
+static void
+teles_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+ byte val, r, exval;
+ struct IsdnCardState *sp;
+ unsigned int count;
+ struct HscxState *hsp;
+
+ sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+ if (!sp) {
+ printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+ return;
+ }
+ val = READHSCX(sp->membase, sp->iobase, 1, HSCX_ISTA);
+
+ if (val & 0x01) {
+ hsp = sp->hs + 1;
+ exval = READHSCX(sp->membase, sp->iobase, 1, HSCX_EXIR);
+ if (exval == 0x40) {
+ if (hsp->mode == 1)
+ hscx_fill_fifo(hsp);
+ else {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ hsp->sendptr = 0;
+ WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+ hsp->hscx, 0x01);
+ printk(KERN_DEBUG "HSCX B EXIR %x\n", exval);
+ }
+ } else
+ printk(KERN_WARNING "HSCX B EXIR %x\n", exval);
+ }
+ if (val & 0xf8) {
+ if (sp->debug)
+ printk(KERN_DEBUG "HSCX B interrupt %x\n", val);
+ hscx_interrupt(sp, val, 1);
+ }
+ if (val & 0x02) {
+ hsp = sp->hs;
+ exval = READHSCX(sp->membase, sp->iobase, 0, HSCX_EXIR);
+ if (exval == 0x40) {
+ if (hsp->mode == 1)
+ hscx_fill_fifo(hsp);
+ else {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ hsp->sendptr = 0;
+ WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+ hsp->hscx, 0x01);
+ printk(KERN_DEBUG "HSCX A EXIR %x\n", exval);
+ }
+ } else
+ printk(KERN_WARNING "HSCX A EXIR %x\n", exval);
+ }
+ if (val & 0x04) {
+ val = READHSCX(sp->membase, sp->iobase, 0, HSCX_ISTA);
+ if (sp->debug)
+ printk(KERN_DEBUG "HSCX A interrupt %x\n",
+ val);
+ hscx_interrupt(sp, val, 0);
+ }
+
+ val = READISAC(sp->membase, sp->iobase, ISAC_ISTA);
+
+ if (sp->debug)
+ printk(KERN_DEBUG "ISAC interrupt %x\n", val);
+
+ if (val & 0x80) { /* RME */
+
+ r = READISAC(sp->membase, sp->iobase, ISAC_RSTA);
+ if ((r & 0x70) != 0x20) {
+ if (r & 0x40)
+ printk(KERN_WARNING "Teles: ISAC RDO\n");
+ if (!r & 0x20)
+ printk(KERN_WARNING "Teles: ISAC CRC error\n");
+ if (sp->rcvibh)
+ BufPoolRelease(sp->rcvibh);
+ sp->rcvibh = NULL;
+ WRITEISAC(sp->membase, sp->iobase, ISAC_CMDR, 0x80);
+ goto afterRME;
+ }
+ if (!sp->rcvibh)
+ if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+ GFP_ATOMIC,
+ (void *) 1, 3)) {
+ printk(KERN_WARNING
+ "ISAC RME out of buffers!\n");
+ WRITEISAC(sp->membase, sp->iobase,
+ ISAC_CMDR, 0x80);
+ goto afterRME;
+ } else
+ sp->rcvptr = 0;
+
+ count = READISAC(sp->membase, sp->iobase, ISAC_RBCL) & 0x1f;
+ if (count == 0)
+ count = 32;
+ empty_fifo(sp, count);
+ sp->rcvibh->datasize = sp->rcvptr;
+ BufQueueLink(&(sp->rq), sp->rcvibh);
+ sp->rcvibh = NULL;
+ isac_sched_event(sp, ISAC_RCVBUFREADY);
+ }
+ afterRME:
+ if (val & 0x40) { /* RPF */
+ if (!sp->rcvibh)
+ if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+ GFP_ATOMIC,
+ (void *) 1, 4)) {
+ printk(KERN_WARNING
+ "ISAC RME out of buffers!\n");
+ WRITEISAC(sp->membase, sp->iobase,
+ ISAC_CMDR, 0x80);
+ goto afterRPF;
+ } else
+ sp->rcvptr = 0;
+ empty_fifo(sp, 32);
+ }
+ afterRPF:
+ if (val & 0x20) {
+ }
+ if (val & 0x10) { /* XPR */
+ if (sp->xmtibh)
+ if (sp->xmtibh->datasize > sp->sendptr) {
+ fill_fifo(sp);
+ goto afterXPR;
+ } else {
+ if (sp->releasebuf)
+ BufPoolRelease(sp->xmtibh);
+ sp->xmtibh = NULL;
+ sp->sendptr = 0;
+ }
+ if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+ sp->releasebuf = !0;
+ fill_fifo(sp);
+ } else
+ isac_sched_event(sp, ISAC_XMTBUFREADY);
+ }
+ afterXPR:
+ if (val & 0x04) { /* CISQ */
+ sp->ph_state = (READISAC(sp->membase, sp->iobase, ISAC_CIX0)
+ >> 2) & 0xf;
+ printk(KERN_DEBUG "l1state %d\n", sp->ph_state);
+ isac_new_ph(sp);
+ }
+ if (sp->membase) {
+ writeisac_0(sp->membase, ISAC_MASK, 0xFF);
+ writehscx_0(sp->membase, 0, HSCX_MASK, 0xFF);
+ writehscx_0(sp->membase, 1, HSCX_MASK, 0xFF);
+ writeisac_0(sp->membase, ISAC_MASK, 0x0);
+ writehscx_0(sp->membase, 0, HSCX_MASK, 0x0);
+ writehscx_0(sp->membase, 1, HSCX_MASK, 0x0);
+ } else {
+ writeisac_3(sp->iobase, ISAC_MASK, 0xFF);
+ writehscx_3(sp->iobase, 0, HSCX_MASK, 0xFF);
+ writehscx_3(sp->iobase, 1, HSCX_MASK, 0xFF);
+ writeisac_3(sp->iobase, ISAC_MASK, 0x0);
+ writehscx_3(sp->iobase, 0, HSCX_MASK, 0x0);
+ writehscx_3(sp->iobase, 1, HSCX_MASK, 0x0);
+ }
+}
+
+/*
+ * soft interrupt
+ */
+
+static void
+act_ivated(struct IsdnCardState *sp)
+{
+ struct PStack *st;
+
+ st = sp->stlist;
+ while (st) {
+ if (st->l1.act_state == 1) {
+ st->l1.act_state = 2;
+ st->l1.l1man(st, PH_ACTIVATE, NULL);
+ }
+ st = st->next;
+ }
+}
+
+static void
+process_new_ph(struct IsdnCardState *sp)
+{
+ if (sp->ph_active == 5)
+ act_ivated(sp);
+}
+
+static void
+process_xmt(struct IsdnCardState *sp)
+{
+ struct PStack *stptr;
+
+ if (sp->xmtibh)
+ return;
+
+ stptr = sp->stlist;
+ while (stptr != NULL)
+ if (stptr->l1.requestpull) {
+ stptr->l1.requestpull = 0;
+ stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+ break;
+ } else
+ stptr = stptr->next;
+}
+
+static void
+process_rcv(struct IsdnCardState *sp)
+{
+ struct BufHeader *ibh, *cibh;
+ struct PStack *stptr;
+ byte *ptr;
+ int found, broadc;
+ char tmp[64];
+
+ while (!BufQueueUnlink(&ibh, &sp->rq)) {
+ stptr = sp->stlist;
+ ptr = DATAPTR(ibh);
+ broadc = (ptr[1] >> 1) == 127;
+
+ if (broadc && sp->dlogflag && (!(ptr[0] >> 2)))
+ dlogframe(sp, ptr + 3, ibh->datasize - 3,
+ "Q.931 frame network->user broadcast");
+
+ if (broadc) {
+ while (stptr != NULL) {
+ if ((ptr[0] >> 2) == stptr->l2.sap)
+ if (!BufPoolGet(&cibh, &sp->rbufpool, GFP_ATOMIC,
+ (void *) 1, 5)) {
+ memcpy(DATAPTR(cibh), DATAPTR(ibh), ibh->datasize);
+ cibh->datasize = ibh->datasize;
+ stptr->l1.l1l2(stptr, PH_DATA, cibh);
+ } else
+ printk(KERN_WARNING "isdn broadcast buffer shortage\n");
+ stptr = stptr->next;
+ }
+ BufPoolRelease(ibh);
+ } else {
+ found = 0;
+ while (stptr != NULL)
+ if (((ptr[0] >> 2) == stptr->l2.sap) &&
+ ((ptr[1] >> 1) == stptr->l2.tei)) {
+ stptr->l1.l1l2(stptr, PH_DATA, ibh);
+ found = !0;
+ break;
+ } else
+ stptr = stptr->next;
+ if (!found) {
+ /* BD 10.10.95
+ * Print out D-Channel msg not processed
+ * by isdn4linux
+ */
+
+ if ((!(ptr[0] >> 2)) && (!(ptr[2] & 0x01))) {
+ sprintf(tmp, "Q.931 frame network->user with tei %d (not for us)", ptr[1] >> 1);
+ dlogframe(sp, ptr + 4, ibh->datasize - 4, tmp);
+ }
+ BufPoolRelease(ibh);
+ }
+ }
+
+ }
+
+}
+
+static void
+isac_bh(struct IsdnCardState *sp)
+{
+ if (!sp)
+ return;
+
+ if (clear_bit(ISAC_PHCHANGE, &sp->event))
+ process_new_ph(sp);
+ if (clear_bit(ISAC_RCVBUFREADY, &sp->event))
+ process_rcv(sp);
+ if (clear_bit(ISAC_XMTBUFREADY, &sp->event))
+ process_xmt(sp);
+}
+
+
+static void
+hscx_process_xmt(struct HscxState *hsp)
+{
+ struct PStack *st = hsp->st;
+
+ if (hsp->xmtibh)
+ return;
+
+ if (st->l1.requestpull) {
+ st->l1.requestpull = 0;
+ st->l1.l1l2(st, PH_PULL_ACK, NULL);
+ }
+ if (!hsp->active)
+ if ((!hsp->xmtibh) && (!hsp->sq.head))
+ modehscx(hsp, 0, 0);
+}
+
+static void
+hscx_process_rcv(struct HscxState *hsp)
+{
+ struct BufHeader *ibh;
+
+#ifdef DEBUG_MAGIC
+ if (hsp->magic != 301270) {
+ printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
+ return;
+ }
+#endif
+ while (!BufQueueUnlink(&ibh, &hsp->rq)) {
+ hsp->st->l1.l1l2(hsp->st, PH_DATA, ibh);
+ }
+}
+
+static void
+hscx_bh(struct HscxState *hsp)
+{
+
+ if (!hsp)
+ return;
+
+ if (clear_bit(HSCX_RCVBUFREADY, &hsp->event))
+ hscx_process_rcv(hsp);
+ if (clear_bit(HSCX_XMTBUFREADY, &hsp->event))
+ hscx_process_xmt(hsp);
+
+}
+
+/*
+ * interrupt stuff ends here
+ */
+
+static void
+restart_ph(struct IsdnCardState *sp)
+{
+ switch (sp->ph_active) {
+ case (0):
+ if (sp->ph_state == 6)
+ ph_command(sp, 0);
+ else
+ ph_command(sp, 1);
+ sp->ph_active = 1;
+ break;
+ }
+}
+
+static void
+initisac(unsigned int cardmem, int iobase)
+{
+ if (cardmem) {
+ writeisac_0(cardmem, ISAC_MASK, 0xff);
+ writeisac_0(cardmem, ISAC_ADF2, 0x0);
+ writeisac_0(cardmem, ISAC_SPCR, 0xa);
+ writeisac_0(cardmem, ISAC_ADF1, 0x2);
+ writeisac_0(cardmem, ISAC_STCR, 0x70);
+ writeisac_0(cardmem, ISAC_MODE, 0xc9);
+ writeisac_0(cardmem, ISAC_CMDR, 0x41);
+ writeisac_0(cardmem, ISAC_CIX0, (1 << 2) | 3);
+ } else {
+ writeisac_3(iobase, ISAC_MASK, 0xff);
+ writeisac_3(iobase, ISAC_ADF2, 0x80);
+ writeisac_3(iobase, ISAC_SQXR, 0x2f);
+ writeisac_3(iobase, ISAC_SPCR, 0x00);
+ writeisac_3(iobase, ISAC_ADF1, 0x02);
+ writeisac_3(iobase, ISAC_STCR, 0x70);
+ writeisac_3(iobase, ISAC_MODE, 0xc9);
+ writeisac_3(iobase, ISAC_TIMR, 0x00);
+ writeisac_3(iobase, ISAC_ADF1, 0x00);
+ writeisac_3(iobase, ISAC_CMDR, 0x41);
+ writeisac_3(iobase, ISAC_CIX0, (1 << 2) | 3);
+ }
+}
+
+static int
+checkcard(int cardnr)
+{
+ int timout;
+ byte cfval, val;
+ struct IsdnCard *card = cards + cardnr;
+
+ if (card->membase)
+ if (card->membase < 0x10000) {
+ card->membase <<= 4;
+ printk(KERN_INFO
+ "Teles membase configured DOSish, assuming 0x%x\n",
+ card->membase);
+ }
+ if (!card->iobase) {
+ if (card->membase) {
+ printk(KERN_NOTICE
+ "Teles 8 assumed, mem: %x irq: %d proto: %s\n",
+ card->membase, card->interrupt,
+ (card->protocol == ISDN_PTYPE_1TR6) ?
+ "1TR6" : "EDSS1");
+ printk(KERN_INFO "HSCX version A:%x B:%x\n",
+ readhscx_0(card->membase, 0, HSCX_VSTR) & 0xf,
+ readhscx_0(card->membase, 1, HSCX_VSTR) & 0xf);
+ }
+ } else {
+ switch (card->iobase) {
+ case 0x180:
+ case 0x280:
+ case 0x380:
+ card->iobase |= 0xc00;
+ break;
+ }
+ if (card->membase) { /* 16.0 */
+ if (check_region(card->iobase, 8)) {
+ printk(KERN_WARNING
+ "teles: ports %x-%x already in use\n",
+ card->iobase,
+ card->iobase + 8 );
+ return -1;
+ }
+ } else { /* 16.3 */
+ if (check_region(card->iobase, 16)) {
+ printk(KERN_WARNING
+ "teles: 16.3 ports %x-%x already in use\n",
+ card->iobase,
+ card->iobase + 16 );
+ return -1;
+ }
+ if (check_region((card->iobase - 0xc00) , 32)) {
+ printk(KERN_WARNING
+ "teles: 16.3 ports %x-%x already in use\n",
+ card->iobase - 0xc00,
+ card->iobase - 0xc00 + 32);
+ return -1;
+ }
+ if (check_region((card->iobase - 0x800) , 32)) {
+ printk(KERN_WARNING
+ "teles: 16.3 ports %x-%x already in use\n",
+ card->iobase - 0x800,
+ card->iobase - 0x800 + 32);
+ return -1;
+ }
+ if (check_region((card->iobase - 0x400) , 32)) {
+ printk(KERN_WARNING
+ "teles: 16.3 ports %x-%x already in use\n",
+ card->iobase - 0x400,
+ card->iobase - 0x400 + 32);
+ return -1;
+ }
+ }
+ switch (card->interrupt) {
+ case 2:
+ cfval = 0x00;
+ break;
+ case 3:
+ cfval = 0x02;
+ break;
+ case 4:
+ cfval = 0x04;
+ break;
+ case 5:
+ cfval = 0x06;
+ break;
+ case 10:
+ cfval = 0x08;
+ break;
+ case 11:
+ cfval = 0x0A;
+ break;
+ case 12:
+ cfval = 0x0C;
+ break;
+ case 15:
+ cfval = 0x0E;
+ break;
+ default:
+ cfval = 0x00;
+ break;
+ }
+ if (card->membase) {
+ cfval |= (card->membase >> 9) & 0xF0;
+ }
+ if (bytein(card->iobase + 0) != 0x51) {
+ printk(KERN_INFO "XXX Byte at %x is %x\n",
+ card->iobase + 0,
+ bytein(card->iobase + 0));
+ return -2;
+ }
+ if (bytein(card->iobase + 1) != 0x93) {
+ printk(KERN_INFO "XXX Byte at %x is %x\n",
+ card->iobase + 1,
+ bytein(card->iobase + 1));
+ return -2;
+ }
+ val = bytein(card->iobase + 2); /* 0x1e=without AB
+ * 0x1f=with AB
+ * 0x1c 16.3 ???
+ */
+ if (val != 0x1c && val != 0x1e && val != 0x1f) {
+ printk(KERN_INFO "XXX Byte at %x is %x\n",
+ card->iobase + 2,
+ bytein(card->iobase + 2));
+ return -2;
+ }
+ if (card->membase) { /* 16.0 */
+ request_region(card->iobase, 8, "teles 16.0");
+ } else {
+ request_region(card->iobase, 16, "teles 16.3");
+ request_region(card->iobase - 0xC00, 32, "teles HSCX0");
+ request_region(card->iobase - 0x800, 32, "teles HSCX1");
+ request_region(card->iobase - 0x400, 32, "teles ISAC");
+ }
+ cli();
+ timout = jiffies + (HZ / 10) + 1;
+ byteout(card->iobase + 4, cfval);
+ sti();
+ while (jiffies <= timout);
+
+ cli();
+ timout = jiffies + (HZ / 10) + 1;
+ byteout(card->iobase + 4, cfval | 1);
+ sti();
+ while (jiffies <= timout);
+
+ if (card->membase)
+ printk(KERN_NOTICE
+ "Teles 16.0 found, io: %x mem: %x irq: %d proto: %s\n",
+ card->iobase, card->membase,
+ card->interrupt,
+ (card->protocol == ISDN_PTYPE_1TR6) ?
+ "1TR6" : "EDSS1");
+ else
+ printk(KERN_NOTICE
+ "Teles 16.3 found, io: %x irq: %d proto: %s\n",
+ card->iobase, card->interrupt,
+ (card->protocol == ISDN_PTYPE_1TR6) ?
+ "1TR6" : "EDSS1");
+ printk(KERN_INFO "HSCX version A:%x B:%x\n",
+ READHSCX(card->membase, card->iobase, 0,
+ HSCX_VSTR) & 0xf,
+ READHSCX(card->membase, card->iobase, 1,
+ HSCX_VSTR) & 0xf);
+
+ }
+ if (card->membase) {
+ cli();
+ timout = jiffies + (HZ / 5) + 1;
+ writeb(0, card->membase + 0x80);
+ sti();
+ while (jiffies <= timout);
+
+ cli();
+ writeb(1, card->membase + 0x80);
+ timout = jiffies + (HZ / 5) + 1;
+ sti();
+ while (jiffies <= timout);
+ }
+ return (0);
+}
+
+void
+modehscx(struct HscxState *hs, int mode,
+ int ichan)
+{
+ struct IsdnCardState *sp = hs->sp;
+ int hscx = hs->hscx;
+
+ printk(KERN_DEBUG "modehscx hscx %d mode %d ichan %d\n",
+ hscx, mode, ichan);
+
+ hs->mode = mode;
+ if (sp->membase) {
+ /* What's that ??? KKeil */
+ if (hscx == 0)
+ ichan = 1 - ichan; /* raar maar waar... */
+ writehscx_0(sp->membase, hscx, HSCX_CCR1, 0x85);
+ writehscx_0(sp->membase, hscx, HSCX_XAD1, 0xFF);
+ writehscx_0(sp->membase, hscx, HSCX_XAD2, 0xFF);
+ writehscx_0(sp->membase, hscx, HSCX_RAH2, 0xFF);
+ writehscx_0(sp->membase, hscx, HSCX_XBCH, 0x0);
+
+ switch (mode) {
+ case (0):
+ writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+ writehscx_0(sp->membase, hscx, HSCX_TSAX, 0xff);
+ writehscx_0(sp->membase, hscx, HSCX_TSAR, 0xff);
+ writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+ writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+ writehscx_0(sp->membase, hscx, HSCX_MODE, 0x84);
+ break;
+ case (1):
+ if (ichan == 0) {
+ writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+ writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
+ writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
+ writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+ writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+ } else {
+ writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+ writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
+ writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
+ writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+ writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+ }
+ writehscx_0(sp->membase, hscx, HSCX_MODE, 0xe4);
+ writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
+ break;
+ case (2):
+ if (ichan == 0) {
+ writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+ writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
+ writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
+ writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+ writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+ } else {
+ writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+ writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
+ writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
+ writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+ writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+ }
+ writehscx_0(sp->membase, hscx, HSCX_MODE, 0x8c);
+ writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
+ break;
+ }
+ writehscx_0(sp->membase, hscx, HSCX_ISTA, 0x00);
+ } else {
+ writehscx_3(sp->iobase, hscx, HSCX_CCR1, 0x85);
+ writehscx_3(sp->iobase, hscx, HSCX_XAD1, 0xFF);
+ writehscx_3(sp->iobase, hscx, HSCX_XAD2, 0xFF);
+ writehscx_3(sp->iobase, hscx, HSCX_RAH2, 0xFF);
+ writehscx_3(sp->iobase, hscx, HSCX_XBCH, 0x00);
+ writehscx_3(sp->iobase, hscx, HSCX_RLCR, 0x00);
+
+ switch (mode) {
+ case (0):
+ writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0xff);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0xff);
+ writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+ writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+ writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x84);
+ break;
+ case (1):
+ if (ichan == 0) {
+ writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
+ writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+ writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+ } else {
+ writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
+ writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+ writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+ }
+ writehscx_3(sp->iobase, hscx, HSCX_MODE, 0xe4);
+ writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
+ break;
+ case (2):
+ if (ichan == 0) {
+ writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
+ writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+ writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+ } else {
+ writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
+ writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
+ writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+ writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+ }
+ writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x8c);
+ writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
+ break;
+ }
+ writehscx_3(sp->iobase, hscx, HSCX_ISTA, 0x00);
+ }
+}
+
+void
+teles_addlist(struct IsdnCardState *sp,
+ struct PStack *st)
+{
+ st->next = sp->stlist;
+ sp->stlist = st;
+}
+
+void
+teles_rmlist(struct IsdnCardState *sp,
+ struct PStack *st)
+{
+ struct PStack *p;
+
+ if (sp->stlist == st)
+ sp->stlist = st->next;
+ else {
+ p = sp->stlist;
+ while (p)
+ if (p->next == st) {
+ p->next = st->next;
+ return;
+ } else
+ p = p->next;
+ }
+}
+
+
+static void
+teles_l2l1(struct PStack *st, int pr,
+ struct BufHeader *ibh)
+{
+ struct IsdnCardState *sp = (struct IsdnCardState *)
+ st->l1.hardware;
+
+
+ switch (pr) {
+ case (PH_DATA):
+ if (sp->xmtibh)
+ BufQueueLink(&sp->sq, ibh);
+ else {
+ sp->xmtibh = ibh;
+ sp->sendptr = 0;
+ sp->releasebuf = !0;
+ fill_fifo(sp);
+ }
+ break;
+ case (PH_DATA_PULLED):
+ if (sp->xmtibh) {
+ printk(KERN_DEBUG "teles_l2l1: this shouldn't happen\n");
+ break;
+ }
+ sp->xmtibh = ibh;
+ sp->sendptr = 0;
+ sp->releasebuf = 0;
+ fill_fifo(sp);
+ break;
+ case (PH_REQUEST_PULL):
+ if (!sp->xmtibh) {
+ st->l1.requestpull = 0;
+ st->l1.l1l2(st, PH_PULL_ACK, NULL);
+ } else
+ st->l1.requestpull = !0;
+ break;
+ }
+}
+
+static void
+check_ph_act(struct IsdnCardState *sp)
+{
+ struct PStack *st = sp->stlist;
+
+ while (st) {
+ if (st->l1.act_state)
+ return;
+ st = st->next;
+ }
+ sp->ph_active = 0;
+}
+
+static void
+teles_manl1(struct PStack *st, int pr,
+ void *arg)
+{
+ struct IsdnCardState *sp = (struct IsdnCardState *)
+ st->l1.hardware;
+ long flags;
+
+ switch (pr) {
+ case (PH_ACTIVATE):
+ save_flags(flags);
+ cli();
+ if (sp->ph_active == 5) {
+ st->l1.act_state = 2;
+ restore_flags(flags);
+ st->l1.l1man(st, PH_ACTIVATE, NULL);
+ } else {
+ st->l1.act_state = 1;
+ if (sp->ph_active == 0)
+ restart_ph(sp);
+ restore_flags(flags);
+ }
+ break;
+ case (PH_DEACTIVATE):
+ st->l1.act_state = 0;
+ check_ph_act(sp);
+ break;
+ }
+}
+
+static void
+teles_l2l1discardq(struct PStack *st, int pr,
+ void *heldby, int releasetoo)
+{
+ struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+
+#ifdef DEBUG_MAGIC
+ if (sp->magic != 301271) {
+ printk(KERN_DEBUG "isac_discardq magic not 301271\n");
+ return;
+ }
+#endif
+
+ BufQueueDiscard(&sp->sq, pr, heldby, releasetoo);
+}
+
+void
+setstack_teles(struct PStack *st, struct IsdnCardState *sp)
+{
+ st->l1.hardware = sp;
+ st->l1.sbufpool = &(sp->sbufpool);
+ st->l1.rbufpool = &(sp->rbufpool);
+ st->l1.smallpool = &(sp->smallpool);
+ st->protocol = sp->teistack->protocol;
+
+ setstack_tei(st);
+
+ st->l1.stlistp = &(sp->stlist);
+ st->l1.act_state = 0;
+ st->l2.l2l1 = teles_l2l1;
+ st->l2.l2l1discardq = teles_l2l1discardq;
+ st->ma.manl1 = teles_manl1;
+ st->l1.requestpull = 0;
+}
+
+void
+init_hscxstate(struct IsdnCardState *sp,
+ int hscx)
+{
+ struct HscxState *hsp = sp->hs + hscx;
+
+ hsp->sp = sp;
+ hsp->hscx = hscx;
+ hsp->membase = sp->membase;
+ hsp->iobase = sp->iobase;
+
+ hsp->tqueue.next = 0;
+ hsp->tqueue.sync = 0;
+ hsp->tqueue.routine = (void *) (void *) hscx_bh;
+ hsp->tqueue.data = hsp;
+
+ hsp->inuse = 0;
+ hsp->init = 0;
+ hsp->active = 0;
+
+#ifdef DEBUG_MAGIC
+ hsp->magic = 301270;
+#endif
+}
+
+void
+initcard(int cardnr)
+{
+ struct IsdnCardState *sp;
+ struct IsdnCard *card = cards + cardnr;
+
+ sp = (struct IsdnCardState *)
+ Smalloc(sizeof(struct IsdnCardState), GFP_KERNEL,
+ "struct IsdnCardState");
+
+ sp->membase = card->membase;
+ sp->iobase = card->iobase;
+ sp->cardnr = cardnr;
+
+ BufPoolInit(&sp->sbufpool, ISAC_SBUF_ORDER, ISAC_SBUF_BPPS,
+ ISAC_SBUF_MAXPAGES);
+ BufPoolInit(&sp->rbufpool, ISAC_RBUF_ORDER, ISAC_RBUF_BPPS,
+ ISAC_RBUF_MAXPAGES);
+ BufPoolInit(&sp->smallpool, ISAC_SMALLBUF_ORDER, ISAC_SMALLBUF_BPPS,
+ ISAC_SMALLBUF_MAXPAGES);
+
+ sp->dlogspace = Smalloc(4096, GFP_KERNEL, "dlogspace");
+
+ initisac(card->membase, card->iobase);
+
+ sp->rcvibh = NULL;
+ sp->rcvptr = 0;
+ sp->xmtibh = NULL;
+ sp->sendptr = 0;
+ sp->event = 0;
+ sp->tqueue.next = 0;
+ sp->tqueue.sync = 0;
+ sp->tqueue.routine = (void *) (void *) isac_bh;
+ sp->tqueue.data = sp;
+
+ BufQueueInit(&sp->rq);
+ BufQueueInit(&sp->sq);
+
+ sp->stlist = NULL;
+
+ sp->ph_active = 0;
+
+ sp->dlogflag = 0;
+ sp->debug = 0;
+
+ sp->releasebuf = 0;
+#ifdef DEBUG_MAGIC
+ sp->magic = 301271;
+#endif
+
+ cards[sp->cardnr].sp = sp;
+
+ init_hscxstate(sp, 0);
+ init_hscxstate(sp, 1);
+
+ modehscx(sp->hs, 0, 0);
+ modehscx(sp->hs + 1, 0, 0);
+
+ WRITEISAC(sp->membase, sp->iobase, ISAC_MASK, 0x0);
+}
+
+static int
+get_irq(int cardnr)
+{
+ struct IsdnCard *card = cards + cardnr;
+ long flags;
+
+ save_flags(flags);
+ cli();
+ if (request_irq(card->interrupt, &teles_interrupt,
+ SA_INTERRUPT, "teles", NULL)) {
+ printk(KERN_WARNING "Teles couldn't get interrupt %d\n",
+ card->interrupt);
+ restore_flags(flags);
+ return (!0);
+ }
+ irq2dev_map[card->interrupt] = (void *) card->sp;
+ restore_flags(flags);
+ return (0);
+}
+
+static void
+release_irq(int cardnr)
+{
+ struct IsdnCard *card = cards + cardnr;
+
+ irq2dev_map[card->interrupt] = NULL;
+ free_irq(card->interrupt, NULL);
+}
+
+void
+close_hscxstate(struct HscxState *hs)
+{
+ modehscx(hs, 0, 0);
+ hs->inuse = 0;
+
+ if (hs->init) {
+ BufPoolFree(&hs->smallpool);
+ BufPoolFree(&hs->rbufpool);
+ BufPoolFree(&hs->sbufpool);
+ }
+ hs->init = 0;
+}
+
+void
+closecard(int cardnr)
+{
+ struct IsdnCardState *sp = cards[cardnr].sp;
+
+ cards[cardnr].sp = NULL;
+
+ Sfree(sp->dlogspace);
+
+ BufPoolFree(&sp->smallpool);
+ BufPoolFree(&sp->rbufpool);
+ BufPoolFree(&sp->sbufpool);
+
+ close_hscxstate(sp->hs + 1);
+ close_hscxstate(sp->hs);
+
+ if (cards[cardnr].iobase)
+ if (cards[cardnr].membase) { /* 16.0 */
+ release_region(cards[cardnr].iobase, 8);
+ } else {
+ release_region(cards[cardnr].iobase, 16);
+ release_region(cards[cardnr].iobase - 0xC00, 32);
+ release_region(cards[cardnr].iobase - 0x800, 32);
+ release_region(cards[cardnr].iobase - 0x400, 32);
+ }
+
+ Sfree((void *) sp);
+}
+
+void
+teles_shiftcards(int idx)
+{
+ int i;
+
+ for (i = idx; i < 15; i++)
+ memcpy(&cards[i],&cards[i+1],sizeof(cards[i]));
+}
+
+int
+teles_inithardware(void)
+{
+ int foundcards = 0;
+ int i = 0;
+
+ while (i < nrcards) {
+ if (!cards[i].protocol)
+ break;
+ switch (checkcard(i)) {
+ case (0):
+ initcard(i);
+ if (get_irq(i)) {
+ closecard(i);
+ teles_shiftcards(i);
+ } else {
+ foundcards++;
+ i++;
+ }
+ break;
+ case (-1):
+ teles_shiftcards(i);
+ break;
+ case (-2):
+ release_region(cards[i].iobase, 8);
+ printk(KERN_WARNING "NO Teles card found at 0x%x!\n", cards[i].iobase);
+ teles_shiftcards(i);
+ break;
+ }
+ }
+ return foundcards;
+}
+
+void
+teles_closehardware(void)
+{
+ int i;
+
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].sp) {
+ release_irq(i);
+ closecard(i);
+ }
+}
+
+static void
+hscx_l2l1(struct PStack *st, int pr,
+ struct BufHeader *ibh)
+{
+ struct IsdnCardState *sp = (struct IsdnCardState *)
+ st->l1.hardware;
+ struct HscxState *hsp = sp->hs + st->l1.hscx;
+ long flags;
+
+ switch (pr) {
+ case (PH_DATA):
+ save_flags(flags);
+ cli();
+ if (hsp->xmtibh) {
+ BufQueueLink(&hsp->sq, ibh);
+ restore_flags(flags);
+ }
+ else {
+ restore_flags(flags);
+ hsp->xmtibh = ibh;
+ hsp->sendptr = 0;
+ hsp->releasebuf = !0;
+ hscx_fill_fifo(hsp);
+ }
+ break;
+ case (PH_DATA_PULLED):
+ if (hsp->xmtibh) {
+ printk(KERN_DEBUG "hscx_l2l1: this shouldn't happen\n");
+ break;
+ }
+ hsp->xmtibh = ibh;
+ hsp->sendptr = 0;
+ hsp->releasebuf = 0;
+ hscx_fill_fifo(hsp);
+ break;
+ case (PH_REQUEST_PULL):
+ if (!hsp->xmtibh) {
+ st->l1.requestpull = 0;
+ st->l1.l1l2(st, PH_PULL_ACK, NULL);
+ } else
+ st->l1.requestpull = !0;
+ break;
+ }
+
+}
+
+extern struct IsdnBuffers *tracebuf;
+
+static void
+hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
+ int releasetoo)
+{
+ struct IsdnCardState *sp = (struct IsdnCardState *)
+ st->l1.hardware;
+ struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+#ifdef DEBUG_MAGIC
+ if (hsp->magic != 301270) {
+ printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
+ return;
+ }
+#endif
+
+ BufQueueDiscard(&hsp->sq, pr, heldby, releasetoo);
+}
+
+static int
+open_hscxstate(struct IsdnCardState *sp,
+ int hscx)
+{
+ struct HscxState *hsp = sp->hs + hscx;
+
+ if (!hsp->init) {
+ BufPoolInit(&hsp->sbufpool, HSCX_SBUF_ORDER, HSCX_SBUF_BPPS,
+ HSCX_SBUF_MAXPAGES);
+ BufPoolInit(&hsp->rbufpool, HSCX_RBUF_ORDER, HSCX_RBUF_BPPS,
+ HSCX_RBUF_MAXPAGES);
+ BufPoolInit(&hsp->smallpool, HSCX_SMALLBUF_ORDER, HSCX_SMALLBUF_BPPS,
+ HSCX_SMALLBUF_MAXPAGES);
+ }
+ hsp->init = !0;
+
+ BufQueueInit(&hsp->rq);
+ BufQueueInit(&hsp->sq);
+
+ hsp->releasebuf = 0;
+ hsp->rcvibh = NULL;
+ hsp->xmtibh = NULL;
+ hsp->rcvptr = 0;
+ hsp->sendptr = 0;
+ hsp->event = 0;
+ return (0);
+}
+
+static void
+hscx_manl1(struct PStack *st, int pr,
+ void *arg)
+{
+ struct IsdnCardState *sp = (struct IsdnCardState *)
+ st->l1.hardware;
+ struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+ switch (pr) {
+ case (PH_ACTIVATE):
+ hsp->active = !0;
+ modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
+ st->l1.l1man(st, PH_ACTIVATE, NULL);
+ break;
+ case (PH_DEACTIVATE):
+ if (!hsp->xmtibh)
+ modehscx(hsp, 0, 0);
+
+ hsp->active = 0;
+ break;
+ }
+}
+
+int
+setstack_hscx(struct PStack *st, struct HscxState *hs)
+{
+ if (open_hscxstate(st->l1.hardware, hs->hscx))
+ return (-1);
+
+ st->l1.hscx = hs->hscx;
+ st->l2.l2l1 = hscx_l2l1;
+ st->ma.manl1 = hscx_manl1;
+ st->l2.l2l1discardq = hscx_l2l1discardq;
+
+ st->l1.sbufpool = &hs->sbufpool;
+ st->l1.rbufpool = &hs->rbufpool;
+ st->l1.smallpool = &hs->smallpool;
+ st->l1.act_state = 0;
+ st->l1.requestpull = 0;
+
+ hs->st = st;
+ return (0);
+}
+
+void
+teles_reportcard(int cardnr)
+{
+ printk(KERN_DEBUG "teles_reportcard\n");
+}
diff --git a/drivers/isdn/teles/config.c b/drivers/isdn/teles/config.c
new file mode 100644
index 000000000..86d29afa5
--- /dev/null
+++ b/drivers/isdn/teles/config.c
@@ -0,0 +1,48 @@
+/* $Id: config.c,v 1.1 1996/04/13 10:23:11 fritz Exp $
+ *
+ * $Log: config.c,v $
+ * Revision 1.1 1996/04/13 10:23:11 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include "teles.h"
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ *
+ * { membase,irq,portbase,protocol,NULL }
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6
+ *
+ * Cards which don't have an io port (Teles 8 bit cards for
+ * example) can be entered with io port 0x0
+ *
+ * For the Teles 16.3, membase has to be set to 0.
+ *
+ */
+
+struct IsdnCard cards[] =
+{
+ {0xd0000, 15, 0xd80, ISDN_PTYPE_EURO, NULL}, /* example */
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+ {0, 0, 0, 0, NULL},
+};
diff --git a/drivers/isdn/teles/fsm.c b/drivers/isdn/teles/fsm.c
new file mode 100644
index 000000000..c0b2f494b
--- /dev/null
+++ b/drivers/isdn/teles/fsm.c
@@ -0,0 +1,159 @@
+/* $Id: fsm.c,v 1.2 1996/04/29 22:49:57 fritz Exp $
+ *
+ * $Log: fsm.c,v $
+ * Revision 1.2 1996/04/29 22:49:57 fritz
+ * Removed compatibility-macros.
+ *
+ * Revision 1.1 1996/04/13 10:23:41 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "teles.h"
+
+void
+FsmNew(struct Fsm *fsm,
+ struct FsmNode *fnlist, int fncount)
+{
+ int i;
+
+ fsm->jumpmatrix = (int *) Smalloc(4L * fsm->state_count * fsm->event_count,
+ GFP_KERNEL, "Fsm jumpmatrix");
+ memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
+
+ for (i = 0; i < fncount; i++)
+ fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+ fnlist[i].state] = (int) fnlist[i].routine;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+ Sfree((void *) fsm->jumpmatrix);
+}
+
+int
+FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+ void (*r) (struct FsmInst *, int, void *);
+ char str[80];
+
+ r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+ if (r) {
+ if (fi->debug) {
+ sprintf(str, "State %s Event %s",
+ fi->fsm->strState[fi->state],
+ fi->fsm->strEvent[event]);
+ fi->printdebug(fi, str);
+ }
+ r(fi, event, arg);
+ return (0);
+ } else {
+ if (fi->debug) {
+ sprintf(str, "State %s Event %s no routine",
+ fi->fsm->strState[fi->state],
+ fi->fsm->strEvent[event]);
+ fi->printdebug(fi, str);
+ }
+ return (!0);
+ }
+}
+
+void
+FsmChangeState(struct FsmInst *fi, int newstate)
+{
+ char str[80];
+
+ fi->state = newstate;
+ if (fi->debug) {
+ sprintf(str, "ChangeState %s",
+ fi->fsm->strState[newstate]);
+ fi->printdebug(fi, str);
+ }
+}
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+ FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+ ft->fi = fi;
+ ft->tl.function = (void *) FsmExpireTimer;
+ ft->tl.data = (long) ft;
+ init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+ long flags;
+
+#if 0
+ if (ft->fi->debug) {
+ sprintf(str, "FsmDelTimer %lx %d", ft, where);
+ ft->fi->printdebug(ft->fi, str);
+ }
+#endif
+
+ save_flags(flags);
+ cli();
+ if (ft->tl.next)
+ del_timer(&ft->tl);
+ restore_flags(flags);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+ int millisec, int event, void *arg, int where)
+{
+
+#if 0
+ if (ft->fi->debug) {
+ sprintf(str, "FsmAddTimer %lx %d %d", ft, millisec, where);
+ ft->fi->printdebug(ft->fi, str);
+ }
+#endif
+
+ if (ft->tl.next) {
+ printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+ return -1;
+ }
+ init_timer(&ft->tl);
+ ft->event = event;
+ ft->arg = arg;
+ ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+ add_timer(&ft->tl);
+ return 0;
+}
+
+int
+FsmTimerRunning(struct FsmTimer *ft)
+{
+ return (ft->tl.next != NULL);
+}
+
+void
+jiftime(char *s, long mark)
+{
+ s += 8;
+
+ *s-- = '\0';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = '.';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 6 + '0';
+ mark /= 6;
+ *s-- = ':';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 10 + '0';
+}
diff --git a/drivers/isdn/teles/isdnl2.c b/drivers/isdn/teles/isdnl2.c
new file mode 100644
index 000000000..779b7b70a
--- /dev/null
+++ b/drivers/isdn/teles/isdnl2.c
@@ -0,0 +1,1317 @@
+/* $Id: isdnl2.c,v 1.2 1996/05/17 03:46:15 fritz Exp $
+ *
+ * $Log: isdnl2.c,v $
+ * Revision 1.2 1996/05/17 03:46:15 fritz
+ * General cleanup.
+ *
+ * Revision 1.1 1996/04/13 10:24:16 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "teles.h"
+
+#define TIMER_1 2000
+
+static void l2m_debug(struct FsmInst *fi, char *s);
+
+struct Fsm l2fsm =
+{NULL, 0, 0};
+
+enum {
+ ST_L2_1,
+ ST_L2_3,
+ ST_L2_4,
+ ST_L2_5,
+ ST_L2_6,
+ ST_L2_7,
+ ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+static char *strL2State[] =
+{
+ "ST_L2_1",
+ "ST_L2_3",
+ "ST_L2_4",
+ "ST_L2_5",
+ "ST_L2_6",
+ "ST_L2_7",
+ "ST_L2_8",
+};
+
+enum {
+ EV_L2_UI,
+ EV_L2_SABMX,
+ EV_L2_UA,
+ EV_L2_DISC,
+ EV_L2_I,
+ EV_L2_RR,
+ EV_L2_REJ,
+ EV_L2_FRMR,
+ EV_L2_DL_DATA,
+ EV_L2_DL_ESTABLISH,
+ EV_L2_MDL_ASSIGN,
+ EV_L2_DL_UNIT_DATA,
+ EV_L2_DL_RELEASE,
+ EV_L2_MDL_NOTEIPROC,
+ EV_L2_T200,
+ EV_L2_ACK_PULL,
+ EV_L2_T203,
+ EV_L2_RNR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_RNR+1)
+
+static char *strL2Event[] =
+{
+ "EV_L2_UI",
+ "EV_L2_SABMX",
+ "EV_L2_UA",
+ "EV_L2_DISC",
+ "EV_L2_I",
+ "EV_L2_RR",
+ "EV_L2_REJ",
+ "EV_L2_FRMR",
+ "EV_L2_DL_DATA",
+ "EV_L2_DL_ESTABLISH",
+ "EV_L2_MDL_ASSIGN",
+ "EV_L2_DL_UNIT_DATA",
+ "EV_L2_DL_RELEASE",
+ "EV_L2_MDL_NOTEIPROC",
+ "EV_L2_T200",
+ "EV_L2_ACK_PULL",
+ "EV_L2_T203",
+ "EV_L2_RNR",
+};
+
+int errcount = 0;
+
+static int l2addrsize(struct Layer2 *tsp);
+
+static int
+cansend(struct PStack *st)
+{
+ int p1;
+
+ p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
+ return (st->l2.vs != p1);
+}
+
+static void
+discard_i_queue(struct PStack *st)
+{
+ struct BufHeader *ibh;
+
+ while (!BufQueueUnlink(&ibh, &st->l2.i_queue))
+ BufPoolRelease(ibh);
+}
+
+int
+l2headersize(struct Layer2 *tsp, int UI)
+{
+ return ((tsp->extended && (!UI) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
+}
+
+int
+l2addrsize(struct Layer2 *tsp)
+{
+ return (tsp->laptype == LAPD ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *tsp,
+ struct BufHeader *ibh, int rsp)
+{
+ byte *ptr = DATAPTR(ibh);
+ int crbit;
+
+ if (tsp->laptype == LAPD) {
+ crbit = rsp;
+ if (!tsp->orig)
+ crbit = !crbit;
+ *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
+ *ptr++ = (tsp->tei << 1) | 1;
+ return (2);
+ } else {
+ crbit = rsp;
+ if (tsp->orig)
+ crbit = !crbit;
+ if (crbit)
+ *ptr++ = 1;
+ else
+ *ptr++ = 3;
+ return (1);
+ }
+}
+
+static void
+enqueue_ui(struct PStack *st,
+ struct BufHeader *ibh)
+{
+ st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static void
+enqueue_super(struct PStack *st,
+ struct BufHeader *ibh)
+{
+ st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static int
+legalnr(struct PStack *st, int nr)
+{
+ struct Layer2 *l2 = &st->l2;
+ int lnr, lvs;
+
+ lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8);
+ lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8);
+ return (lnr <= lvs);
+}
+
+static void
+setva(struct PStack *st, int nr)
+{
+ struct Layer2 *l2 = &st->l2;
+
+ if (l2->va != nr) {
+ while (l2->va != nr) {
+ l2->va = (l2->va + 1) % (l2->extended ? 128 : 8);
+ BufPoolRelease(l2->windowar[l2->sow]);
+ l2->sow = (l2->sow + 1) % l2->window;
+ }
+ if (st->l4.l2writewakeup)
+ st->l4.l2writewakeup(st);
+ }
+}
+
+static void
+l2s1(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l2.l2tei(st, MDL_ASSIGN, (void *)st->l2.ces);
+ FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2s2(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+
+ byte *ptr;
+ int i;
+
+ i = sethdraddr(&(st->l2), ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ *ptr = 0x3;
+
+ enqueue_ui(st, ibh);
+}
+
+static void
+l2s3(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+
+ st->l2.l2l3(st, DL_UNIT_DATA, ibh);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh;
+ int i;
+ byte *ptr;
+
+ FsmChangeState(fi, ST_L2_5);
+ st->l2.rc = 0;
+
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 1");
+
+
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+ return;
+ i = sethdraddr(&st->l2, ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ if (st->l2.extended)
+ *ptr = 0x7f;
+ else
+ *ptr = 0x3f;
+ ibh->datasize = i + 1;
+
+ enqueue_super(st, ibh);
+}
+
+static void
+l2s11(struct FsmInst *fi, int event, void *arg)
+{
+ establishlink(fi);
+}
+
+static void
+l2s13(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct Channel *chanp = st->l4.userdata;
+ byte *ptr;
+ struct BufHeader *ibh;
+ int i;
+
+ FsmChangeState(fi, ST_L2_6);
+
+ FsmDelTimer(&st->l2.t203_timer, 1);
+ if (st->l2.t200_running) {
+ FsmDelTimer(&st->l2.t200_timer, 2);
+ st->l2.t200_running = 0;
+ }
+ st->l2.rc = 0;
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 2");
+
+
+ if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+ goto nodisc;
+
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 9))
+ return;
+ i = sethdraddr(&(st->l2), ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ *ptr = 0x53;
+ ibh->datasize = i + 1;
+ enqueue_super(st, ibh);
+
+ nodisc:
+ discard_i_queue(st);
+}
+
+static void
+l2s12(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ byte *ptr;
+ int i;
+
+ BufPoolRelease(ibh);
+ st->l2.vs = 0;
+ st->l2.va = 0;
+ st->l2.vr = 0;
+ st->l2.sow = 0;
+ FsmChangeState(fi, ST_L2_7);
+ if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 3");
+
+ st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+ return;
+ i = sethdraddr(&(st->l2), ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ *ptr = 0x73;
+ ibh->datasize = i + 1;
+ enqueue_super(st, ibh);
+
+}
+
+static void
+l2s14(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ struct Channel *chanp = st->l4.userdata;
+ byte *ptr;
+ int i, p;
+
+ ptr = DATAPTR(ibh);
+ ptr += l2addrsize(&(st->l2));
+ p = (*ptr) & 0x10;
+ BufPoolRelease(ibh);
+
+ FsmChangeState(fi, ST_L2_4);
+
+ FsmDelTimer(&st->l2.t203_timer, 3);
+ if (st->l2.t200_running) {
+ FsmDelTimer(&st->l2.t200_timer, 4);
+ st->l2.t200_running = 0;
+ }
+ if ((chanp->impair == 1) && (st->l2.laptype == LAPB))
+ goto noresponse;
+
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 11))
+ return;
+ i = sethdraddr(&(st->l2), ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ *ptr = 0x63 | (p ? 0x10 : 0x0);
+ ibh->datasize = i + 1;
+ enqueue_super(st, ibh);
+
+ noresponse:
+ st->l2.l2man(st, DL_RELEASE, NULL);
+
+}
+
+static void
+l2s5(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ int f;
+ byte *data;
+
+ data = DATAPTR(ibh);
+ data += l2addrsize(&(st->l2));
+
+ f = *data & 0x10;
+ BufPoolRelease(ibh);
+
+ if (f) {
+ st->l2.vs = 0;
+ st->l2.va = 0;
+ st->l2.vr = 0;
+ st->l2.sow = 0;
+ FsmChangeState(fi, ST_L2_7);
+
+ FsmDelTimer(&st->l2.t200_timer, 5);
+ if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 4");
+
+
+ st->l2.l2man(st, DL_ESTABLISH, NULL);
+ }
+}
+
+static void
+l2s15(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ int f;
+ byte *data;
+
+ data = DATAPTR(ibh);
+ data += l2addrsize(&st->l2);
+
+ f = *data & 0x10;
+ BufPoolRelease(ibh);
+
+ if (f) {
+ FsmDelTimer(&st->l2.t200_timer, 6);
+ FsmChangeState(fi, ST_L2_4);
+ st->l2.l2man(st, DL_RELEASE, NULL);
+ }
+}
+
+static void
+l2s6(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct Channel *chanp = st->l4.userdata;
+ struct BufHeader *ibh = arg;
+ int p, i, seq, rsp;
+ byte *ptr;
+ struct Layer2 *l2;
+
+ l2 = &st->l2;
+ ptr = DATAPTR(ibh);
+
+ if (l2->laptype == LAPD) {
+ rsp = ptr[0] & 0x2;
+ if (l2->orig)
+ rsp = !rsp;
+ } else {
+ rsp = ptr[0] == 0x3;
+ if (l2->orig)
+ rsp = !rsp;
+ }
+
+ ptr += l2addrsize(l2);
+
+ if (l2->extended) {
+ p = (ptr[1] & 0x1) == 0x1;
+ seq = ptr[1] >> 1;
+ } else {
+ p = (ptr[0] & 0x10);
+ seq = (ptr[0] >> 5) & 0x7;
+ }
+ BufPoolRelease(ibh);
+
+ if ((chanp->impair == 4) && (st->l2.laptype == LAPB))
+ goto noresp;
+
+ if ((!rsp) && p) {
+ if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
+ i = sethdraddr(l2, ibh, !0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+
+ if (l2->extended) {
+ *ptr++ = 0x1;
+ *ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+ i += 2;
+ } else {
+ *ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
+ i += 1;
+ }
+ ibh->datasize = i;
+ enqueue_super(st, ibh);
+ }
+ }
+ noresp:
+ if (legalnr(st, seq))
+ if (seq == st->l2.vs) {
+ setva(st, seq);
+ FsmDelTimer(&st->l2.t200_timer, 7);
+ st->l2.t200_running = 0;
+ FsmDelTimer(&st->l2.t203_timer, 8);
+ if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 5))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 5");
+
+ if (st->l2.i_queue.head)
+ st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+ } else if (st->l2.va != seq) {
+ setva(st, seq);
+ FsmDelTimer(&st->l2.t200_timer, 9);
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 6");
+
+ if (st->l2.i_queue.head)
+ st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+ }
+}
+
+static void
+l2s7(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ int i;
+ byte *ptr;
+ struct IsdnCardState *sp = st->l1.hardware;
+ char str[64];
+
+ i = sethdraddr(&st->l2, ibh, 0);
+ ptr = DATAPTR(ibh);
+
+ if (st->l2.laptype == LAPD)
+ if (sp->dlogflag) {
+ sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+ dlogframe(sp, ptr + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ str);
+ }
+ BufQueueLink(&st->l2.i_queue, ibh);
+
+ st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static void
+l2s8(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct Channel *chanp = st->l4.userdata;
+ struct BufHeader *ibh = arg;
+ byte *ptr;
+ struct BufHeader *ibh2;
+ struct IsdnCardState *sp = st->l1.hardware;
+ struct Layer2 *l2 = &(st->l2);
+ int i, p, seq, nr, wasok;
+ char str[64];
+
+ ptr = DATAPTR(ibh);
+ ptr += l2addrsize(l2);
+ if (l2->extended) {
+ p = (ptr[1] & 0x1) == 0x1;
+ seq = ptr[0] >> 1;
+ nr = (ptr[1] >> 1) & 0x7f;
+ } else {
+ p = (ptr[0] & 0x10);
+ seq = (ptr[0] >> 1) & 0x7;
+ nr = (ptr[0] >> 5) & 0x7;
+ }
+
+ if (l2->vr == seq) {
+ wasok = !0;
+
+ l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8);
+ l2->rejexp = 0;
+
+ ptr = DATAPTR(ibh);
+ if (st->l2.laptype == LAPD)
+ if (sp->dlogflag) {
+ sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
+ dlogframe(st->l1.hardware, ptr + l2->ihsize,
+ ibh->datasize - l2->ihsize, str);
+ }
+ label8_1:
+ if ((chanp->impair == 3) && (st->l2.laptype == LAPB))
+ goto noRR;
+
+ if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 13)) {
+ i = sethdraddr(&(st->l2), ibh2, p);
+ ptr = DATAPTR(ibh2);
+ ptr += i;
+
+ if (l2->extended) {
+ *ptr++ = 0x1;
+ *ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+ i += 2;
+ } else {
+ *ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
+ i += 1;
+ }
+ ibh2->datasize = i;
+ enqueue_super(st, ibh2);
+ noRR:
+ }
+ } else {
+ /* n(s)!=v(r) */
+ wasok = 0;
+ BufPoolRelease(ibh);
+ if (st->l2.rejexp) {
+ if (p)
+ goto label8_1;
+ } else {
+ st->l2.rejexp = !0;
+ if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 14)) {
+ i = sethdraddr(&(st->l2), ibh2, p);
+ ptr = DATAPTR(ibh2);
+ ptr += i;
+
+ if (l2->extended) {
+ *ptr++ = 0x9;
+ *ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+ i += 2;
+ } else {
+ *ptr++ = (l2->vr << 5) | 0x9 | (p ? 0x10 : 0x0);
+ i += 1;
+ }
+ ibh2->datasize = i;
+ enqueue_super(st, ibh2);
+ }
+ }
+ }
+
+ if (legalnr(st, nr))
+ if (nr == st->l2.vs) {
+ setva(st, nr);
+ FsmDelTimer(&st->l2.t200_timer, 10);
+ st->l2.t200_running = 0;
+ FsmDelTimer(&st->l2.t203_timer, 11);
+ if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 5");
+
+ if (st->l2.i_queue.head)
+ st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+ } else if (nr != st->l2.va) {
+ setva(st, nr);
+ FsmDelTimer(&st->l2.t200_timer, 12);
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 6");
+
+ if (st->l2.i_queue.head)
+ st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+ }
+ if (wasok)
+ st->l2.l2l3(st, DL_DATA, ibh);
+
+}
+
+static void
+l2s17(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l2.tei = (int) arg;
+ establishlink(fi);
+}
+
+static void
+enquiry_response(struct PStack *st)
+{
+ struct BufHeader *ibh2;
+ int i;
+ byte *ptr;
+ struct Layer2 *l2;
+
+ l2 = &st->l2;
+ if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 16)) {
+ i = sethdraddr(&(st->l2), ibh2, !0);
+ ptr = DATAPTR(ibh2);
+ ptr += i;
+
+ if (l2->extended) {
+ *ptr++ = 0x1;
+ *ptr++ = (l2->vr << 1) | 0x1;
+ i += 2;
+ } else {
+ *ptr++ = (l2->vr << 5) | 0x1 | 0x10;
+ i += 1;
+ }
+ ibh2->datasize = i;
+ enqueue_super(st, ibh2);
+ }
+}
+
+static void
+invoke_retransmission(struct PStack *st, int nr)
+{
+ struct Layer2 *l2 = &st->l2;
+ int p1;
+
+ if (l2->vs != nr) {
+ while (l2->vs != nr) {
+
+ l2->vs = l2->vs - 1;
+ if (l2->vs < 0)
+ l2->vs += l2->extended ? 128 : 8;
+
+ p1 = l2->vs - l2->va;
+ if (p1 < 0)
+ p1 += l2->extended ? 128 : 8;
+ p1 = (p1 + l2->sow) % l2->window;
+
+ BufQueueLinkFront(&l2->i_queue, l2->windowar[p1]);
+ }
+ st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+ }
+}
+
+static void
+l2s16(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ int p, seq, rsp;
+ byte *ptr;
+ struct Layer2 *l2;
+
+ l2 = &(st->l2);
+ ptr = DATAPTR(ibh);
+
+ if (l2->laptype == LAPD) {
+ rsp = ptr[0] & 0x2;
+ if (l2->orig)
+ rsp = !rsp;
+ } else {
+ rsp = ptr[0] == 0x3;
+ if (l2->orig)
+ rsp = !rsp;
+ }
+
+
+ ptr += l2addrsize(l2);
+
+ if (l2->extended) {
+ p = (ptr[1] & 0x1) == 0x1;
+ seq = ptr[1] >> 1;
+ } else {
+ p = (ptr[0] & 0x10);
+ seq = (ptr[0] >> 5) & 0x7;
+ }
+ BufPoolRelease(ibh);
+
+ if ((!rsp) && p)
+ enquiry_response(st);
+
+ if (!legalnr(st, seq))
+ return;
+
+ setva(st, seq);
+ invoke_retransmission(st, seq);
+
+}
+
+static void
+l2s19(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2s20(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ int i;
+ struct BufHeader *ibh;
+ byte *ptr;
+
+ if (st->l2.rc == st->l2.n200) {
+ FsmChangeState(fi, ST_L2_4);
+ st->l2.l2man(st, DL_RELEASE, NULL);
+ } else {
+ st->l2.rc++;
+
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 7");
+
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+ return;
+
+ i = sethdraddr(&st->l2, ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ if (st->l2.extended)
+ *ptr = 0x7f;
+ else
+ *ptr = 0x3f;
+ ibh->datasize = i + 1;
+ enqueue_super(st, ibh);
+ }
+}
+
+static void
+l2s21(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct Channel *chanp = st->l4.userdata;
+ int i;
+ struct BufHeader *ibh;
+ byte *ptr;
+
+ if (st->l2.rc == st->l2.n200) {
+ FsmChangeState(fi, ST_L2_4);
+ st->l2.l2man(st, DL_RELEASE, NULL);
+ } else {
+ st->l2.rc++;
+
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 8");
+
+
+ if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+ goto nodisc;
+
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+ return;
+
+ i = sethdraddr(&st->l2, ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ *ptr = 0x53;
+ ibh->datasize = i + 1;
+ enqueue_super(st, ibh);
+ nodisc:
+
+ }
+}
+
+static void
+l2s22(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh;
+ struct Layer2 *l2 = &st->l2;
+ byte *ptr;
+ int p1;
+
+ if (!cansend(st))
+ return;
+
+ if (BufQueueUnlink(&ibh, &l2->i_queue))
+ return;
+
+
+ p1 = l2->vs - l2->va;
+ if (p1 < 0)
+ p1 += l2->extended ? 128 : 8;
+ p1 = (p1 + l2->sow) % l2->window;
+ l2->windowar[p1] = ibh;
+
+ ptr = DATAPTR(ibh);
+ ptr += l2addrsize(l2);
+
+ if (l2->extended) {
+ *ptr++ = l2->vs << 1;
+ *ptr++ = (l2->vr << 1) | 0x1;
+ l2->vs = (l2->vs + 1) % 128;
+ } else {
+ *ptr++ = (l2->vr << 5) | (l2->vs << 1) | 0x10;
+ l2->vs = (l2->vs + 1) % 8;
+ }
+
+ st->l2.l2l1(st, PH_DATA_PULLED, ibh);
+
+ if (!st->l2.t200_running) {
+ FsmDelTimer(&st->l2.t203_timer, 13);
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 9");
+
+ st->l2.t200_running = !0;
+ }
+ if (l2->i_queue.head && cansend(st))
+ st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+
+}
+
+static void
+transmit_enquiry(struct PStack *st)
+{
+ struct BufHeader *ibh;
+ byte *ptr;
+
+ if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
+ ptr = DATAPTR(ibh);
+ ptr += sethdraddr(&st->l2, ibh, 0);
+
+ if (st->l2.extended) {
+ *ptr++ = 0x1;
+ *ptr++ = (st->l2.vr << 1) | 1;
+ } else {
+ *ptr++ = (st->l2.vr << 5) | 0x11;
+ }
+ ibh->datasize = ptr - DATAPTR(ibh);
+ enqueue_super(st, ibh);
+ if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 10");
+
+ st->l2.t200_running = !0;
+ }
+}
+
+static void
+l2s23(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l2.t200_running = 0;
+
+ st->l2.rc = 1;
+ FsmChangeState(fi, ST_L2_8);
+ transmit_enquiry(st);
+}
+
+static void
+l2s24(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ int p, seq, rsp;
+ byte *ptr;
+ struct Layer2 *l2;
+
+ l2 = &st->l2;
+ ptr = DATAPTR(ibh);
+
+ if (l2->laptype == LAPD) {
+ rsp = ptr[0] & 0x2;
+ if (l2->orig)
+ rsp = !rsp;
+ } else {
+ rsp = ptr[0] == 0x3;
+ if (l2->orig)
+ rsp = !rsp;
+ }
+
+
+ ptr += l2addrsize(l2);
+
+ if (l2->extended) {
+ p = (ptr[1] & 0x1) == 0x1;
+ seq = ptr[1] >> 1;
+ } else {
+ p = (ptr[0] & 0x10);
+ seq = (ptr[0] >> 5) & 0x7;
+ }
+ BufPoolRelease(ibh);
+
+ if (rsp && p) {
+ if (legalnr(st, seq)) {
+ FsmChangeState(fi, ST_L2_7);
+ setva(st, seq);
+ if (st->l2.t200_running) {
+ FsmDelTimer(&st->l2.t200_timer, 14);
+ st->l2.t200_running = 0;
+ }
+ if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 11");
+
+ invoke_retransmission(st, seq);
+ }
+ } else {
+ if (!rsp && p)
+ enquiry_response(st);
+ if (legalnr(st, seq)) {
+ setva(st, seq);
+ }
+ }
+}
+
+static void
+l2s25(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l2.rc = 0;
+ FsmChangeState(fi, ST_L2_8);
+ transmit_enquiry(st);
+}
+
+static void
+l2s26(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (st->l2.rc == st->l2.n200) {
+ l2s13(fi, event, NULL);
+ } else {
+ st->l2.rc++;
+ transmit_enquiry(st);
+ }
+}
+
+static void
+l2s27(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ byte *ptr;
+ int i, p, est;
+
+ ptr = DATAPTR(ibh);
+ ptr += l2addrsize(&st->l2);
+
+ if (st->l2.extended)
+ p = ptr[1] & 0x1;
+ else
+ p = ptr[0] & 0x10;
+
+ BufPoolRelease(ibh);
+
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+ return;
+ i = sethdraddr(&st->l2, ibh, 0);
+ ptr = DATAPTR(ibh);
+ ptr += i;
+ *ptr = 0x63 | p;
+ ibh->datasize = i + 1;
+ enqueue_super(st, ibh);
+
+ if (st->l2.vs != st->l2.va) {
+ discard_i_queue(st);
+ est = !0;
+ } else
+ est = 0;
+
+ FsmDelTimer(&st->l2.t200_timer, 15);
+ st->l2.t200_running = 0;
+
+ if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+ if (st->l2.l2m.debug)
+ l2m_debug(&st->l2.l2m, "FAT 12");
+
+ st->l2.vs = 0;
+ st->l2.va = 0;
+ st->l2.vr = 0;
+ st->l2.sow = 0;
+
+
+ if (est)
+ st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+}
+
+static void
+l2s28(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct BufHeader *ibh = arg;
+ byte *ptr;
+ char tmp[64];
+
+ ptr = DATAPTR(ibh);
+ ptr += l2addrsize(&st->l2);
+ ptr++;
+
+ if (st->l2.l2m.debug) {
+ if (st->l2.extended)
+ sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
+ ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
+ else
+ sprintf(tmp, "FRMR information %2x %2x %2x",
+ ptr[0], ptr[1], ptr[2]);
+
+ l2m_debug(&st->l2.l2m, tmp);
+ }
+ BufPoolRelease(ibh);
+}
+
+static int
+IsUI(byte * data, int ext)
+{
+ return ((data[0] & 0xef) == 0x3);
+}
+
+static int
+IsUA(byte * data, int ext)
+{
+ return ((data[0] & 0xef) == 0x63);
+}
+
+static int
+IsDISC(byte * data, int ext)
+{
+ return ((data[0] & 0xef) == 0x43);
+}
+
+static int
+IsRR(byte * data, int ext)
+{
+ if (ext)
+ return (data[0] == 0x1);
+ else
+ return ((data[0] & 0xf) == 1);
+}
+
+static int
+IsI(byte * data, int ext)
+{
+ return ((data[0] & 0x1) == 0x0);
+}
+
+static int
+IsSABMX(byte * data, int ext)
+{
+ return (ext ? data[0] == 0x7f : data[0] == 0x3f);
+}
+
+static int
+IsREJ(byte * data, int ext)
+{
+ return (ext ? data[0] == 0x9 : (data[0] & 0xf) == 0x9);
+}
+
+static int
+IsFRMR(byte * data, int ext)
+{
+ return ((data[0] & 0xef) == 0x87);
+}
+
+static int
+IsRNR(byte * data, int ext)
+{
+ if (ext)
+ return (data[0] == 0x5);
+ else
+ return ((data[0] & 0xf) == 5);
+}
+
+static struct FsmNode L2FnList[] =
+{
+ {ST_L2_1, EV_L2_DL_ESTABLISH, l2s1},
+ {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2s19},
+ {ST_L2_3, EV_L2_MDL_ASSIGN, l2s17},
+ {ST_L2_4, EV_L2_DL_UNIT_DATA, l2s2},
+ {ST_L2_4, EV_L2_DL_ESTABLISH, l2s11},
+ {ST_L2_7, EV_L2_DL_UNIT_DATA, l2s2},
+ {ST_L2_7, EV_L2_DL_DATA, l2s7},
+ {ST_L2_7, EV_L2_DL_RELEASE, l2s13},
+ {ST_L2_7, EV_L2_ACK_PULL, l2s22},
+ {ST_L2_8, EV_L2_DL_RELEASE, l2s13},
+
+ {ST_L2_1, EV_L2_UI, l2s3},
+ {ST_L2_4, EV_L2_UI, l2s3},
+ {ST_L2_4, EV_L2_SABMX, l2s12},
+ {ST_L2_5, EV_L2_UA, l2s5},
+ {ST_L2_6, EV_L2_UA, l2s15},
+ {ST_L2_7, EV_L2_UI, l2s3},
+ {ST_L2_7, EV_L2_DISC, l2s14},
+ {ST_L2_7, EV_L2_I, l2s8},
+ {ST_L2_7, EV_L2_RR, l2s6},
+ {ST_L2_7, EV_L2_REJ, l2s16},
+ {ST_L2_7, EV_L2_SABMX, l2s27},
+ {ST_L2_7, EV_L2_FRMR, l2s28},
+ {ST_L2_8, EV_L2_RR, l2s24},
+ {ST_L2_8, EV_L2_DISC, l2s14},
+ {ST_L2_8, EV_L2_FRMR, l2s28},
+
+ {ST_L2_5, EV_L2_T200, l2s20},
+ {ST_L2_6, EV_L2_T200, l2s21},
+ {ST_L2_7, EV_L2_T200, l2s23},
+ {ST_L2_7, EV_L2_T203, l2s25},
+ {ST_L2_8, EV_L2_T200, l2s26},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static void
+isdnl2_l1l2(struct PStack *st, int pr, struct BufHeader *arg)
+{
+ struct BufHeader *ibh;
+ byte *datap;
+ int ret = !0;
+
+ switch (pr) {
+ case (PH_DATA):
+
+ ibh = arg;
+ datap = DATAPTR(ibh);
+ datap += l2addrsize(&st->l2);
+
+ if (IsI(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_I, ibh);
+ else if (IsRR(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_RR, ibh);
+ else if (IsUI(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_UI, ibh);
+ else if (IsSABMX(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, ibh);
+ else if (IsUA(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_UA, ibh);
+ else if (IsDISC(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, ibh);
+ else if (IsREJ(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, ibh);
+ else if (IsFRMR(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, ibh);
+ else if (IsRNR(datap, st->l2.extended))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, ibh);
+
+ if (ret)
+ BufPoolRelease(ibh);
+
+ break;
+ case (PH_PULL_ACK):
+ FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+ break;
+ }
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr,
+ void *arg)
+{
+ switch (pr) {
+ case (DL_DATA):
+ if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg))
+ BufPoolRelease((struct BufHeader *) arg);
+ break;
+ case (DL_UNIT_DATA):
+ if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg))
+ BufPoolRelease((struct BufHeader *) arg);
+ break;
+ }
+}
+
+static void
+isdnl2_manl2(struct PStack *st, int pr,
+ void *arg)
+{
+ switch (pr) {
+ case (DL_ESTABLISH):
+ FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+ break;
+ case (DL_RELEASE):
+ FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
+ break;
+ case (MDL_NOTEIPROC):
+ FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
+ break;
+ }
+}
+
+static void
+isdnl2_teil2(struct PStack *st, int pr,
+ void *arg)
+{
+ switch (pr) {
+ case (MDL_ASSIGN):
+ FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+ break;
+ }
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+ FsmDelTimer(&st->l2.t200_timer, 15);
+ FsmDelTimer(&st->l2.t203_timer, 16);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *s)
+{
+ struct PStack *st = fi->userdata;
+ char tm[32], str[256];
+
+ jiftime(tm, jiffies);
+ sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
+ teles_putstatus(str);
+}
+
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+ st->l1.l1l2 = isdnl2_l1l2;
+ st->l3.l3l2 = isdnl2_l3l2;
+ st->ma.manl2 = isdnl2_manl2;
+ st->ma.teil2 = isdnl2_teil2;
+
+ st->l2.uihsize = l2headersize(&st->l2, !0);
+ st->l2.ihsize = l2headersize(&st->l2, 0);
+ BufQueueInit(&(st->l2.i_queue));
+ st->l2.rejexp = 0;
+ st->l2.debug = 1;
+
+ st->l2.l2m.fsm = &l2fsm;
+ st->l2.l2m.state = ST_L2_1;
+ st->l2.l2m.debug = 0;
+ st->l2.l2m.userdata = st;
+ st->l2.l2m.printdebug = l2m_debug;
+ strcpy(st->l2.debug_id, debug_id);
+
+ FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer);
+ FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer);
+ st->l2.t200_running = 0;
+}
+
+void
+setstack_transl2(struct PStack *st)
+{
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+void
+Isdnl2New(void)
+{
+ l2fsm.state_count = L2_STATE_COUNT;
+ l2fsm.event_count = L2_EVENT_COUNT;
+ l2fsm.strEvent = strL2Event;
+ l2fsm.strState = strL2State;
+ FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+ FsmFree(&l2fsm);
+}
diff --git a/drivers/isdn/teles/isdnl3.c b/drivers/isdn/teles/isdnl3.c
new file mode 100644
index 000000000..411dc4ad4
--- /dev/null
+++ b/drivers/isdn/teles/isdnl3.c
@@ -0,0 +1,603 @@
+/* $Id: isdnl3.c,v 1.9 1996/06/06 14:22:27 fritz Exp $
+ *
+ * $Log: isdnl3.c,v $
+ * Revision 1.9 1996/06/06 14:22:27 fritz
+ * Changed level of "non-digital call..." message, since
+ * with audio support, this is quite normal.
+ *
+ * Revision 1.8 1996/06/03 20:35:04 fritz
+ * Fixed typos.
+ *
+ * Revision 1.7 1996/06/03 20:03:39 fritz
+ * Fixed typos.
+ *
+ * Revision 1.6 1996/05/21 11:33:50 keil
+ * Adding SETUP_ACKNOWLEDGE as answer of a SETUP message.
+ *
+ * Revision 1.5 1996/05/18 01:37:16 fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.4 1996/05/17 03:46:16 fritz
+ * General cleanup.
+ *
+ * Revision 1.3 1996/04/30 21:57:53 isdn4dev
+ * remove some debugging code, improve callback Karsten Keil
+ *
+ * Revision 1.2 1996/04/20 16:45:05 fritz
+ * Changed to report all incoming calls to Linklevel, not just those
+ * with Service 7.
+ * Misc. typos
+ *
+ * Revision 1.1 1996/04/13 10:24:45 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#define P_1TR6
+#include "teles.h"
+#include "l3_1TR6.h"
+#define DEBUG_1TR6 0
+
+static void
+i_down(struct PStack *st,
+ struct BufHeader *ibh)
+{
+ st->l3.l3l2(st, DL_DATA, ibh);
+}
+
+static void
+newl3state(struct PStack *st, int state)
+{
+ st->l3.state = state;
+ if (DEBUG_1TR6 > 4)
+ printk(KERN_INFO "isdnl3: bc:%d cr:%x new state %d\n",
+ st->pa->bchannel, st->pa->callref, state);
+
+}
+
+static void
+l3_message(struct PStack *st, int mt)
+{
+ struct BufHeader *dibh;
+ byte *p;
+ int size;
+
+ BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+ p = DATAPTR(dibh);
+ p += st->l2.ihsize;
+ size = st->l2.ihsize;
+
+ *p++ = 0x8;
+ *p++ = 0x1;
+ *p++ = st->l3.callref;
+ *p++ = mt;
+ size += 4;
+
+ dibh->datasize = size;
+ i_down(st, dibh);
+}
+
+static void
+l3s3(struct PStack *st, byte pr, void *arg)
+{
+ l3_message(st, MT_RELEASE);
+ newl3state(st, 19);
+}
+
+static void
+l3s4(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ newl3state(st, 0);
+ st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3s4_1(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ newl3state(st, 19);
+ l3_message(st, MT_RELEASE);
+ st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3s5(struct PStack *st, byte pr,
+ void *arg)
+{
+ struct BufHeader *dibh;
+ byte *p;
+ char *teln;
+
+ st->l3.callref = st->pa->callref;
+ BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+ p = DATAPTR(dibh);
+ p += st->l2.ihsize;
+
+ *p++ = 0x8;
+ *p++ = 0x1;
+ *p++ = st->l3.callref;
+ *p++ = MT_SETUP;
+ *p++ = 0xa1;
+
+ /*
+ * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+ */
+ switch (st->pa->info) {
+ case 1: /* Telephony */
+ *p++ = 0x4; /* BC-IE-code */
+ *p++ = 0x3; /* Length */
+ *p++ = 0x90; /* Coding Std. national, 3.1 kHz audio */
+ *p++ = 0x90; /* Circuit-Mode 64kbps */
+ *p++ = 0xa3; /* A-Law Audio */
+ break;
+ case 5: /* Datatransmission 64k, BTX */
+ case 7: /* Datatransmission 64k */
+ default:
+ *p++ = 0x4; /* BC-IE-code */
+ *p++ = 0x2; /* Length */
+ *p++ = 0x88; /* Coding Std. nat., unrestr. dig. Inform. */
+ *p++ = 0x90; /* Packet-Mode 64kbps */
+ break;
+ }
+ /*
+ * What about info2? Mapping to High-Layer-Compatibility?
+ */
+ if (st->pa->calling[0] != '\0') {
+ *p++ = 0x6c;
+ *p++ = strlen(st->pa->calling) + 1;
+ /* Classify as AnyPref. */
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+ teln = st->pa->calling;
+ while (*teln)
+ *p++ = *teln++ & 0x7f;
+ }
+ *p++ = 0x70;
+ *p++ = strlen(st->pa->called) + 1;
+ /* Classify as AnyPref. */
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+
+ teln = st->pa->called;
+ while (*teln)
+ *p++ = *teln++ & 0x7f;
+
+
+ dibh->datasize = p - DATAPTR(dibh);
+
+ newl3state(st, 1);
+ i_down(st, dibh);
+
+}
+
+static void
+l3s6(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ struct BufHeader *ibh = arg;
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ 0x18, 0))) {
+ st->pa->bchannel = p[2] & 0x3;
+ } else
+ printk(KERN_WARNING "octect 3 not found\n");
+
+ BufPoolRelease(ibh);
+ newl3state(st, 3);
+ st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3s7(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ newl3state(st, 12);
+ st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+static void
+l3s8(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+ newl3state(st, 10);
+}
+
+static void
+l3s11(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ newl3state(st, 4);
+ st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3s12(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ int bcfound = 0;
+ struct BufHeader *ibh = arg;
+
+ p = DATAPTR(ibh);
+ p += st->l2.uihsize;
+ st->pa->callref = getcallref(p);
+ st->l3.callref = 0x80 + st->pa->callref;
+
+ /*
+ * Channel Identification
+ */
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+ 0x18, 0))) {
+ st->pa->bchannel = p[2] & 0x3;
+ bcfound++ ;
+ } else
+ printk(KERN_WARNING "l3s12: Channel ident not found\n");
+
+ p = DATAPTR(ibh);
+ if (st->protocol == ISDN_PTYPE_1TR6) {
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x01, 6))) {
+ st->pa->info = p[2];
+ st->pa->info2 = p[3];
+ } else
+ printk(KERN_WARNING "l3s12(1TR6): ServiceIndicator not found\n");
+ } else {
+ /*
+ * Bearer Capabilities
+ */
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x04, 0))) {
+ switch (p[2] & 0x1f) {
+ case 0x00:
+ /* Speech */
+ case 0x10:
+ /* 3.1 Khz audio */
+ st->pa->info = 1;
+ break;
+ case 0x08:
+ /* Unrestricted digital information */
+ st->pa->info = 7;
+ break;
+ case 0x09:
+ /* Restricted digital information */
+ st->pa->info = 2;
+ break;
+ case 0x11:
+ /* Unrestr. digital information with tones/announcements */
+ st->pa->info = 3;
+ break;
+ case 0x18:
+ /* Video */
+ st->pa->info = 4;
+ break;
+ default:
+ st->pa->info = 0;
+ }
+ } else
+ printk(KERN_WARNING "l3s12: Bearer capabilities not found\n");
+ }
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+ 0x70, 0)))
+ iecpy(st->pa->called, p, 1);
+ else
+ strcpy(st->pa->called, "");
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+ 0x6c, 0))) {
+ if (st->protocol == ISDN_PTYPE_1TR6)
+ iecpy(st->pa->calling, p, 1);
+ else
+ iecpy(st->pa->calling, p, 2);
+ } else
+ strcpy(st->pa->calling, "");
+ BufPoolRelease(ibh);
+
+ if (bcfound) {
+ if (st->pa->info != 7) {
+ printk(KERN_DEBUG "non-digital call: %s -> %s\n",
+ st->pa->calling,
+ st->pa->called);
+ }
+ newl3state(st, 6);
+ st->l3.l3l4(st, CC_SETUP_IND, NULL);
+ }
+}
+
+static void
+l3s13(struct PStack *st, byte pr, void *arg)
+{
+ newl3state(st, 0);
+}
+
+static void
+l3s16(struct PStack *st, byte pr,
+ void *arg)
+{
+ st->l3.callref = 0x80 + st->pa->callref;
+ l3_message(st, MT_CONNECT);
+ newl3state(st, 8);
+}
+
+static void
+l3s17(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+ newl3state(st, 10);
+}
+
+static void
+l3s18(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *dibh;
+ byte *p;
+ int size;
+
+ BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+ p = DATAPTR(dibh);
+ p += st->l2.ihsize;
+ size = st->l2.ihsize;
+
+ *p++ = 0x8;
+ *p++ = 0x1;
+ *p++ = st->l3.callref;
+ *p++ = MT_DISCONNECT;
+ size += 4;
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = 0x90;
+ size += 4;
+
+ dibh->datasize = size;
+ i_down(st, dibh);
+
+ newl3state(st, 11);
+}
+
+static void
+l3s19(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ newl3state(st, 0);
+ l3_message(st, MT_RELEASE_COMPLETE);
+ st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3s20(struct PStack *st, byte pr,
+ void *arg)
+{
+ l3_message(st, MT_ALERTING);
+ newl3state(st, 7);
+}
+
+struct stateentry {
+ int state;
+ byte primitive;
+ void (*rout) (struct PStack *, byte, void *);
+};
+
+static struct stateentry downstatelist[] =
+{
+ {0,CC_SETUP_REQ,l3s5},
+ {1,CC_DISCONNECT_REQ,l3s18},
+ {1,CC_RELEASE_REQ,l3s3},
+ {1,CC_DLRL,l3s13},
+ {3,CC_DISCONNECT_REQ,l3s18},
+ {3,CC_RELEASE_REQ,l3s3},
+ {3,CC_DLRL,l3s13},
+ {4,CC_RELEASE_REQ,l3s3},
+ {4,CC_DISCONNECT_REQ,l3s18},
+ {4,CC_DLRL,l3s13},
+ {6,CC_RELEASE_REQ,l3s3},
+ {6,CC_DISCONNECT_REQ,l3s18},
+ {6,CC_ALERTING_REQ,l3s20},
+ {6,CC_DLRL,l3s13},
+ {7,CC_RELEASE_REQ,l3s3},
+ {7,CC_SETUP_RSP,l3s16},
+ {7,CC_DLRL,l3s13},
+ {8,CC_RELEASE_REQ,l3s3},
+ {8,CC_DISCONNECT_REQ,l3s18},
+ {8,CC_DLRL,l3s13},
+ {10,CC_DISCONNECT_REQ,l3s18},
+ {10,CC_RELEASE_REQ,l3s3},
+ {10,CC_DLRL,l3s13},
+ {11,CC_RELEASE_REQ,l3s3},
+ {12,CC_RELEASE_REQ,l3s3},
+ {19,CC_DLRL,l3s13},
+};
+
+static int downsllen = sizeof(downstatelist) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist[] =
+{
+ {0,MT_SETUP,l3s12},
+ {1,MT_CALL_PROCEEDING,l3s6},
+ {1,MT_SETUP_ACKNOWLEDGE,l3s6},
+ {1,MT_RELEASE_COMPLETE,l3s4},
+ {1,MT_RELEASE,l3s19},
+ {1,MT_DISCONNECT,l3s7},
+ {3,MT_DISCONNECT,l3s7},
+ {3,MT_CONNECT,l3s8},
+ {3,MT_ALERTING,l3s11},
+ {3,MT_RELEASE,l3s19},
+ {3,MT_RELEASE_COMPLETE,l3s4},
+ {4,MT_CONNECT,l3s8},
+ {4,MT_DISCONNECT,l3s7},
+ {4,MT_RELEASE,l3s19},
+ {4,MT_RELEASE_COMPLETE,l3s4},
+ {6,MT_SETUP,l3s12},
+ {7,MT_RELEASE,l3s19},
+ {7,MT_RELEASE_COMPLETE,l3s4_1},
+ {7,MT_DISCONNECT,l3s7},
+ {8,MT_RELEASE,l3s19},
+ {8,MT_CONNECT_ACKNOWLEDGE,l3s17},
+ {8,MT_DISCONNECT,l3s7},
+ {8,MT_RELEASE_COMPLETE,l3s4_1},
+ {10,MT_DISCONNECT,l3s7},
+ {10,MT_RELEASE,l3s19},
+ {10,MT_RELEASE_COMPLETE,l3s4_1},
+ {11,MT_RELEASE,l3s19},
+ {11,MT_RELEASE_COMPLETE,l3s4},
+ {19,MT_RELEASE_COMPLETE,l3s4},
+};
+
+static int datasllen = sizeof(datastatelist) /
+sizeof(struct stateentry);
+
+#ifdef P_1TR6
+#include "l3_1TR6.c"
+#endif
+
+static void
+l3up(struct PStack *st,
+ int pr, void *arg)
+{
+ int i, mt, size;
+ byte *ptr;
+ struct BufHeader *ibh = arg;
+
+ if (pr == DL_DATA) {
+ ptr = DATAPTR(ibh);
+ ptr += st->l2.ihsize;
+ size = ibh->datasize - st->l2.ihsize;
+ mt = ptr[3];
+ switch (ptr[0]) {
+#ifdef P_1TR6
+ case PROTO_DIS_N0:
+ BufPoolRelease(ibh);
+ break;
+ case PROTO_DIS_N1:
+ for (i = 0; i < datasl_1tr6t_len; i++)
+ if ((st->l3.state == datastatelist_1tr6t[i].state) &&
+ (mt == datastatelist_1tr6t[i].primitive))
+ break;
+ if (i == datasl_1tr6t_len) {
+ BufPoolRelease(ibh);
+ if (DEBUG_1TR6 > 0)
+ printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %x\n",
+ st->l3.state, mt);
+ } else
+ datastatelist_1tr6t[i].rout(st, pr, ibh);
+ break;
+#endif
+ default: /* E-DSS1 */
+ for (i = 0; i < datasllen; i++)
+ if ((st->l3.state == datastatelist[i].state) &&
+ (mt == datastatelist[i].primitive))
+ break;
+ if (i == datasllen) {
+ BufPoolRelease(ibh);
+ if (DEBUG_1TR6 > 0)
+ printk(KERN_INFO "isdnl3up unhandled E-DSS1 state %d MT %x\n",
+ st->l3.state, mt);
+ } else
+ datastatelist[i].rout(st, pr, ibh);
+ }
+ } else if (pr == DL_UNIT_DATA) {
+ ptr = DATAPTR(ibh);
+ ptr += st->l2.uihsize;
+ size = ibh->datasize - st->l2.uihsize;
+ mt = ptr[3];
+ switch (ptr[0]) {
+#ifdef P_1TR6
+ case PROTO_DIS_N0:
+ BufPoolRelease(ibh);
+ break;
+ case PROTO_DIS_N1:
+ for (i = 0; i < datasl_1tr6t_len; i++)
+ if ((st->l3.state == datastatelist_1tr6t[i].state) &&
+ (mt == datastatelist_1tr6t[i].primitive))
+ break;
+ if (i == datasl_1tr6t_len) {
+ if (DEBUG_1TR6 > 0) {
+ printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %x\n"
+ ,st->l3.state, mt);
+ }
+ BufPoolRelease(ibh);
+ } else
+ datastatelist_1tr6t[i].rout(st, pr, ibh);
+ break;
+#endif
+ default: /* E-DSS1 */
+ for (i = 0; i < datasllen; i++)
+ if ((st->l3.state == datastatelist[i].state) &&
+ (mt == datastatelist[i].primitive))
+ break;
+ if (i == datasllen) {
+ BufPoolRelease(ibh);
+ if (DEBUG_1TR6 > 0)
+ printk(KERN_INFO "isdnl3up unhandled E-DSS1 state %d MT %x\n",
+ st->l3.state, mt);
+ } else
+ datastatelist[i].rout(st, pr, ibh);
+ }
+ }
+}
+
+static void
+l3down(struct PStack *st,
+ int pr, void *arg)
+{
+ int i;
+ struct BufHeader *ibh = arg;
+
+ switch (st->protocol) {
+#ifdef P_1TR6
+ case ISDN_PTYPE_1TR6:
+ for (i = 0; i < downsl_1tr6t_len; i++)
+ if ((st->l3.state == downstatelist_1tr6t[i].state) &&
+ (pr == downstatelist_1tr6t[i].primitive))
+ break;
+ if (i == downsl_1tr6t_len) {
+ if (DEBUG_1TR6 > 0) {
+ printk(KERN_INFO "isdnl3down unhandled 1tr6 state %d primitive %x\n", st->l3.state, pr);
+ }
+ } else
+ downstatelist_1tr6t[i].rout(st, pr, ibh);
+ break;
+#endif
+ default:
+ for (i = 0; i < downsllen; i++)
+ if ((st->l3.state == downstatelist[i].state) &&
+ (pr == downstatelist[i].primitive))
+ break;
+ if (i == downsllen) {
+ if (DEBUG_1TR6 > 0) {
+ printk(KERN_INFO "isdnl3down unhandled E-DSS1 state %d primitive %x\n", st->l3.state, pr);
+ }
+ } else
+ downstatelist[i].rout(st, pr, ibh);
+ }
+}
+
+void
+setstack_isdnl3(struct PStack *st)
+{
+ st->l4.l4l3 = l3down;
+ st->l2.l2l3 = l3up;
+ st->l3.state = 0;
+ st->l3.callref = 0;
+ st->l3.debug = 0;
+}
diff --git a/drivers/isdn/teles/l3_1TR6.c b/drivers/isdn/teles/l3_1TR6.c
new file mode 100644
index 000000000..e3716769c
--- /dev/null
+++ b/drivers/isdn/teles/l3_1TR6.c
@@ -0,0 +1,507 @@
+/* $Id: l3_1TR6.c,v 1.4 1996/06/06 14:22:28 fritz Exp $
+ *
+ * $Log: l3_1TR6.c,v $
+ * Revision 1.4 1996/06/06 14:22:28 fritz
+ * Changed level of "non-digital call..." message, since
+ * with audio support, this is quite normal.
+ *
+ * Revision 1.3 1996/04/30 21:54:42 isdn4dev
+ * SPV, callback , remove some debugging code Karsten Keil
+ *
+ * Revision 1.2 1996/04/20 16:47:23 fritz
+ * Changed statemachine to allow reject of an incoming call.
+ * Report all incoming calls, not just those with Service = 7.
+ * Misc. typos
+ *
+ * Revision 1.1 1996/04/13 10:25:16 fritz
+ * Initial revision
+ *
+ *
+ */
+
+static void
+l3_1TR6_message(struct PStack *st, int mt, int pd)
+{
+ struct BufHeader *dibh;
+ byte *p;
+
+ BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+ p = DATAPTR(dibh);
+ p += st->l2.ihsize;
+
+ *p++ = pd;
+ *p++ = 0x1;
+ *p++ = st->l3.callref;
+ *p++ = mt;
+
+ dibh->datasize = p - DATAPTR(dibh);
+ i_down(st, dibh);
+}
+
+static void
+l3_1tr6_setup(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *dibh;
+ byte *p;
+ char *teln;
+
+ st->l3.callref = st->pa->callref;
+ BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+ p = DATAPTR(dibh);
+ p += st->l2.ihsize;
+
+ *p++ = PROTO_DIS_N1;
+ *p++ = 0x1;
+ *p++ = st->l3.callref;
+ *p++ = MT_N1_SETUP;
+
+ if ('S' == (st->pa->called[0] & 0x5f)) { /* SPV ??? */
+ /* NSF SPV */
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_SPV; /* SPV */
+ *p++ = st->pa->info; /* 0 for all Services */
+ *p++ = st->pa->info2; /* 0 for all Services */
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_Activate; /* aktiviere SPV (default) */
+ *p++ = st->pa->info; /* 0 for all Services */
+ *p++ = st->pa->info2; /* 0 for all Services */
+ }
+ if (st->pa->calling[0] != '\0') {
+ *p++ = WE0_origAddr;
+ *p++ = strlen(st->pa->calling) + 1;
+ /* Classify as AnyPref. */
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+ teln = st->pa->calling;
+ while (*teln)
+ *p++ = *teln++ & 0x7f;
+ }
+ *p++ = WE0_destAddr;
+ teln = st->pa->called;
+ if ('S' != (st->pa->called[0] & 0x5f)) { /* Keine SPV ??? */
+ *p++ = strlen(st->pa->called) + 1;
+ st->pa->spv = 0;
+ } else { /* SPV */
+ *p++ = strlen(st->pa->called);
+ teln++; /* skip S */
+ st->pa->spv = 1;
+ }
+ /* Classify as AnyPref. */
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+ while (*teln)
+ *p++ = *teln++ & 0x7f;
+
+ *p++ = WE_Shift_F6;
+ /* Codesatz 6 fuer Service */
+ *p++ = WE6_serviceInd;
+ *p++ = 2; /* len=2 info,info2 */
+ *p++ = st->pa->info;
+ *p++ = st->pa->info2;
+
+ dibh->datasize = p - DATAPTR(dibh);
+
+ newl3state(st, 1);
+ i_down(st, dibh);
+
+}
+
+static void
+l3_1tr6_tu_setup(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ struct BufHeader *ibh = arg;
+
+ p = DATAPTR(ibh);
+ p += st->l2.uihsize;
+ st->pa->callref = getcallref(p);
+ st->l3.callref = 0x80 + st->pa->callref;
+
+ /* Channel Identification */
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+ WE0_chanID, 0))) {
+ st->pa->bchannel = p[2] & 0x3;
+ } else
+ printk(KERN_INFO "l3tu_setup: Channel ident not found\n");
+
+ p = DATAPTR(ibh);
+
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, WE6_serviceInd, 6))) {
+ st->pa->info = p[2];
+ st->pa->info2 = p[3];
+ } else
+ printk(KERN_INFO "l3s12(1TR6): ServiceIndicator not found\n");
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+ WE0_destAddr, 0)))
+ iecpy(st->pa->called, p, 1);
+ else
+ strcpy(st->pa->called, "");
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+ WE0_origAddr, 0))) {
+ iecpy(st->pa->calling, p, 1);
+ } else
+ strcpy(st->pa->calling, "");
+
+ p = DATAPTR(ibh);
+ st->pa->spv = 0;
+ if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+ WE0_netSpecFac, 0))) {
+ if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
+ st->pa->spv = 1;
+ }
+ BufPoolRelease(ibh);
+
+ /* Signal all services, linklevel takes care of Service-Indicator */
+ if (st->pa->info != 7) {
+ printk(KERN_DEBUG "non-digital call: %s -> %s\n",
+ st->pa->calling,
+ st->pa->called);
+ }
+ newl3state(st, 6);
+ st->l3.l3l4(st, CC_SETUP_IND, NULL);
+}
+
+static void
+l3_1tr6_tu_setup_ack(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ struct BufHeader *ibh = arg;
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ WE0_chanID, 0))) {
+ st->pa->bchannel = p[2] & 0x3;
+ } else
+ printk(KERN_INFO "octect 3 not found\n");
+
+
+ BufPoolRelease(ibh);
+ newl3state(st, 2);
+}
+
+static void
+l3_1tr6_tu_call_sent(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ struct BufHeader *ibh = arg;
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ WE0_chanID, 0))) {
+ st->pa->bchannel = p[2] & 0x3;
+ } else
+ printk(KERN_INFO "octect 3 not found\n");
+
+ BufPoolRelease(ibh);
+ newl3state(st, 3);
+ st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3_1tr6_tu_alert(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ struct BufHeader *ibh = arg;
+
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ WE6_statusCalled, 6))) {
+ if (DEBUG_1TR6 > 2)
+ printk(KERN_INFO "status called %x\n", p[2]);
+ } else if (DEBUG_1TR6 > 0)
+ printk(KERN_INFO "statusCalled not found\n");
+
+ BufPoolRelease(ibh);
+ newl3state(st, 4);
+ st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3_1tr6_tu_info(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ int i,tmpcharge=0;
+ char a_charge[8];
+ struct BufHeader *ibh = arg;
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ WE6_chargingInfo, 6))) {
+ iecpy(a_charge, p, 1);
+ for (i = 0; i < strlen (a_charge); i++) {
+ tmpcharge *= 10;
+ tmpcharge += a_charge[i] & 0xf;
+ }
+ if (tmpcharge > st->pa->chargeinfo) {
+ st->pa->chargeinfo = tmpcharge;
+ st->l3.l3l4 (st, CC_INFO_CHARGE, NULL);
+ }
+ if (DEBUG_1TR6 > 2)
+ printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
+ } else if (DEBUG_1TR6 > 2)
+ printk(KERN_INFO "chargingInfo not found\n");
+
+ BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_tu_info_s2(struct PStack *st, byte pr, void *arg)
+{
+ byte *p;
+ int i;
+ struct BufHeader *ibh = arg;
+
+ if (DEBUG_1TR6 > 4) {
+ p = DATAPTR(ibh);
+ for (i = 0; i < ibh->datasize; i++) {
+ printk(KERN_INFO "Info DATA %x\n", p[i]);
+ }
+ }
+ BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_tu_connect(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ st->pa->chargeinfo=0;
+ BufPoolRelease(ibh);
+ st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+ newl3state(st, 10);
+}
+
+static void
+l3_1tr6_tu_rel(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1);
+ st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+ newl3state(st, 0);
+}
+
+static void
+l3_1tr6_tu_rel_ack(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ newl3state(st, 0);
+ st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3_1tr6_tu_disc(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+ byte *p;
+ int i,tmpcharge=0;
+ char a_charge[8];
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ WE6_chargingInfo, 6))) {
+ iecpy(a_charge, p, 1);
+ for (i = 0; i < strlen (a_charge); i++) {
+ tmpcharge *= 10;
+ tmpcharge += a_charge[i] & 0xf;
+ }
+ if (tmpcharge > st->pa->chargeinfo) {
+ st->pa->chargeinfo = tmpcharge;
+ st->l3.l3l4 (st, CC_INFO_CHARGE, NULL);
+ }
+ if (DEBUG_1TR6 > 2)
+ printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
+ } else if (DEBUG_1TR6 > 2)
+ printk(KERN_INFO "chargingInfo not found\n");
+
+ p = DATAPTR(ibh);
+ if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+ WE0_cause, 0))) {
+ if (p[1] > 0) {
+ st->pa->cause = p[2];
+ } else {
+ st->pa->cause = 0;
+ }
+ if (DEBUG_1TR6 > 1)
+ printk(KERN_INFO "Cause %x\n", st->pa->cause);
+ } else if (DEBUG_1TR6 > 0)
+ printk(KERN_INFO "Cause not found\n");
+
+ BufPoolRelease(ibh);
+ newl3state(st, 12);
+ st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+
+static void
+l3_1tr6_tu_connect_ack(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *ibh = arg;
+
+ BufPoolRelease(ibh);
+ st->pa->chargeinfo = 0;
+ st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+ newl3state(st, 10);
+}
+
+static void
+l3_1tr6_alert(struct PStack *st, byte pr,
+ void *arg)
+{
+ l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
+ newl3state(st, 7);
+}
+
+static void
+l3_1tr6_conn(struct PStack *st, byte pr,
+ void *arg)
+{
+ struct BufHeader *dibh;
+ byte *p;
+
+ st->l3.callref = 0x80 + st->pa->callref;
+
+ BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+ p = DATAPTR(dibh);
+ p += st->l2.ihsize;
+
+ *p++ = PROTO_DIS_N1;
+ *p++ = 0x1;
+ *p++ = st->l3.callref;
+ *p++ = MT_N1_CONN;
+
+ if (st->pa->spv) { /* SPV ??? */
+ /* NSF SPV */
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_SPV; /* SPV */
+ *p++ = st->pa->info;
+ *p++ = st->pa->info2;
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_Activate; /* aktiviere SPV */
+ *p++ = st->pa->info;
+ *p++ = st->pa->info2;
+ }
+ dibh->datasize = p - DATAPTR(dibh);
+
+ i_down(st, dibh);
+
+ newl3state(st, 8);
+}
+
+static void
+l3_1tr6_ignore(struct PStack *st, byte pr, void *arg)
+{
+ newl3state(st, 0);
+}
+
+static void
+l3_1tr6_disconn_req(struct PStack *st, byte pr, void *arg)
+{
+ struct BufHeader *dibh;
+ byte *p;
+ byte rejflg;
+
+ BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 21);
+ p = DATAPTR(dibh);
+ p += st->l2.ihsize;
+
+ *p++ = PROTO_DIS_N1;
+ *p++ = 0x1;
+ *p++ = st->l3.callref;
+ *p++ = MT_N1_DISC;
+
+ if (st->l3.state == 7) {
+ rejflg = 1;
+ *p++ = WE0_cause; /* Anruf abweisen */
+ *p++ = 0x01; /* Laenge = 1 */
+ *p++ = CAUSE_CallRejected;
+ } else {
+ rejflg = 0;
+ *p++ = WE0_cause;
+ *p++ = 0x0; /* Laenge = 0 normales Ausloesen */
+ }
+
+ dibh->datasize = p - DATAPTR(dibh);
+
+ i_down(st, dibh);
+
+ newl3state(st, 11);
+}
+
+static void
+l3_1tr6_rel_req(struct PStack *st, byte pr, void *arg)
+{
+ l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
+ newl3state(st, 19);
+}
+
+static struct stateentry downstatelist_1tr6t[] =
+{
+ {0, CC_SETUP_REQ, l3_1tr6_setup},
+ {4, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+ {6, CC_REJECT_REQ, l3_1tr6_ignore},
+ {6, CC_SETUP_RSP, l3_1tr6_conn},
+ {6, CC_ALERTING_REQ, l3_1tr6_alert},
+ {7, CC_SETUP_RSP, l3_1tr6_conn},
+ {7, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+ {7, CC_DLRL, l3_1tr6_disconn_req},
+ {8, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+ {10, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+ {12, CC_RELEASE_REQ, l3_1tr6_rel_req}
+};
+
+static int downsl_1tr6t_len = sizeof(downstatelist_1tr6t) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist_1tr6t[] =
+{
+ {0, MT_N1_SETUP, l3_1tr6_tu_setup},
+ {0, MT_N1_REL, l3_1tr6_tu_rel},
+ {1, MT_N1_SETUP_ACK, l3_1tr6_tu_setup_ack},
+ {1, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
+ {1, MT_N1_REL, l3_1tr6_tu_rel},
+ {1, MT_N1_DISC, l3_1tr6_tu_disc},
+ {2, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
+ {2, MT_N1_ALERT, l3_1tr6_tu_alert},
+ {2, MT_N1_CONN, l3_1tr6_tu_connect},
+ {2, MT_N1_REL, l3_1tr6_tu_rel},
+ {2, MT_N1_DISC, l3_1tr6_tu_disc},
+ {2, MT_N1_INFO, l3_1tr6_tu_info_s2},
+ {3, MT_N1_ALERT, l3_1tr6_tu_alert},
+ {3, MT_N1_CONN, l3_1tr6_tu_connect},
+ {3, MT_N1_REL, l3_1tr6_tu_rel},
+ {3, MT_N1_DISC, l3_1tr6_tu_disc},
+ {4, MT_N1_ALERT, l3_1tr6_tu_alert},
+ {4, MT_N1_CONN, l3_1tr6_tu_connect},
+ {4, MT_N1_REL, l3_1tr6_tu_rel},
+ {4, MT_N1_DISC, l3_1tr6_tu_disc},
+ {7, MT_N1_REL, l3_1tr6_tu_rel},
+ {7, MT_N1_DISC, l3_1tr6_tu_disc},
+ {8, MT_N1_REL, l3_1tr6_tu_rel},
+ {8, MT_N1_DISC, l3_1tr6_tu_disc},
+ {8, MT_N1_CONN_ACK, l3_1tr6_tu_connect_ack},
+ {10, MT_N1_REL, l3_1tr6_tu_rel},
+ {10, MT_N1_DISC, l3_1tr6_tu_disc},
+ {10, MT_N1_INFO, l3_1tr6_tu_info},
+ {11, MT_N1_REL, l3_1tr6_tu_rel},
+ {12, MT_N1_REL, l3_1tr6_tu_rel},
+ {19, MT_N1_REL_ACK, l3_1tr6_tu_rel_ack}
+};
+
+static int datasl_1tr6t_len = sizeof(datastatelist_1tr6t) /
+sizeof(struct stateentry);
diff --git a/drivers/isdn/teles/l3_1TR6.h b/drivers/isdn/teles/l3_1TR6.h
new file mode 100644
index 000000000..a3502fb36
--- /dev/null
+++ b/drivers/isdn/teles/l3_1TR6.h
@@ -0,0 +1,160 @@
+/* $Id: l3_1TR6.h,v 1.3 1996/04/30 21:53:48 isdn4dev Exp $
+ *
+ * $Log: l3_1TR6.h,v $
+ * Revision 1.3 1996/04/30 21:53:48 isdn4dev
+ * Bugs, SPV, Logging in q931.c Karsten Keil
+ *
+ * Revision 1.1 1996/04/13 10:25:42 fritz
+ * Initial revision
+ *
+ *
+ */
+#ifndef l3_1TR6
+#define l3_1TR6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 0x61
+#define MT_N0_CANC_IND 0x62
+#define MT_N0_FAC_STA 0x63
+#define MT_N0_STA_ACK 0x64
+#define MT_N0_STA_REJ 0x65
+#define MT_N0_FAC_INF 0x66
+#define MT_N0_INF_ACK 0x67
+#define MT_N0_INF_REJ 0x68
+#define MT_N0_CLOSE 0x75
+#define MT_N0_CLO_ACK 0x77
+
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+
+
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SPV 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAccessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+
+#endif
diff --git a/drivers/isdn/teles/llglue.c b/drivers/isdn/teles/llglue.c
new file mode 100644
index 000000000..81a0a1e00
--- /dev/null
+++ b/drivers/isdn/teles/llglue.c
@@ -0,0 +1,149 @@
+/* $Id: llglue.c,v 1.6 1996/06/03 20:03:39 fritz Exp $
+ *
+ * $Log: llglue.c,v $
+ * Revision 1.6 1996/06/03 20:03:39 fritz
+ * Fixed typos.
+ *
+ * Revision 1.5 1996/05/31 00:58:47 fritz
+ * Errata: Reverted change from rev 1.4.
+ *
+ * Revision 1.4 1996/05/26 14:59:57 fritz
+ * Bugfix: maxbufsize had been set without respect to possible X.75 header.
+ *
+ * Revision 1.3 1996/05/01 14:19:57 fritz
+ * Added ISDN_FEATURE_L2_TRANS
+ *
+ * Revision 1.2 1996/04/29 23:01:46 fritz
+ * Added driverId and channel to readstatus().
+ *
+ * Revision 1.1 1996/04/13 10:26:29 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "teles.h"
+#include <linux/malloc.h>
+#include <linux/timer.h>
+
+
+extern struct Channel *chanlist;
+int drid;
+char *teles_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+isdn_if iif;
+
+#define TELES_STATUS_BUFSIZE 4096
+static byte *teles_status_buf = NULL;
+static byte *teles_status_read = NULL;
+static byte *teles_status_write = NULL;
+static byte *teles_status_end = NULL;
+
+int
+teles_readstatus(byte * buf, int len, int user, int id, int channel)
+{
+ int count;
+ byte *p;
+
+ for (p = buf, count = 0; count < len; p++, count++) {
+ if (user) {
+ put_user(*teles_status_read, p);
+ teles_status_read++;
+ } else
+ *p++ = *teles_status_read++;
+ if (teles_status_read > teles_status_end)
+ teles_status_read = teles_status_buf;
+ }
+ return count;
+}
+
+void
+teles_putstatus(char *buf)
+{
+ long flags;
+ int len, count, i;
+ byte *p;
+ isdn_ctrl ic;
+
+ save_flags(flags);
+ cli();
+ count = 0;
+ len = strlen(buf);
+ for (p = buf, i = len; i > 0; i--, p++) {
+ *teles_status_write++ = *p;
+ if (teles_status_write > teles_status_end)
+ teles_status_write = teles_status_buf;
+ count++;
+ }
+ restore_flags(flags);
+ if (count) {
+ ic.command = ISDN_STAT_STAVAIL;
+ ic.driver = drid;
+ ic.arg = count;
+ iif.statcallb(&ic);
+ }
+}
+
+
+int
+ll_init(void)
+{
+ isdn_ctrl ic;
+
+ teles_status_buf = Smalloc(TELES_STATUS_BUFSIZE,
+ GFP_KERNEL, "teles_status_buf");
+ if (!teles_status_buf) {
+ printk(KERN_ERR "teles: Could not allocate status-buffer\n");
+ return (-EIO);
+ } else {
+ teles_status_read = teles_status_buf;
+ teles_status_write = teles_status_buf;
+ teles_status_end = teles_status_buf + TELES_STATUS_BUFSIZE - 1;
+ }
+
+ iif.channels = CallcNewChan();
+ iif.maxbufsize = BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS);
+ iif.features =
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_1TR6 |
+ ISDN_FEATURE_P_EURO;
+
+ iif.command = teles_command;
+ iif.writebuf = teles_writebuf;
+ iif.writecmd = NULL;
+ iif.readstat = teles_readstatus;
+ strncpy(iif.id, teles_id, sizeof(iif.id) - 1);
+
+ register_isdn(&iif);
+ drid = iif.channels;
+
+ ic.driver = drid;
+ ic.command = ISDN_STAT_RUN;
+ iif.statcallb(&ic);
+ return 0;
+}
+
+void
+ll_stop(void)
+{
+ isdn_ctrl ic;
+
+ ic.command = ISDN_STAT_STOP;
+ ic.driver = drid;
+ iif.statcallb(&ic);
+
+ CallcFreeChan();
+}
+
+void
+ll_unload(void)
+{
+ isdn_ctrl ic;
+
+ ic.command = ISDN_STAT_UNLOAD;
+ ic.driver = drid;
+ iif.statcallb(&ic);
+}
diff --git a/drivers/isdn/teles/mod.c b/drivers/isdn/teles/mod.c
new file mode 100644
index 000000000..0b12d9c92
--- /dev/null
+++ b/drivers/isdn/teles/mod.c
@@ -0,0 +1,143 @@
+/* $Id: mod.c,v 1.1 1996/04/13 10:27:02 fritz Exp $
+ *
+ * $Log: mod.c,v $
+ * Revision 1.1 1996/04/13 10:27:02 fritz
+ * Initial revision
+ *
+ *
+ */
+#include "teles.h"
+
+extern struct IsdnCard cards[];
+extern char *teles_id;
+
+int nrcards;
+
+typedef struct {
+ unsigned int membase;
+ int interrupt;
+ unsigned int iobase;
+ unsigned int protocol;
+} io_type;
+
+io_type io[] =
+{
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+};
+
+void
+teles_mod_dec_use_count(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+void
+teles_mod_inc_use_count(void)
+{
+ MOD_INC_USE_COUNT;
+}
+
+#ifdef MODULE
+#define teles_init init_module
+#else
+void teles_setup(char *str, int *ints)
+{
+ int i, j, argc;
+ static char sid[20];
+
+ argc = ints[0];
+ i = 0;
+ j = 1;
+ while (argc && (i<16)) {
+ if (argc) {
+ io[i].iobase = ints[j];
+ j++; argc--;
+ }
+ if (argc) {
+ io[i].interrupt = ints[j];
+ j++; argc--;
+ }
+ if (argc) {
+ io[i].membase = ints[j];
+ j++; argc--;
+ }
+ if (argc) {
+ io[i].protocol = ints[j];
+ j++; argc--;
+ }
+ i++;
+ }
+ if (strlen(str)) {
+ strcpy(sid,str);
+ teles_id = sid;
+ }
+}
+#endif
+
+int
+teles_init(void)
+{
+ int i;
+
+ nrcards = 0;
+ for (i = 0; i < 16; i++) {
+ if (io[i].protocol) {
+ cards[i].membase = io[i].membase;
+ cards[i].interrupt = io[i].interrupt;
+ cards[i].iobase = io[i].iobase;
+ cards[i].protocol = io[i].protocol;
+ }
+ }
+ for (i = 0; i < 16; i++)
+ if (cards[i].protocol)
+ nrcards++;
+ printk(KERN_DEBUG "teles: Total %d card%s defined\n",
+ nrcards, (nrcards > 1) ? "s" : "");
+ if (teles_inithardware()) {
+ /* Install only, if at least one card found */
+ Isdnl2New();
+ TeiNew();
+ CallcNew();
+ ll_init();
+
+ /* No symbols to export, hide all symbols */
+ register_symtab(NULL);
+
+#ifdef MODULE
+ printk(KERN_NOTICE "Teles module installed\n");
+#endif
+ return (0);
+ } else
+ return -EIO;
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+
+ ll_stop();
+ TeiFree();
+ Isdnl2Free();
+ CallcFree();
+ teles_closehardware();
+ ll_unload();
+ printk(KERN_NOTICE "Teles module removed\n");
+
+}
+#endif
diff --git a/drivers/isdn/teles/proto.h b/drivers/isdn/teles/proto.h
new file mode 100644
index 000000000..0d7ae8ef4
--- /dev/null
+++ b/drivers/isdn/teles/proto.h
@@ -0,0 +1,18 @@
+/* $Id: proto.h,v 1.1 1996/09/23 01:53:52 fritz Exp $
+ *
+ * not much now - just the l3 proto discriminator
+ *
+ * $Log: proto.h,v $
+ * Revision 1.1 1996/09/23 01:53:52 fritz
+ * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
+ *
+ */
+
+#ifndef PROTO_H
+#define PROTO_H
+
+#define PROTO_EURO 0x08
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+#endif
diff --git a/drivers/isdn/teles/q931.c b/drivers/isdn/teles/q931.c
new file mode 100644
index 000000000..0b93388cf
--- /dev/null
+++ b/drivers/isdn/teles/q931.c
@@ -0,0 +1,1149 @@
+/* $Id: q931.c,v 1.5 1996/06/03 20:03:40 fritz Exp $
+ *
+ * q931.c code to decode ITU Q.931 call control messages
+ *
+ * Author Jan den Ouden
+ *
+ * Changelog
+ *
+ * Pauline Middelink general improvements
+ *
+ * Beat Doebeli cause texts, display information element
+ *
+ * Karsten Keil cause texts, display information element for 1TR6
+ *
+ *
+ * $Log: q931.c,v $
+ * Revision 1.5 1996/06/03 20:03:40 fritz
+ * Fixed typos.
+ *
+ * Revision 1.4 1996/05/17 03:46:17 fritz
+ * General cleanup.
+ *
+ * Revision 1.3 1996/04/30 22:06:50 isdn4dev
+ * logging 1TR6 messages correctly Karsten Keil
+ *
+ * Revision 1.2 1996/04/20 16:48:19 fritz
+ * Misc. typos
+ *
+ * Revision 1.1 1996/04/13 10:27:49 fritz
+ * Initial revision
+ *
+ *
+ */
+
+
+#define __NO_VERSION__
+#include "teles.h"
+#include "l3_1TR6.h"
+
+byte *
+findie(byte * p, int size, byte ie, int wanted_set)
+{
+ int l, codeset, maincodeset;
+ byte *pend = p + size;
+
+ /* skip protocol discriminator, callref and message type */
+ p++;
+ l = (*p++) & 0xf;
+ p += l;
+ p++;
+ codeset = 0;
+ maincodeset = 0;
+ /* while there are bytes left... */
+ while (p < pend) {
+ if ((*p & 0xf0) == 0x90) {
+ codeset = *p & 0x07;
+ if (!(*p & 0x08))
+ maincodeset = codeset;
+ }
+ if (*p & 0x80)
+ p++;
+ else {
+ if (codeset == wanted_set) {
+ if (*p == ie)
+ return (p);
+ if (*p > ie)
+ return (NULL);
+ }
+ p++;
+ l = *p++;
+ p += l;
+ codeset = maincodeset;
+ }
+ }
+ return (NULL);
+}
+
+void
+iecpy(byte * dest, byte * iestart, int ieoffset)
+{
+ byte *p;
+ int l;
+
+ p = iestart + ieoffset + 2;
+ l = iestart[1] - ieoffset;
+ while (l--)
+ *dest++ = *p++;
+ *dest++ = '\0';
+}
+
+int
+getcallref(byte * p)
+{
+ p++; /* prot discr */
+ p++; /* callref length */
+ return (*p); /* assuming one-byte callref */
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+ byte nr;
+ char *descr;
+} mtlist[] = {
+
+ {
+ 0x1, "ALERTING"
+ },
+ {
+ 0x2, "CALL PROCEEDING"
+ },
+ {
+ 0x7, "CONNECT"
+ },
+ {
+ 0xf, "CONNECT ACKNOWLEDGE"
+ },
+ {
+ 0x3, "PROGRESS"
+ },
+ {
+ 0x5, "SETUP"
+ },
+ {
+ 0xd, "SETUP ACKNOWLEDGE"
+ },
+ {
+ 0x26, "RESUME"
+ },
+ {
+ 0x2e, "RESUME ACKNOWLEDGE"
+ },
+ {
+ 0x22, "RESUME REJECT"
+ },
+ {
+ 0x25, "SUSPEND"
+ },
+ {
+ 0x2d, "SUSPEND ACKNOWLEDGE"
+ },
+ {
+ 0x21, "SUSPEND REJECT"
+ },
+ {
+ 0x20, "USER INFORMATION"
+ },
+ {
+ 0x45, "DISCONNECT"
+ },
+ {
+ 0x4d, "RELEASE"
+ },
+ {
+ 0x5a, "RELEASE COMPLETE"
+ },
+ {
+ 0x46, "RESTART"
+ },
+ {
+ 0x4e, "RESTART ACKNOWLEDGE"
+ },
+ {
+ 0x60, "SEGMENT"
+ },
+ {
+ 0x79, "CONGESTION CONTROL"
+ },
+ {
+ 0x7b, "INFORMATION"
+ },
+ {
+ 0x62, "FACILITY"
+ },
+ {
+ 0x6e, "NOTIFY"
+ },
+ {
+ 0x7d, "STATUS"
+ },
+ {
+ 0x75, "STATUS ENQUIRY"
+ }
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+static
+struct MessageType mt_n0[] =
+{
+ {MT_N0_REG_IND, "REGister INDication"},
+ {MT_N0_CANC_IND, "CANCel INDication"},
+ {MT_N0_FAC_STA, "FACility STAtus"},
+ {MT_N0_STA_ACK, "STAtus ACKnowledge"},
+ {MT_N0_STA_REJ, "STAtus REJect"},
+ {MT_N0_FAC_INF, "FACility INFormation"},
+ {MT_N0_INF_ACK, "INFormation ACKnowledge"},
+ {MT_N0_INF_REJ, "INFormation REJect"},
+ {MT_N0_CLOSE, "CLOSE"},
+ {MT_N0_CLO_ACK, "CLOse ACKnowledge"}
+};
+
+int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType));
+
+static
+struct MessageType mt_n1[] =
+{
+ {MT_N1_ESC, "ESCape"},
+ {MT_N1_ALERT, "ALERT"},
+ {MT_N1_CALL_SENT, "CALL SENT"},
+ {MT_N1_CONN, "CONNect"},
+ {MT_N1_CONN_ACK, "CONNect ACKnowledge"},
+ {MT_N1_SETUP, "SETUP"},
+ {MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
+ {MT_N1_RES, "RESume"},
+ {MT_N1_RES_ACK, "RESume ACKnowledge"},
+ {MT_N1_RES_REJ, "RESume REJect"},
+ {MT_N1_SUSP, "SUSPend"},
+ {MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
+ {MT_N1_SUSP_REJ, "SUSPend REJect"},
+ {MT_N1_USER_INFO, "USER INFO"},
+ {MT_N1_DET, "DETach"},
+ {MT_N1_DISC, "DISConnect"},
+ {MT_N1_REL, "RELease"},
+ {MT_N1_REL_ACK, "RELease ACKnowledge"},
+ {MT_N1_CANC_ACK, "CANCel ACKnowledge"},
+ {MT_N1_CANC_REJ, "CANCel REJect"},
+ {MT_N1_CON_CON, "CONgestion CONtrol"},
+ {MT_N1_FAC, "FACility"},
+ {MT_N1_FAC_ACK, "FACility ACKnowledge"},
+ {MT_N1_FAC_CAN, "FACility CANcel"},
+ {MT_N1_FAC_REG, "FACility REGister"},
+ {MT_N1_FAC_REJ, "FACility REJect"},
+ {MT_N1_INFO, "INFOrmation"},
+ {MT_N1_REG_ACK, "REGister ACKnowledge"},
+ {MT_N1_REG_REJ, "REGister REJect"},
+ {MT_N1_STAT, "STATus"}
+};
+
+int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType));
+
+static struct MessageType fac_1tr6[] =
+{
+ {FAC_Sperre, "Sperre"},
+ {FAC_Forward1, "Forward 1"},
+ {FAC_Forward2, "Forward 2"},
+ {FAC_Konferenz, "Konferenz"},
+ {FAC_GrabBchan, "Grab Bchannel"},
+ {FAC_Reactivate, "Reactivate"},
+ {FAC_Konferenz3, "Dreier Konferenz"},
+ {FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"},
+ {FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"},
+ {FAC_NummernIdent, "Rufnummer-Identifizierung"},
+ {FAC_GBG, "GBG"},
+ {FAC_DisplayUebergeben, "Display Uebergeben"},
+ {FAC_DisplayUmgeleitet, "Display Umgeleitet"},
+ {FAC_Unterdruecke, "Unterdruecke Rufnummer"},
+ {FAC_Deactivate, "Deactivate"},
+ {FAC_Activate, "Activate"},
+ {FAC_SPV, "SPV"},
+ {FAC_Rueckwechsel, "Rueckwechsel"},
+ {FAC_Umleitung, "Umleitung"}
+};
+int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType));
+
+
+
+static int
+prbits(char *dest, byte b, int start, int len)
+{
+ char *dp = dest;
+
+ b = b << (8 - start);
+ while (len--) {
+ if (b & 0x80)
+ *dp++ = '1';
+ else
+ *dp++ = '0';
+ b = b << 1;
+ }
+ return (dp - dest);
+}
+
+static
+byte *
+skipext(byte * p)
+{
+ while (!(*p++ & 0x80));
+ return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ * not yet written...
+ */
+
+static
+struct CauseValue {
+ byte nr;
+ char *edescr;
+ char *ddescr;
+} cvlist[] = {
+
+ {
+ 0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+ },
+ {
+ 0x02, "No route to specified transit network", ""
+ },
+ {
+ 0x03, "No route to destination", ""
+ },
+ {
+ 0x04, "Send special information tone", ""
+ },
+ {
+ 0x05, "Misdialled trunk prefix", ""
+ },
+ {
+ 0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+ },
+ {
+ 0x07, "Channel awarded and being delivered in an established channel", ""
+ },
+ {
+ 0x08, "Preemption", ""
+ },
+ {
+ 0x09, "Preemption - circuit reserved for reuse", ""
+ },
+ {
+ 0x10, "Normal call clearing", "Normale Ausloesung"
+ },
+ {
+ 0x11, "User busy", "TNB besetzt"
+ },
+ {
+ 0x12, "No user responding", ""
+ },
+ {
+ 0x13, "No answer from user (user alerted)", ""
+ },
+ {
+ 0x14, "Subscriber absent", ""
+ },
+ {
+ 0x15, "Call rejected", ""
+ },
+ {
+ 0x16, "Number changed", ""
+ },
+ {
+ 0x1a, "non-selected user clearing", ""
+ },
+ {
+ 0x1b, "Destination out of order", ""
+ },
+ {
+ 0x1c, "Invalid number format (address incomplete)", ""
+ },
+ {
+ 0x1d, "Facility rejected", ""
+ },
+ {
+ 0x1e, "Response to Status enquiry", ""
+ },
+ {
+ 0x1f, "Normal, unspecified", ""
+ },
+ {
+ 0x22, "No circuit/channel available", ""
+ },
+ {
+ 0x26, "Network out of order", ""
+ },
+ {
+ 0x27, "Permanent frame mode connection out-of-service", ""
+ },
+ {
+ 0x28, "Permanent frame mode connection operational", ""
+ },
+ {
+ 0x29, "Temporary failure", ""
+ },
+ {
+ 0x2a, "Switching equipment congestion", ""
+ },
+ {
+ 0x2b, "Access information discarded", ""
+ },
+ {
+ 0x2c, "Requested circuit/channel not available", ""
+ },
+ {
+ 0x2e, "Precedence call blocked", ""
+ },
+ {
+ 0x2f, "Resource unavailable, unspecified", ""
+ },
+ {
+ 0x31, "Quality of service unavailable", ""
+ },
+ {
+ 0x32, "Requested facility not subscribed", ""
+ },
+ {
+ 0x35, "Outgoing calls barred within CUG", ""
+ },
+ {
+ 0x37, "Incoming calls barred within CUG", ""
+ },
+ {
+ 0x39, "Bearer capability not authorized", ""
+ },
+ {
+ 0x3a, "Bearer capability not presently available", ""
+ },
+ {
+ 0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+ },
+ {
+ 0x3f, "Service or option not available, unspecified", ""
+ },
+ {
+ 0x41, "Bearer capability not implemented", ""
+ },
+ {
+ 0x42, "Channel type not implemented", ""
+ },
+ {
+ 0x43, "Requested facility not implemented", ""
+ },
+ {
+ 0x44, "Only restricted digital information bearer capability is available", ""
+ },
+ {
+ 0x4f, "Service or option not implemented", ""
+ },
+ {
+ 0x51, "Invalid call reference value", ""
+ },
+ {
+ 0x52, "Identified channel does not exist", ""
+ },
+ {
+ 0x53, "A suspended call exists, but this call identity does not", ""
+ },
+ {
+ 0x54, "Call identity in use", ""
+ },
+ {
+ 0x55, "No call suspended", ""
+ },
+ {
+ 0x56, "Call having the requested call identity has been cleared", ""
+ },
+ {
+ 0x57, "User not member of CUG", ""
+ },
+ {
+ 0x58, "Incompatible destination", ""
+ },
+ {
+ 0x5a, "Non-existent CUG", ""
+ },
+ {
+ 0x5b, "Invalid transit network selection", ""
+ },
+ {
+ 0x5f, "Invalid message, unspecified", ""
+ },
+ {
+ 0x60, "Mandatory information element is missing", ""
+ },
+ {
+ 0x61, "Message type non-existent or not implemented", ""
+ },
+ {
+ 0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+ },
+ {
+ 0x63, "Information element/parameter non-existent or not implemented", ""
+ },
+ {
+ 0x64, "Invalid information element contents", ""
+ },
+ {
+ 0x65, "Message not compatible with call state", ""
+ },
+ {
+ 0x66, "Recovery on timer expiry", ""
+ },
+ {
+ 0x67, "Parameter non-existent or not implemented - passed on", ""
+ },
+ {
+ 0x6e, "Message with unrecognized parameter discarded", ""
+ },
+ {
+ 0x6f, "Protocol error, unspecified", ""
+ },
+ {
+ 0x7f, "Interworking, unspecified", ""
+ },
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, byte * p)
+{
+ byte *end;
+ char *dp = dest;
+ int i, cause;
+
+ end = p + p[1] + 1;
+ p += 2;
+ dp += sprintf(dp, " coding ");
+ dp += prbits(dp, *p, 7, 2);
+ dp += sprintf(dp, " location ");
+ dp += prbits(dp, *p, 4, 4);
+ *dp++ = '\n';
+ p = skipext(p);
+
+ cause = 0x7f & *p++;
+
+ /* locate cause value */
+ for (i = 0; i < CVSIZE; i++)
+ if (cvlist[i].nr == cause)
+ break;
+
+ /* display cause value if it exists */
+ if (i == CVSIZE)
+ dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+ else
+ dp += sprintf(dp, " cause value %x : %s \n", cause, cvlist[i].edescr);
+
+ while (!0) {
+ if (p > end)
+ break;
+ dp += sprintf(dp, " diag attribute %d ", *p++ & 0x7f);
+ dp += sprintf(dp, " rej %d ", *p & 0x7f);
+ if (*p & 0x80) {
+ *dp++ = '\n';
+ break;
+ } else
+ dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+ }
+ return (dp - dest);
+
+}
+
+static
+struct MessageType cause_1tr6[] =
+{
+ {CAUSE_InvCRef, "Invalid Call Reference"},
+ {CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
+ {CAUSE_CIDunknown, "Caller Identity unknown"},
+ {CAUSE_CIDinUse, "Caller Identity in Use"},
+ {CAUSE_NoChans, "No Channels available"},
+ {CAUSE_FacNotImpl, "Facility Not Implemented"},
+ {CAUSE_FacNotSubscr, "Facility Not Subscribed"},
+ {CAUSE_OutgoingBarred, "Outgoing calls barred"},
+ {CAUSE_UserAccessBusy, "User Access Busy"},
+ {CAUSE_NegativeGBG, "Negative GBG"},
+ {CAUSE_UnknownGBG, "Unknown GBG"},
+ {CAUSE_NoSPVknown, "No SPV known"},
+ {CAUSE_DestNotObtain, "Destination not obtainable"},
+ {CAUSE_NumberChanged, "Number changed"},
+ {CAUSE_OutOfOrder, "Out Of Order"},
+ {CAUSE_NoUserResponse, "No User Response"},
+ {CAUSE_UserBusy, "User Busy"},
+ {CAUSE_IncomingBarred, "Incoming Barred"},
+ {CAUSE_CallRejected, "Call Rejected"},
+ {CAUSE_NetworkCongestion, "Network Congestion"},
+ {CAUSE_RemoteUser, "Remote User initiated"},
+ {CAUSE_LocalProcErr, "Local Procedure Error"},
+ {CAUSE_RemoteProcErr, "Remote Procedure Error"},
+ {CAUSE_RemoteUserSuspend, "Remote User Suspend"},
+ {CAUSE_RemoteUserResumed, "Remote User Resumed"},
+ {CAUSE_UserInfoDiscarded, "User Info Discarded"}
+};
+
+int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
+
+static int
+prcause_1tr6(char *dest, byte * p)
+{
+ char *dp = dest;
+ int i, cause;
+
+ p++;
+ if (0 == *p) {
+ dp += sprintf(dp, " OK (cause length=0)\n");
+ return (dp - dest);
+ } else if (*p > 1) {
+ dp += sprintf(dp, " coding ");
+ dp += prbits(dp, p[2], 7, 2);
+ dp += sprintf(dp, " location ");
+ dp += prbits(dp, p[2], 4, 4);
+ *dp++ = '\n';
+ }
+ p++;
+ cause = 0x7f & *p;
+
+ /* locate cause value */
+ for (i = 0; i < cause_1tr6_len; i++)
+ if (cause_1tr6[i].nr == cause)
+ break;
+
+ /* display cause value if it exists */
+ if (i == cause_1tr6_len)
+ dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+ else
+ dp += sprintf(dp, " cause value %x : %s \n", cause, cause_1tr6[i].descr);
+
+ return (dp - dest);
+
+}
+
+static int
+prchident(char *dest, byte * p) {
+ char *dp = dest;
+
+ p += 2;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+static int
+prcalled(char *dest, byte * p) {
+ int l;
+ char *dp = dest;
+
+ p++;
+ l = *p++ - 1;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ dp += sprintf(dp, " number digits ");
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+static int
+prcalling(char *dest, byte * p) {
+ int l;
+ char *dp = dest;
+
+ p++;
+ l = *p++ - 1;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ if (!(*p & 0x80)) {
+ dp += sprintf(dp, " octet 3a ");
+ dp += prbits(dp, *++p, 8, 8);
+ *dp++ = '\n';
+ l--;
+ };
+ p++;
+
+ dp += sprintf(dp, " number digits ");
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, byte * p)
+{
+ char *dp = dest, ch;
+
+ p += 2;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ dp += sprintf(dp, " octet 4 ");
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ if ((*p++ & 0x1f) == 0x18) {
+ dp += sprintf(dp, " octet 4.1 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ }
+ /* check for user information layer 1 */
+ if ((*p & 0x60) == 0x20) {
+ ch = ' ';
+ do {
+ dp += sprintf(dp, " octet 5%c ", ch);
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ if (ch == ' ')
+ ch = 'a';
+ else
+ ch++;
+ }
+ while (!(*p++ & 0x80));
+ }
+ /* check for user information layer 2 */
+ if ((*p & 0x60) == 0x40) {
+ dp += sprintf(dp, " octet 6 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ }
+ /* check for user information layer 3 */
+ if ((*p & 0x60) == 0x60) {
+ dp += sprintf(dp, " octet 7 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ }
+ return (dp - dest);
+}
+
+static int
+general(char *dest, byte * p) {
+ char *dp = dest;
+ char ch = ' ';
+ int l, octet = 3;
+
+ p++;
+ l = *p++;
+ /* Iterate over all octets in the information element */
+ while (l--) {
+ dp += sprintf(dp, " octet %d%c ", octet, ch);
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+
+ /* last octet in group? */
+ if (*p & 0x80) {
+ octet++;
+ ch = ' ';
+ } else if (ch == ' ')
+ ch = 'a';
+ else
+ ch++;
+ }
+ return (dp - dest);
+}
+
+static int
+prcharge(char *dest, byte * p) {
+ char *dp = dest;
+ int l;
+
+ p++;
+ l = *p++ - 1;
+ dp += sprintf(dp, " GEA ");
+ dp += prbits(dp, *p++, 8, 8);
+ dp += sprintf(dp, " Anzahl: ");
+ /* Iterate over all octets in the * information element */
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+static int
+prtext(char *dest, byte * p) {
+ char *dp = dest;
+ int l;
+
+ p++;
+ l = *p++;
+ dp += sprintf(dp, " ");
+ /* Iterate over all octets in the * information element */
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+static int
+display(char *dest, byte * p) {
+ char *dp = dest;
+ char ch = ' ';
+ int l, octet = 3;
+
+ p++;
+ l = *p++;
+ /* Iterate over all octets in the * display-information element */
+ dp += sprintf(dp, " \"");
+ while (l--) {
+ dp += sprintf(dp, "%c", *p++);
+
+ /* last octet in group? */
+ if (*p & 0x80) {
+ octet++;
+ ch = ' ';
+ } else if (ch == ' ')
+ ch = 'a';
+
+ else
+ ch++;
+ }
+ *dp++ = '\"';
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+int
+prfacility(char *dest, byte * p)
+{
+ char *dp = dest;
+ int l, l2;
+
+ p++;
+ l = *p++;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p++, 8, 8);
+ dp += sprintf(dp, "\n");
+ l -= 1;
+
+ while (l > 0) {
+ dp += sprintf(dp, " octet 4 ");
+ dp += prbits(dp, *p++, 8, 8);
+ dp += sprintf(dp, "\n");
+ dp += sprintf(dp, " octet 5 %d\n", l2 = *p++ & 0x7f);
+ l -= 2;
+ dp += sprintf(dp, " contents ");
+ while (l2--) {
+ dp += sprintf(dp, "%2x ", *p++);
+ l--;
+ }
+ dp += sprintf(dp, "\n");
+ }
+
+ return (dp - dest);
+}
+
+static
+struct InformationElement {
+ byte nr;
+ char *descr;
+ int (*f) (char *, byte *);
+} ielist[] = {
+
+ {
+ 0x00, "Segmented message", general
+ },
+ {
+ 0x04, "Bearer capability", prbearer
+ },
+ {
+ 0x08, "Cause", prcause
+ },
+ {
+ 0x10, "Call identity", general
+ },
+ {
+ 0x14, "Call state", general
+ },
+ {
+ 0x18, "Channel identification", prchident
+ },
+ {
+ 0x1c, "Facility", prfacility
+ },
+ {
+ 0x1e, "Progress indicator", general
+ },
+ {
+ 0x20, "Network-specific facilities", general
+ },
+ {
+ 0x27, "Notification indicator", general
+ },
+ {
+ 0x28, "Display", display
+ },
+ {
+ 0x29, "Date/Time", general
+ },
+ {
+ 0x2c, "Keypad facility", general
+ },
+ {
+ 0x34, "Signal", general
+ },
+ {
+ 0x40, "Information rate", general
+ },
+ {
+ 0x42, "End-to-end delay", general
+ },
+ {
+ 0x43, "Transit delay selection and indication", general
+ },
+ {
+ 0x44, "Packet layer binary parameters", general
+ },
+ {
+ 0x45, "Packet layer window size", general
+ },
+ {
+ 0x46, "Packet size", general
+ },
+ {
+ 0x47, "Closed user group", general
+ },
+ {
+ 0x4a, "Reverse charge indication", general
+ },
+ {
+ 0x6c, "Calling party number", prcalling
+ },
+ {
+ 0x6d, "Calling party subaddress", general
+ },
+ {
+ 0x70, "Called party number", prcalled
+ },
+ {
+ 0x71, "Called party subaddress", general
+ },
+ {
+ 0x74, "Redirecting number", general
+ },
+ {
+ 0x78, "Transit network selection", general
+ },
+ {
+ 0x79, "Restart indicator", general
+ },
+ {
+ 0x7c, "Low layer compatibility", general
+ },
+ {
+ 0x7d, "High layer compatibility", general
+ },
+ {
+ 0x7e, "User-user", general
+ },
+ {
+ 0x7f, "Escape for extension", general
+ },
+};
+
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+static struct InformationElement we_0[] =
+{
+ {WE0_cause, "Cause", prcause_1tr6},
+ {WE0_connAddr, "Connecting Address", prcalled},
+ {WE0_callID, "Call IDentity", general},
+ {WE0_chanID, "Channel IDentity", general},
+ {WE0_netSpecFac, "Network Specific Facility", general},
+ {WE0_display, "Display", general},
+ {WE0_keypad, "Keypad", general},
+ {WE0_origAddr, "Origination Address", prcalled},
+ {WE0_destAddr, "Destination Address", prcalled},
+ {WE0_userInfo, "User Info", general}
+};
+
+static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement));
+
+static struct InformationElement we_6[] =
+{
+ {WE6_serviceInd, "Service Indicator", general},
+ {WE6_chargingInfo, "Charging Information", prcharge},
+ {WE6_date, "Date", prtext},
+ {WE6_facSelect, "Facility Select", general},
+ {WE6_facStatus, "Facility Status", general},
+ {WE6_statusCalled, "Status Called", general},
+ {WE6_addTransAttr, "Additional Transmission Attributes", general}
+};
+static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement));
+
+void
+dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment) {
+ byte *bend = buf + size;
+ char *dp;
+ int i, cs = 0, cs_old = 0, cs_fest = 0;
+
+ /* display header */
+ dp = sp->dlogspace;
+ dp += sprintf(dp, "%s\n", comment);
+
+ {
+ byte *p = buf;
+ dp += sprintf(dp, "hex: ");
+ while (p < bend)
+ dp += sprintf(dp, "%02x ", *p++);
+ dp += sprintf(dp, "\n");
+ teles_putstatus(sp->dlogspace);
+ dp = sp->dlogspace;
+ }
+ if ((0xfe & buf[0]) == PROTO_DIS_N0) { /* 1TR6 */
+ /* locate message type */
+ if (buf[0] == PROTO_DIS_N0) { /* N0 */
+ for (i = 0; i < mt_n0_len; i++)
+ if (mt_n0[i].nr == buf[3])
+ break;
+ /* display message type iff it exists */
+ if (i == mt_n0_len)
+ dp += sprintf(dp, "Unknown message type N0 %x!\n", buf[3]);
+ else
+ dp += sprintf(dp, "call reference %d size %d message type %s\n",
+ buf[2], size, mt_n0[i].descr);
+ } else { /* N1 */
+ for (i = 0; i < mt_n1_len; i++)
+ if (mt_n1[i].nr == buf[3])
+ break;
+ /* display message type iff it exists */
+ if (i == mt_n1_len)
+ dp += sprintf(dp, "Unknown message type N1 %x!\n", buf[3]);
+ else
+ dp += sprintf(dp, "call reference %d size %d message type %s\n",
+ buf[2], size, mt_n1[i].descr);
+ }
+
+ /* display each information element */
+ buf += 4;
+ while (buf < bend) {
+ /* Is it a single octet information element? */
+ if (*buf & 0x80) {
+ switch ((*buf >> 4) & 7) {
+ case 1:
+ dp += sprintf(dp, " Shift %x\n", *buf & 0xf);
+ cs_old = cs;
+ cs = *buf & 7;
+ cs_fest = *buf & 8;
+ break;
+ case 3:
+ dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf);
+ break;
+ case 2:
+ if (*buf == 0xa0) {
+ dp += sprintf(dp, " More data\n");
+ break;
+ }
+ if (*buf == 0xa1) {
+ dp += sprintf(dp, " Sending complete\n");
+ }
+ break;
+ /* fall through */
+ default:
+ dp += sprintf(dp, " Reserved %x\n", *buf);
+ break;
+ }
+ buf++;
+ continue;
+ }
+ /* No, locate it in the table */
+ if (cs == 0) {
+ for (i = 0; i < we_0_len; i++)
+ if (*buf == we_0[i].nr)
+ break;
+
+ /* When found, give appropriate msg */
+ if (i != we_0_len) {
+ dp += sprintf(dp, " %s\n", we_0[i].descr);
+ dp += we_0[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+ } else if (cs == 6) {
+ for (i = 0; i < we_6_len; i++)
+ if (*buf == we_6[i].nr)
+ break;
+
+ /* When found, give appropriate msg */
+ if (i != we_6_len) {
+ dp += sprintf(dp, " %s\n", we_6[i].descr);
+ dp += we_6[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+ } else
+ dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+ /* Skip to next element */
+ if (cs_fest == 8) {
+ cs = cs_old;
+ cs_old = 0;
+ cs_fest = 0;
+ }
+ buf += buf[1] + 2;
+ }
+ } else { /* EURO */
+ /* locate message type */
+ for (i = 0; i < MTSIZE; i++)
+ if (mtlist[i].nr == buf[3])
+ break;
+
+ /* display message type iff it exists */
+ if (i == MTSIZE)
+ dp += sprintf(dp, "Unknown message type %x!\n", buf[3]);
+ else
+ dp += sprintf(dp, "call reference %d size %d message type %s\n",
+ buf[2], size, mtlist[i].descr);
+
+ /* display each information element */
+ buf += 4;
+ while (buf < bend) {
+ /* Is it a single octet information element? */
+ if (*buf & 0x80) {
+ switch ((*buf >> 4) & 7) {
+ case 1:
+ dp += sprintf(dp, " Shift %x\n", *buf & 0xf);
+ break;
+ case 3:
+ dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf);
+ break;
+ case 5:
+ dp += sprintf(dp, " Repeat indicator %x\n", *buf & 0xf);
+ break;
+ case 2:
+ if (*buf == 0xa0) {
+ dp += sprintf(dp, " More data\n");
+ break;
+ }
+ if (*buf == 0xa1) {
+ dp += sprintf(dp, " Sending complete\n");
+ }
+ break;
+ /* fall through */
+ default:
+ dp += sprintf(dp, " Reserved %x\n", *buf);
+ break;
+ }
+ buf++;
+ continue;
+ }
+ /* No, locate it in the table */
+ for (i = 0; i < IESIZE; i++)
+ if (*buf == ielist[i].nr)
+ break;
+
+ /* When not found, give appropriate msg */
+ if (i != IESIZE) {
+ dp += sprintf(dp, " %s\n", ielist[i].descr);
+ dp += ielist[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]);
+
+ /* Skip to next element */
+ buf += buf[1] + 2;
+ }
+ }
+ dp += sprintf(dp, "\n");
+ teles_putstatus(sp->dlogspace);
+}
diff --git a/drivers/isdn/teles/tei.c b/drivers/isdn/teles/tei.c
new file mode 100644
index 000000000..3ab9f3646
--- /dev/null
+++ b/drivers/isdn/teles/tei.c
@@ -0,0 +1,248 @@
+/* $Id: tei.c,v 1.1 1996/04/13 10:28:25 fritz Exp $
+ *
+ * $Log: tei.c,v $
+ * Revision 1.1 1996/04/13 10:28:25 fritz
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "teles.h"
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+
+static struct PStack *
+findces(struct PStack *st, int ces)
+{
+ struct PStack *ptr = *(st->l1.stlistp);
+
+ while (ptr)
+ if (ptr->l2.ces == ces)
+ return (ptr);
+ else
+ ptr = ptr->next;
+ return (NULL);
+}
+
+static struct PStack *
+findtei(struct PStack *st, int tei)
+{
+ struct PStack *ptr = *(st->l1.stlistp);
+
+ if (tei == 127)
+ return (NULL);
+
+ while (ptr)
+ if (ptr->l2.tei == tei)
+ return (ptr);
+ else
+ ptr = ptr->next;
+ return (NULL);
+}
+
+void
+tei_handler(struct PStack *st,
+ byte pr, struct BufHeader *ibh)
+{
+ byte *bp;
+ unsigned int tces;
+ struct PStack *otsp, *ptr;
+ unsigned int data;
+
+ if (st->l2.debug)
+ printk(KERN_DEBUG "teihandler %d\n", pr);
+
+ switch (pr) {
+ case (MDL_ASSIGN):
+ data = (unsigned int) ibh;
+ BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 6);
+ if (!ibh)
+ return;
+ bp = DATAPTR(ibh);
+ bp += st->l2.uihsize;
+ bp[0] = 0xf;
+ bp[1] = data >> 8;
+ bp[2] = data & 0xff;
+ bp[3] = 0x1;
+ bp[4] = 0xff;
+ ibh->datasize = 8;
+ st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+ break;
+ case (DL_UNIT_DATA):
+ bp = DATAPTR(ibh);
+ bp += 3;
+ if (bp[0] != 0xf)
+ break;
+ switch (bp[3]) {
+ case (2):
+ tces = (bp[1] << 8) | bp[2];
+ BufPoolRelease(ibh);
+ if (st->l3.debug)
+ printk(KERN_DEBUG "tei identity assigned for %d=%d\n", tces,
+ bp[4] >> 1);
+ if ((otsp = findces(st, tces)))
+ otsp->ma.teil2(otsp, MDL_ASSIGN,
+ (void *)(bp[4] >> 1));
+ break;
+ case (4):
+ if (st->l3.debug)
+ printk(KERN_DEBUG "checking identity for %d\n", bp[4] >> 1);
+ if (bp[4] >> 1 == 0x7f) {
+ BufPoolRelease(ibh);
+ ptr = *(st->l1.stlistp);
+ while (ptr) {
+ if ((ptr->l2.tei & 0x7f) != 0x7f) {
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
+ break;
+ bp = DATAPTR(ibh);
+ bp += 3;
+ bp[0] = 0xf;
+ bp[1] = ptr->l2.ces >> 8;
+ bp[2] = ptr->l2.ces & 0xff;
+ bp[3] = 0x5;
+ bp[4] = (ptr->l2.tei << 1) | 1;
+ ibh->datasize = 8;
+ st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+ }
+ ptr = ptr->next;
+ }
+ } else {
+ otsp = findtei(st, bp[4] >> 1);
+ BufPoolRelease(ibh);
+ if (!otsp)
+ break;
+ if (st->l3.debug)
+ printk(KERN_DEBUG "ces is %d\n", otsp->l2.ces);
+ if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
+ break;
+ bp = DATAPTR(ibh);
+ bp += 3;
+ bp[0] = 0xf;
+ bp[1] = otsp->l2.ces >> 8;
+ bp[2] = otsp->l2.ces & 0xff;
+ bp[3] = 0x5;
+ bp[4] = (otsp->l2.tei << 1) | 1;
+ ibh->datasize = 8;
+ st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+ }
+ break;
+ default:
+ BufPoolRelease(ibh);
+ if (st->l3.debug)
+ printk(KERN_DEBUG "tei message unknown %d ai %d\n", bp[3], bp[4] >> 1);
+ }
+ break;
+ default:
+ printk(KERN_WARNING "tei handler unknown primitive %d\n", pr);
+ break;
+ }
+}
+
+unsigned int
+randomces(void)
+{
+ int x = jiffies & 0xffff;
+
+ return (x);
+}
+
+static void
+tei_man(struct PStack *sp, int i, void *v)
+{
+ printk(KERN_DEBUG "tei_man\n");
+}
+
+static void
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *sp = st->l1.hardware;
+
+ tei_handler(sp->teistack, pr, arg);
+}
+
+void
+setstack_tei(struct PStack *st)
+{
+ st->l2.l2tei = tei_l2tei;
+}
+
+static void
+init_tei(struct IsdnCardState *sp, int protocol)
+{
+ struct PStack *st;
+ char tmp[128];
+
+#define DIRTY_HACK_AGAINST_SIGSEGV
+
+ st = (struct PStack *) Smalloc(sizeof(struct PStack), GFP_KERNEL,
+ "struct PStack");
+
+#ifdef DIRTY_HACK_AGAINST_SIGSEGV
+ sp->teistack = st; /* struct is not initialized yet */
+ sp->teistack->protocol = protocol; /* struct is not initialized yet */
+#endif /* DIRTY_HACK_AGAINST_SIGSEGV */
+
+
+ setstack_teles(st, sp);
+
+ st->l2.extended = !0;
+ st->l2.laptype = LAPD;
+ st->l2.window = 1;
+ st->l2.orig = !0;
+ st->protocol = protocol;
+
+/*
+ * the following is not necessary for tei mng. (broadcast only)
+ */
+
+ st->l2.t200 = 500; /* 500 milliseconds */
+ st->l2.n200 = 4; /* try 4 times */
+
+ st->l2.sap = 63;
+ st->l2.tei = 127;
+
+ sprintf(tmp, "Card %d tei ", sp->cardnr);
+ setstack_isdnl2(st, tmp);
+ st->l2.debug = 0;
+ st->l3.debug = 0;
+
+ st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+
+ st->l2.l2l3 = (void *) tei_handler;
+ st->l1.l1man = tei_man;
+ st->l2.l2man = tei_man;
+ st->l4.l2writewakeup = NULL;
+
+ teles_addlist(sp, st);
+ sp->teistack = st;
+}
+
+static void
+release_tei(struct IsdnCardState *sp)
+{
+ struct PStack *st = sp->teistack;
+
+ teles_rmlist(sp, st);
+ Sfree((void *) st);
+}
+
+void
+TeiNew(void)
+{
+ int i;
+
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].sp)
+ init_tei(cards[i].sp, cards[i].protocol);
+}
+
+void
+TeiFree(void)
+{
+ int i;
+
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].sp)
+ release_tei(cards[i].sp);
+}
diff --git a/drivers/isdn/teles/teles.h b/drivers/isdn/teles/teles.h
new file mode 100644
index 000000000..502169a6b
--- /dev/null
+++ b/drivers/isdn/teles/teles.h
@@ -0,0 +1,488 @@
+/* $Id: teles.h,v 1.2 1996/04/30 21:52:04 isdn4dev Exp $
+ *
+ * $Log: teles.h,v $
+ * Revision 1.2 1996/04/30 21:52:04 isdn4dev
+ * SPV for 1TR6 - Karsten
+ *
+ * Revision 1.1 1996/04/13 10:29:00 fritz
+ * Initial revision
+ *
+ *
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define PH_ACTIVATE 1
+#define PH_DATA 2
+#define PH_DEACTIVATE 3
+
+#define MDL_ASSIGN 4
+#define DL_UNIT_DATA 5
+#define SC_STARTUP 6
+#define CC_ESTABLISH 7
+#define DL_ESTABLISH 8
+#define DL_DATA 9
+#define CC_S_STATUS_ENQ 10
+
+#define CC_CONNECT 15
+#define CC_CONNECT_ACKNOWLEDGE 16
+#define CO_EOF 17
+#define SC_DISCONNECT 18
+#define CO_DTMF 19
+#define DL_RELEASE 20
+
+#define CO_ALARM 22
+#define CC_REJECT 23
+
+#define CC_SETUP_REQ 24
+#define CC_SETUP_CNF 25
+#define CC_SETUP_IND 26
+#define CC_SETUP_RSP 27
+#define CC_SETUP_COMPLETE_IND 28
+
+#define CC_DISCONNECT_REQ 29
+#define CC_DISCONNECT_IND 30
+
+#define CC_RELEASE_CNF 31
+#define CC_RELEASE_IND 32
+#define CC_RELEASE_REQ 33
+
+#define CC_REJECT_REQ 34
+
+#define CC_PROCEEDING_IND 35
+
+#define CC_DLRL 36
+#define CC_DLEST 37
+
+#define CC_ALERTING_REQ 38
+#define CC_ALERTING_IND 39
+
+#define DL_STOP 40
+#define DL_START 41
+
+#define MDL_NOTEIPROC 46
+
+#define LC_ESTABLISH 47
+#define LC_RELEASE 48
+
+#define PH_REQUEST_PULL 49
+#define PH_PULL_ACK 50
+#define PH_DATA_PULLED 51
+#define CC_INFO_CHARGE 52
+
+/*
+ * Message-Types
+ */
+
+#define MT_ALERTING 0x01
+#define MT_CALL_PROCEEDING 0x02
+#define MT_CONNECT 0x07
+#define MT_CONNECT_ACKNOWLEDGE 0x0f
+#define MT_PROGRESS 0x03
+#define MT_SETUP 0x05
+#define MT_SETUP_ACKNOWLEDGE 0x0d
+#define MT_RESUME 0x26
+#define MT_RESUME_ACKNOWLEDGE 0x2e
+#define MT_RESUME_REJECT 0x22
+#define MT_SUSPEND 0x25
+#define MT_SUSPEND_ACKNOWLEDGE 0x2d
+#define MT_SUSPEND_REJECT 0x21
+#define MT_USER_INFORMATION 0x20
+#define MT_DISCONNECT 0x45
+#define MT_RELEASE 0x4d
+#define MT_RELEASE_COMPLETE 0x5a
+#define MT_RESTART 0x46
+#define MT_RESTART_ACKNOWLEDGE 0x4e
+#define MT_SEGMENT 0x60
+#define MT_CONGESTION_CONTROL 0x79
+#define MT_INFORMATION 0x7b
+#define MT_FACILITY 0x62
+#define MT_NOTIFY 0x6e
+#define MT_STATUS 0x7d
+#define MT_STATUS_ENQUIRY 0x75
+
+#define IE_CAUSE 0x08
+
+struct HscxIoctlArg {
+ int channel;
+ int mode;
+ int transbufsize;
+};
+
+#ifdef __KERNEL__
+
+#undef DEBUG_MAGIC
+
+#define HSCX_SBUF_ORDER 1
+#define HSCX_SBUF_BPPS 2
+#define HSCX_SBUF_MAXPAGES 3
+
+#define HSCX_RBUF_ORDER 1
+#define HSCX_RBUF_BPPS 2
+#define HSCX_RBUF_MAXPAGES 3
+
+#define HSCX_SMALLBUF_ORDER 0
+#define HSCX_SMALLBUF_BPPS 40
+#define HSCX_SMALLBUF_MAXPAGES 1
+
+#define ISAC_SBUF_ORDER 0
+#define ISAC_SBUF_BPPS 16
+#define ISAC_SBUF_MAXPAGES 1
+
+#define ISAC_RBUF_ORDER 0
+#define ISAC_RBUF_BPPS 16
+#define ISAC_RBUF_MAXPAGES 1
+
+#define ISAC_SMALLBUF_ORDER 0
+#define ISAC_SMALLBUF_BPPS 40
+#define ISAC_SMALLBUF_MAXPAGES 1
+
+#define byte unsigned char
+
+#define MAX_WINDOW 8
+
+byte *Smalloc(int size, int pr, char *why);
+void Sfree(byte * ptr);
+
+/*
+ * Statemachine
+ */
+struct Fsm {
+ int *jumpmatrix;
+ int state_count, event_count;
+ char **strEvent, **strState;
+};
+
+struct FsmInst {
+ struct Fsm *fsm;
+ int state;
+ int debug;
+ void *userdata;
+ int userint;
+ void (*printdebug) (struct FsmInst *, char *);
+};
+
+struct FsmNode {
+ int state, event;
+ void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+ struct FsmInst *fi;
+ struct timer_list tl;
+ int event;
+ void *arg;
+};
+
+struct BufHeader {
+#ifdef DEBUG_MAGIC
+ int magic;
+#endif
+ struct BufHeader *next;
+ struct BufPool *bp;
+ int datasize;
+ byte primitive, where;
+ void *heldby;
+};
+
+struct Pages {
+ struct Pages *next;
+};
+
+struct BufPool {
+#ifdef DEBUG_MAGIC
+ int magic;
+#endif
+ struct BufHeader *freelist;
+ struct Pages *pageslist;
+ int pageorder;
+ int pagescount;
+ int bpps;
+ int bufsize;
+ int maxpages;
+};
+
+struct BufQueue {
+#ifdef DEBUG_MAGIC
+ int magic;
+#endif
+ struct BufHeader *head, *tail;
+};
+
+struct Layer1 {
+ void *hardware;
+ int hscx;
+ struct BufPool *sbufpool, *rbufpool, *smallpool;
+ struct PStack **stlistp;
+ int act_state;
+ void (*l1l2) (struct PStack *, int, struct BufHeader *);
+ void (*l1man) (struct PStack *, int, void *);
+ int hscxmode, hscxchannel, requestpull;
+};
+
+struct Layer2 {
+ int sap, tei, ces;
+ int extended, laptype;
+ int uihsize, ihsize;
+ int vs, va, vr;
+ struct BufQueue i_queue;
+ int window, orig;
+ int rejexp;
+ int debug;
+ struct BufHeader *windowar[MAX_WINDOW];
+ int sow;
+ struct FsmInst l2m;
+ void (*l2l1) (struct PStack *, int, struct BufHeader *);
+ void (*l2l1discardq) (struct PStack *, int, void *, int);
+ void (*l2man) (struct PStack *, int, void *);
+ void (*l2l3) (struct PStack *, int, void *);
+ void (*l2tei) (struct PStack *, int, void *);
+ struct FsmTimer t200_timer, t203_timer;
+ int t200, n200, t203;
+ int rc, t200_running;
+ char debug_id[32];
+};
+
+struct Layer3 {
+ void (*l3l4) (struct PStack *, int, struct BufHeader *);
+ void (*l3l2) (struct PStack *, int, void *);
+ int state, callref;
+ int debug;
+};
+
+struct Layer4 {
+ void (*l4l3) (struct PStack *, int, void *);
+ void *userdata;
+ void (*l1writewakeup) (struct PStack *);
+ void (*l2writewakeup) (struct PStack *);
+};
+
+struct Management {
+ void (*manl1) (struct PStack *, int, void *);
+ void (*manl2) (struct PStack *, int, void *);
+ void (*teil2) (struct PStack *, int, void *);
+};
+
+struct Param {
+ int cause;
+ int bchannel;
+ int callref; /* TEI-Number */
+ int itc;
+ int info; /* Service-Indicator */
+ int info2; /* Service-Indicator, second octet */
+ char calling[40]; /* Called Id */
+ char called[40]; /* Caller Id */
+ int chargeinfo; /* Charge Info - only for 1tr6 in
+ * the moment
+ */
+ int spv; /* SPV Flag */
+};
+
+struct PStack {
+ struct PStack *next;
+ struct Layer1 l1;
+ struct Layer2 l2;
+ struct Layer3 l3;
+ struct Layer4 l4;
+ struct Management ma;
+ struct Param *pa;
+ int protocol; /* EDSS1 or 1TR6 */
+};
+
+struct HscxState {
+ unsigned int membase;
+ int iobase;
+ int inuse, init, active;
+ struct BufPool sbufpool, rbufpool, smallpool;
+ struct IsdnCardState *sp;
+ int hscx, mode;
+ int transbufsize, receive;
+ struct BufHeader *rcvibh, *xmtibh;
+ int rcvptr, sendptr;
+ struct PStack *st;
+ struct tq_struct tqueue;
+ int event;
+ struct BufQueue rq, sq;
+ int releasebuf;
+#ifdef DEBUG_MAGIC
+ int magic; /* 301270 */
+#endif
+};
+
+struct IsdnCardState {
+#ifdef DEBUG_MAGIC
+ int magic;
+#endif
+ unsigned int membase;
+ int iobase;
+ struct BufPool sbufpool, rbufpool, smallpool;
+ struct PStack *stlist;
+ struct BufHeader *xmtibh, *rcvibh;
+ int rcvptr, sendptr;
+ int event;
+ struct tq_struct tqueue;
+ int ph_active;
+ struct BufQueue rq, sq;
+
+ int cardnr, ph_state;
+ struct PStack *teistack;
+ struct HscxState hs[2];
+
+ int dlogflag;
+ char *dlogspace;
+ int debug;
+ int releasebuf;
+};
+
+struct IsdnCard {
+ unsigned int membase;
+ int interrupt;
+ unsigned int iobase;
+ int protocol; /* EDSS1 or 1TR6 */
+ struct IsdnCardState *sp;
+};
+
+#define DATAPTR(x) ((byte *)x+sizeof(struct BufHeader))
+
+#define LAPD 0
+#define LAPB 1
+
+void BufPoolInit(struct BufPool *bp, int order, int bpps,
+ int maxpages);
+int BufPoolAdd(struct BufPool *bp, int priority);
+void BufPoolFree(struct BufPool *bp);
+int BufPoolGet(struct BufHeader **bh,
+ struct BufPool *bp, int priority, void *heldby, int where);
+void BufPoolRelease(struct BufHeader *bh);
+void BufQueueLink(struct BufQueue *bq,
+ struct BufHeader *bh);
+int BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq);
+void BufQueueInit(struct BufQueue *bq);
+void BufQueueRelease(struct BufQueue *bq);
+void BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+ int releasetoo);
+int BufQueueLength(struct BufQueue *bq);
+void BufQueueLinkFront(struct BufQueue *bq,
+ struct BufHeader *bh);
+
+void l2down(struct PStack *st,
+ byte pr, struct BufHeader *ibh);
+void l2up(struct PStack *st,
+ byte pr, struct BufHeader *ibh);
+void acceptph(struct PStack *st,
+ struct BufHeader *ibh);
+void setstack_isdnl2(struct PStack *st, char *debug_id);
+int teles_inithardware(void);
+void teles_closehardware(void);
+
+void setstack_teles(struct PStack *st, struct IsdnCardState *sp);
+unsigned int randomces(void);
+void setstack_isdnl3(struct PStack *st);
+void teles_addlist(struct IsdnCardState *sp,
+ struct PStack *st);
+void releasestack_isdnl2(struct PStack *st);
+void teles_rmlist(struct IsdnCardState *sp,
+ struct PStack *st);
+void newcallref(struct PStack *st);
+
+int ll_init(void);
+void ll_stop(void), ll_unload(void);
+int setstack_hscx(struct PStack *st, struct HscxState *hs);
+void modehscx(struct HscxState *hs, int mode, int ichan);
+byte *findie(byte * p, int size, byte ie, int wanted_set);
+int getcallref(byte * p);
+
+void FsmNew(struct Fsm *fsm,
+ struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi,
+ int event, void *arg);
+void FsmChangeState(struct FsmInst *fi,
+ int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft,
+ int millisec, int event, void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+int FsmTimerRunning(struct FsmTimer *ft);
+void jiftime(char *s, long mark);
+
+void CallcNew(void);
+void CallcFree(void);
+int CallcNewChan(void);
+void CallcFreeChan(void);
+int teles_command(isdn_ctrl * ic);
+int teles_writebuf(int id, int chan, const u_char * buf, int count, int user);
+void teles_putstatus(char *buf);
+void teles_reportcard(int cardnr);
+int ListLength(struct BufHeader *ibh);
+void dlogframe(struct IsdnCardState *sp, byte * p, int size, char *comment);
+void iecpy(byte * dest, byte * iestart, int ieoffset);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+void close_hscxstate(struct HscxState *);
+void setstack_tei(struct PStack *st);
+
+struct LcFsm {
+ struct FsmInst lcfi;
+ int type;
+ struct Channel *ch;
+ void (*lccall) (struct LcFsm *, int, void *);
+ struct PStack *st;
+ int l2_establish;
+ int l2_start;
+ struct FsmTimer act_timer;
+ char debug_id[32];
+};
+
+struct Channel {
+ struct PStack ds, is;
+ struct IsdnCardState *sp;
+ int hscx;
+ int chan;
+ int incoming;
+ struct FsmInst fi;
+ struct LcFsm lc_d, lc_b;
+ struct Param para;
+ int debug;
+#ifdef DEBUG_MAGIC
+ int magic; /* 301272 */
+#endif
+ int l2_protocol, l2_active_protocol;
+ int l2_primitive, l2_headersize;
+ int data_open;
+ int outcallref;
+ int impair;
+};
+
+#define PART_SIZE(order,bpps) (( (PAGE_SIZE<<order) -\
+ sizeof(void *))/bpps)
+#define BUFFER_SIZE(order,bpps) (PART_SIZE(order,bpps)-\
+ sizeof(struct BufHeader))
+
+#endif
+
+void Isdnl2New(void);
+void Isdnl2Free(void);
+void TeiNew(void);
+void TeiFree(void);
+
+
+
+