isdn: move capi drivers to staging
authorArnd Bergmann <arnd@arndb.de>
Sat, 20 Apr 2019 20:28:45 +0000 (22:28 +0200)
committerArnd Bergmann <arnd@arndb.de>
Fri, 31 May 2019 09:17:41 +0000 (11:17 +0200)
I tried to find any indication of whether the capi drivers are still in
use, and have not found anything from a long time ago.

With public ISDN networks almost completely shut down over the past 12
months, there is very little you can actually do with this hardware. The
main remaining use case would be to connect ISDN voice phones to an
in-house installation with Asterisk or LCR, but anyone trying this in
turn seems to be using either the mISDN driver stack, or out-of-tree
drivers from the hardware vendors.

I may of course have missed something, so I would suggest moving these
three drivers (avm, hysdn, gigaset) into drivers/staging/ just in case
someone still uses them.

If nobody complains, we can remove them entirely in six months, or
otherwise move the core code and any drivers that are still needed back
into drivers/isdn.

As Paul Bolle notes, he is still testing the gigaset driver as long as
he can, but the Dutch ISDN network will be shut down in September 2019,
which puts an end to that.

Marcel Holtmann still maintains the Bluetooth CMTP profile and wants to
keep that alive, so the actual CAPI subsystem code remains in place for
now, after all other drivers are gone, CMTP and CAPI can be merged into
a single driver directory.

Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Paul Bolle <pebolle@tiscali.nl>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
92 files changed:
MAINTAINERS
drivers/isdn/Kconfig
drivers/isdn/Makefile
drivers/isdn/capi/Kconfig
drivers/isdn/capi/Makefile
drivers/isdn/gigaset/Kconfig [deleted file]
drivers/isdn/gigaset/Makefile [deleted file]
drivers/isdn/gigaset/asyncdata.c [deleted file]
drivers/isdn/gigaset/bas-gigaset.c [deleted file]
drivers/isdn/gigaset/capi.c [deleted file]
drivers/isdn/gigaset/common.c [deleted file]
drivers/isdn/gigaset/dummyll.c [deleted file]
drivers/isdn/gigaset/ev-layer.c [deleted file]
drivers/isdn/gigaset/gigaset.h [deleted file]
drivers/isdn/gigaset/interface.c [deleted file]
drivers/isdn/gigaset/isocdata.c [deleted file]
drivers/isdn/gigaset/proc.c [deleted file]
drivers/isdn/gigaset/ser-gigaset.c [deleted file]
drivers/isdn/gigaset/usb-gigaset.c [deleted file]
drivers/isdn/hardware/Kconfig [deleted file]
drivers/isdn/hardware/Makefile
drivers/isdn/hardware/avm/Kconfig [deleted file]
drivers/isdn/hardware/avm/Makefile [deleted file]
drivers/isdn/hardware/avm/avm_cs.c [deleted file]
drivers/isdn/hardware/avm/avmcard.h [deleted file]
drivers/isdn/hardware/avm/b1.c [deleted file]
drivers/isdn/hardware/avm/b1dma.c [deleted file]
drivers/isdn/hardware/avm/b1isa.c [deleted file]
drivers/isdn/hardware/avm/b1pci.c [deleted file]
drivers/isdn/hardware/avm/b1pcmcia.c [deleted file]
drivers/isdn/hardware/avm/c4.c [deleted file]
drivers/isdn/hardware/avm/t1isa.c [deleted file]
drivers/isdn/hardware/avm/t1pci.c [deleted file]
drivers/isdn/hysdn/Kconfig [deleted file]
drivers/isdn/hysdn/Makefile [deleted file]
drivers/isdn/hysdn/boardergo.c [deleted file]
drivers/isdn/hysdn/boardergo.h [deleted file]
drivers/isdn/hysdn/hycapi.c [deleted file]
drivers/isdn/hysdn/hysdn_boot.c [deleted file]
drivers/isdn/hysdn/hysdn_defs.h [deleted file]
drivers/isdn/hysdn/hysdn_init.c [deleted file]
drivers/isdn/hysdn/hysdn_net.c [deleted file]
drivers/isdn/hysdn/hysdn_pof.h [deleted file]
drivers/isdn/hysdn/hysdn_procconf.c [deleted file]
drivers/isdn/hysdn/hysdn_proclog.c [deleted file]
drivers/isdn/hysdn/hysdn_sched.c [deleted file]
drivers/isdn/hysdn/ince1pc.h [deleted file]
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/isdn/Kconfig [new file with mode: 0644]
drivers/staging/isdn/Makefile [new file with mode: 0644]
drivers/staging/isdn/TODO [new file with mode: 0644]
drivers/staging/isdn/avm/Kconfig [new file with mode: 0644]
drivers/staging/isdn/avm/Makefile [new file with mode: 0644]
drivers/staging/isdn/avm/avm_cs.c [new file with mode: 0644]
drivers/staging/isdn/avm/avmcard.h [new file with mode: 0644]
drivers/staging/isdn/avm/b1.c [new file with mode: 0644]
drivers/staging/isdn/avm/b1dma.c [new file with mode: 0644]
drivers/staging/isdn/avm/b1isa.c [new file with mode: 0644]
drivers/staging/isdn/avm/b1pci.c [new file with mode: 0644]
drivers/staging/isdn/avm/b1pcmcia.c [new file with mode: 0644]
drivers/staging/isdn/avm/c4.c [new file with mode: 0644]
drivers/staging/isdn/avm/t1isa.c [new file with mode: 0644]
drivers/staging/isdn/avm/t1pci.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/Kconfig [new file with mode: 0644]
drivers/staging/isdn/gigaset/Makefile [new file with mode: 0644]
drivers/staging/isdn/gigaset/asyncdata.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/bas-gigaset.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/capi.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/common.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/dummyll.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/ev-layer.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/gigaset.h [new file with mode: 0644]
drivers/staging/isdn/gigaset/interface.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/isocdata.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/proc.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/ser-gigaset.c [new file with mode: 0644]
drivers/staging/isdn/gigaset/usb-gigaset.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/Kconfig [new file with mode: 0644]
drivers/staging/isdn/hysdn/Makefile [new file with mode: 0644]
drivers/staging/isdn/hysdn/boardergo.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/boardergo.h [new file with mode: 0644]
drivers/staging/isdn/hysdn/hycapi.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_boot.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_defs.h [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_init.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_net.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_pof.h [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_procconf.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_proclog.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/hysdn_sched.c [new file with mode: 0644]
drivers/staging/isdn/hysdn/ince1pc.h [new file with mode: 0644]

index 3a761e6..2be31b1 100644 (file)
@@ -6679,9 +6679,7 @@ M:        Paul Bolle <pebolle@tiscali.nl>
 L:     gigaset307x-common@lists.sourceforge.net
 W:     http://gigaset307x.sourceforge.net/
 S:     Odd Fixes
-F:     Documentation/isdn/README.gigaset
-F:     drivers/isdn/gigaset/
-F:     include/uapi/linux/gigaset_dev.h
+F:     drivers/staging/isdn/gigaset/
 
 GNSS SUBSYSTEM
 M:     Johan Hovold <johan@kernel.org>
@@ -8362,15 +8360,25 @@ S:      Supported
 W:     http://www.linux-iscsi.org
 F:     drivers/infiniband/ulp/isert
 
-ISDN SUBSYSTEM
+ISDN/mISDN SUBSYSTEM
 M:     Karsten Keil <isdn@linux-pingi.de>
 L:     isdn4linux@listserv.isdn4linux.de (subscribers-only)
 L:     netdev@vger.kernel.org
 W:     http://www.isdn4linux.de
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kkeil/isdn-2.6.git
 S:     Maintained
+F:     drivers/isdn/mISDN
+F:     drivers/isdn/hardware
+
+ISDN/CAPI SUBSYSTEM
+M:     Karsten Keil <isdn@linux-pingi.de>
+L:     isdn4linux@listserv.isdn4linux.de (subscribers-only)
+L:     netdev@vger.kernel.org
+W:     http://www.isdn4linux.de
+S:     Odd Fixes
 F:     Documentation/isdn/
-F:     drivers/isdn/
+F:     drivers/isdn/capi/
+F:     drivers/staging/isdn/
+F:     net/bluetooth/cmtp/
 F:     include/linux/isdn/
 F:     include/uapi/linux/isdn/
 
index 6e3bf83..be8387c 100644 (file)
@@ -21,33 +21,8 @@ menuconfig ISDN
 
 if ISDN
 
-menuconfig ISDN_CAPI
-       tristate "CAPI 2.0 subsystem"
-       help
-         This provides CAPI (the Common ISDN Application Programming
-         Interface) Version 2.0, a standard making it easy for programs to
-         access ISDN hardware in a device independent way. (For details see
-         <http://www.capi.org/>.)  CAPI supports making and accepting voice
-         and data connections, controlling call options and protocols,
-         as well as ISDN supplementary services like call forwarding or
-         three-party conferences (if supported by the specific hardware
-         driver).
-
-         Select this option and the appropriate hardware driver below if
-         you have an ISDN adapter supported by the CAPI subsystem.
-
-if ISDN_CAPI
-
 source "drivers/isdn/capi/Kconfig"
 
-source "drivers/isdn/hardware/Kconfig"
-
-endif # ISDN_CAPI
-
-source "drivers/isdn/gigaset/Kconfig"
-
-source "drivers/isdn/hysdn/Kconfig"
-
 source "drivers/isdn/mISDN/Kconfig"
 
 endif # ISDN
index f2a529c..63baf27 100644 (file)
@@ -6,5 +6,3 @@
 obj-$(CONFIG_ISDN_CAPI)                        += capi/
 obj-$(CONFIG_MISDN)                    += mISDN/
 obj-$(CONFIG_ISDN)                     += hardware/
-obj-$(CONFIG_HYSDN)                    += hysdn/
-obj-$(CONFIG_ISDN_DRV_GIGASET)         += gigaset/
index 089dbee..573fea5 100644 (file)
@@ -1,4 +1,22 @@
 # SPDX-License-Identifier: GPL-2.0-only
+menuconfig ISDN_CAPI
+       tristate "CAPI 2.0 subsystem"
+       help
+         This provides CAPI (the Common ISDN Application Programming
+         Interface) Version 2.0, a standard making it easy for programs to
+         access ISDN hardware in a device independent way. (For details see
+         <http://www.capi.org/>.)  CAPI supports making and accepting voice
+         and data connections, controlling call options and protocols,
+         as well as ISDN supplementary services like call forwarding or
+         three-party conferences (if supported by the specific hardware
+         driver).
+
+         This subsystem requires a hardware specific driver.
+         See CONFIG_BT_CMTP for the last remaining regular driver
+         in the kernel that uses the CAPI subsystem.
+
+if ISDN_CAPI
+
 config CAPI_TRACE
        bool "CAPI trace support"
        default y
@@ -34,3 +52,5 @@ config ISDN_CAPI_CAPIDRV_VERBOSE
          If you say Y here, the capidrv interface will give verbose reasons
          for disconnecting. This will increase the size of the kernel by 7 KB.
          If unsure, say N.
+
+endif
index 06da3ed..d299f3e 100644 (file)
@@ -13,3 +13,5 @@ obj-$(CONFIG_ISDN_CAPI_CAPIDRV)               += capidrv.o
 
 kernelcapi-y                           := kcapi.o capiutil.o capilib.o
 kernelcapi-$(CONFIG_PROC_FS)           += kcapi_proc.o
+
+ccflags-y += -I$(srctree)/$(src)/../include -I$(srctree)/$(src)/../include/uapi
diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig
deleted file mode 100644 (file)
index c593105..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-menuconfig ISDN_DRV_GIGASET
-       tristate "Siemens Gigaset support"
-       depends on TTY
-       select CRC_CCITT
-       select BITREVERSE
-       help
-         This driver supports the Siemens Gigaset SX205/255 family of
-         ISDN DECT bases, including the predecessors Gigaset 3070/3075
-         and 4170/4175 and their T-Com versions Sinus 45isdn and Sinus
-         721X.
-         If you have one of these devices, say M here and for at least
-         one of the connection specific parts that follow.
-         This will build a module called "gigaset".
-         Note: If you build your ISDN subsystem (ISDN_CAPI or ISDN_I4L)
-         as a module, you have to build this driver as a module too,
-         otherwise the Gigaset device won't show up as an ISDN device.
-
-if ISDN_DRV_GIGASET
-
-config GIGASET_CAPI
-       bool "Gigaset CAPI support"
-       depends on ISDN_CAPI='y'||(ISDN_CAPI='m'&&ISDN_DRV_GIGASET='m')
-       default 'y'
-       help
-         Build the Gigaset driver as a CAPI 2.0 driver interfacing with
-         the Kernel CAPI subsystem. To use it with the old ISDN4Linux
-         subsystem you'll have to enable the capidrv glue driver.
-         (select ISDN_CAPI_CAPIDRV.)
-         Say N to build the old native ISDN4Linux variant.
-         If unsure, say Y.
-
-config GIGASET_BASE
-       tristate "Gigaset base station support"
-       depends on USB
-       help
-         Say M here if you want to use the USB interface of the Gigaset
-         base for connection to your system.
-         This will build a module called "bas_gigaset".
-
-config GIGASET_M105
-       tristate "Gigaset M105 support"
-       depends on USB
-       help
-         Say M here if you want to connect to the Gigaset base via DECT
-         using a Gigaset M105 (Sinus 45 Data 2) USB DECT device.
-         This will build a module called "usb_gigaset".
-
-config GIGASET_M101
-       tristate "Gigaset M101 support"
-       help
-         Say M here if you want to connect to the Gigaset base via DECT
-         using a Gigaset M101 (Sinus 45 Data 1) RS232 DECT device.
-         This will build a module called "ser_gigaset".
-
-config GIGASET_DEBUG
-       bool "Gigaset debugging"
-       help
-         This enables debugging code in the Gigaset drivers.
-         If in doubt, say yes.
-
-endif # ISDN_DRV_GIGASET
diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile
deleted file mode 100644 (file)
index 9c01089..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
-
-ifdef CONFIG_GIGASET_CAPI
-gigaset-y += capi.o
-else
-gigaset-y += dummyll.o
-endif
-
-usb_gigaset-y := usb-gigaset.o
-ser_gigaset-y := ser-gigaset.o
-bas_gigaset-y := bas-gigaset.o isocdata.o
-
-obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset.o
-obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o
-obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o
-obj-$(CONFIG_GIGASET_M101) += ser_gigaset.o
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
deleted file mode 100644 (file)
index c0cbee0..0000000
+++ /dev/null
@@ -1,609 +0,0 @@
-/*
- * Common data handling layer for ser_gigaset and usb_gigaset
- *
- * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
- *                       Hansjoerg Lipp <hjlipp@web.de>,
- *                       Stefan Eilers.
- *
- * =====================================================================
- *     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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/crc-ccitt.h>
-#include <linux/bitrev.h>
-#include <linux/export.h>
-
-/* check if byte must be stuffed/escaped
- * I'm not sure which data should be encoded.
- * Therefore I will go the hard way and encode every value
- * less than 0x20, the flag sequence and the control escape char.
- */
-static inline int muststuff(unsigned char c)
-{
-       if (c < PPP_TRANS) return 1;
-       if (c == PPP_FLAG) return 1;
-       if (c == PPP_ESCAPE) return 1;
-       /* other possible candidates: */
-       /* 0x91: XON with parity set */
-       /* 0x93: XOFF with parity set */
-       return 0;
-}
-
-/* == data input =========================================================== */
-
-/* process a block of received bytes in command mode
- * (mstate != MS_LOCKED && (inputstate & INS_command))
- * Append received bytes to the command response buffer and forward them
- * line by line to the response handler. Exit whenever a mode/state change
- * might have occurred.
- * Note: Received lines may be terminated by CR, LF, or CR LF, which will be
- * removed before passing the line to the response handler.
- * Return value:
- *     number of processed bytes
- */
-static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)
-{
-       unsigned char *src = inbuf->data + inbuf->head;
-       struct cardstate *cs = inbuf->cs;
-       unsigned cbytes = cs->cbytes;
-       unsigned procbytes = 0;
-       unsigned char c;
-
-       while (procbytes < numbytes) {
-               c = *src++;
-               procbytes++;
-
-               switch (c) {
-               case '\n':
-                       if (cbytes == 0 && cs->respdata[0] == '\r') {
-                               /* collapse LF with preceding CR */
-                               cs->respdata[0] = 0;
-                               break;
-                       }
-                       /* fall through */
-               case '\r':
-                       /* end of message line, pass to response handler */
-                       if (cbytes >= MAX_RESP_SIZE) {
-                               dev_warn(cs->dev, "response too large (%d)\n",
-                                        cbytes);
-                               cbytes = MAX_RESP_SIZE;
-                       }
-                       cs->cbytes = cbytes;
-                       gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
-                                          cbytes, cs->respdata);
-                       gigaset_handle_modem_response(cs);
-                       cbytes = 0;
-
-                       /* store EOL byte for CRLF collapsing */
-                       cs->respdata[0] = c;
-
-                       /* cs->dle may have changed */
-                       if (cs->dle && !(inbuf->inputstate & INS_DLE_command))
-                               inbuf->inputstate &= ~INS_command;
-
-                       /* return for reevaluating state */
-                       goto exit;
-
-               case DLE_FLAG:
-                       if (inbuf->inputstate & INS_DLE_char) {
-                               /* quoted DLE: clear quote flag */
-                               inbuf->inputstate &= ~INS_DLE_char;
-                       } else if (cs->dle ||
-                                  (inbuf->inputstate & INS_DLE_command)) {
-                               /* DLE escape, pass up for handling */
-                               inbuf->inputstate |= INS_DLE_char;
-                               goto exit;
-                       }
-                       /* quoted or not in DLE mode: treat as regular data */
-                       /* fall through */
-               default:
-                       /* append to line buffer if possible */
-                       if (cbytes < MAX_RESP_SIZE)
-                               cs->respdata[cbytes] = c;
-                       cbytes++;
-               }
-       }
-exit:
-       cs->cbytes = cbytes;
-       return procbytes;
-}
-
-/* process a block of received bytes in lock mode
- * All received bytes are passed unmodified to the tty i/f.
- * Return value:
- *     number of processed bytes
- */
-static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)
-{
-       unsigned char *src = inbuf->data + inbuf->head;
-
-       gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src);
-       gigaset_if_receive(inbuf->cs, src, numbytes);
-       return numbytes;
-}
-
-/* process a block of received bytes in HDLC data mode
- * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
- * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
- * When a frame is complete, check the FCS and pass valid frames to the LL.
- * If DLE is encountered, return immediately to let the caller handle it.
- * Return value:
- *     number of processed bytes
- */
-static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
-{
-       struct cardstate *cs = inbuf->cs;
-       struct bc_state *bcs = cs->bcs;
-       int inputstate = bcs->inputstate;
-       __u16 fcs = bcs->rx_fcs;
-       struct sk_buff *skb = bcs->rx_skb;
-       unsigned char *src = inbuf->data + inbuf->head;
-       unsigned procbytes = 0;
-       unsigned char c;
-
-       if (inputstate & INS_byte_stuff) {
-               if (!numbytes)
-                       return 0;
-               inputstate &= ~INS_byte_stuff;
-               goto byte_stuff;
-       }
-
-       while (procbytes < numbytes) {
-               c = *src++;
-               procbytes++;
-               if (c == DLE_FLAG) {
-                       if (inputstate & INS_DLE_char) {
-                               /* quoted DLE: clear quote flag */
-                               inputstate &= ~INS_DLE_char;
-                       } else if (cs->dle || (inputstate & INS_DLE_command)) {
-                               /* DLE escape, pass up for handling */
-                               inputstate |= INS_DLE_char;
-                               break;
-                       }
-               }
-
-               if (c == PPP_ESCAPE) {
-                       /* byte stuffing indicator: pull in next byte */
-                       if (procbytes >= numbytes) {
-                               /* end of buffer, save for later processing */
-                               inputstate |= INS_byte_stuff;
-                               break;
-                       }
-byte_stuff:
-                       c = *src++;
-                       procbytes++;
-                       if (c == DLE_FLAG) {
-                               if (inputstate & INS_DLE_char) {
-                                       /* quoted DLE: clear quote flag */
-                                       inputstate &= ~INS_DLE_char;
-                               } else if (cs->dle ||
-                                          (inputstate & INS_DLE_command)) {
-                                       /* DLE escape, pass up for handling */
-                                       inputstate |=
-                                               INS_DLE_char | INS_byte_stuff;
-                                       break;
-                               }
-                       }
-                       c ^= PPP_TRANS;
-#ifdef CONFIG_GIGASET_DEBUG
-                       if (!muststuff(c))
-                               gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
-#endif
-               } else if (c == PPP_FLAG) {
-                       /* end of frame: process content if any */
-                       if (inputstate & INS_have_data) {
-                               gig_dbg(DEBUG_HDLC,
-                                       "7e----------------------------");
-
-                               /* check and pass received frame */
-                               if (!skb) {
-                                       /* skipped frame */
-                                       gigaset_isdn_rcv_err(bcs);
-                               } else if (skb->len < 2) {
-                                       /* frame too short for FCS */
-                                       dev_warn(cs->dev,
-                                                "short frame (%d)\n",
-                                                skb->len);
-                                       gigaset_isdn_rcv_err(bcs);
-                                       dev_kfree_skb_any(skb);
-                               } else if (fcs != PPP_GOODFCS) {
-                                       /* frame check error */
-                                       dev_err(cs->dev,
-                                               "Checksum failed, %u bytes corrupted!\n",
-                                               skb->len);
-                                       gigaset_isdn_rcv_err(bcs);
-                                       dev_kfree_skb_any(skb);
-                               } else {
-                                       /* good frame */
-                                       __skb_trim(skb, skb->len - 2);
-                                       gigaset_skb_rcvd(bcs, skb);
-                               }
-
-                               /* prepare reception of next frame */
-                               inputstate &= ~INS_have_data;
-                               skb = gigaset_new_rx_skb(bcs);
-                       } else {
-                               /* empty frame (7E 7E) */
-#ifdef CONFIG_GIGASET_DEBUG
-                               ++bcs->emptycount;
-#endif
-                               if (!skb) {
-                                       /* skipped (?) */
-                                       gigaset_isdn_rcv_err(bcs);
-                                       skb = gigaset_new_rx_skb(bcs);
-                               }
-                       }
-
-                       fcs = PPP_INITFCS;
-                       continue;
-#ifdef CONFIG_GIGASET_DEBUG
-               } else if (muststuff(c)) {
-                       /* Should not happen. Possible after ZDLE=1<CR><LF>. */
-                       gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
-#endif
-               }
-
-               /* regular data byte, append to skb */
-#ifdef CONFIG_GIGASET_DEBUG
-               if (!(inputstate & INS_have_data)) {
-                       gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
-                               bcs->emptycount);
-                       bcs->emptycount = 0;
-               }
-#endif
-               inputstate |= INS_have_data;
-               if (skb) {
-                       if (skb->len >= bcs->rx_bufsize) {
-                               dev_warn(cs->dev, "received packet too long\n");
-                               dev_kfree_skb_any(skb);
-                               /* skip remainder of packet */
-                               bcs->rx_skb = skb = NULL;
-                       } else {
-                               __skb_put_u8(skb, c);
-                               fcs = crc_ccitt_byte(fcs, c);
-                       }
-               }
-       }
-
-       bcs->inputstate = inputstate;
-       bcs->rx_fcs = fcs;
-       return procbytes;
-}
-
-/* process a block of received bytes in transparent data mode
- * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC)
- * Invert bytes, undoing byte stuffing and watching for DLE escapes.
- * If DLE is encountered, return immediately to let the caller handle it.
- * Return value:
- *     number of processed bytes
- */
-static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
-{
-       struct cardstate *cs = inbuf->cs;
-       struct bc_state *bcs = cs->bcs;
-       int inputstate = bcs->inputstate;
-       struct sk_buff *skb = bcs->rx_skb;
-       unsigned char *src = inbuf->data + inbuf->head;
-       unsigned procbytes = 0;
-       unsigned char c;
-
-       if (!skb) {
-               /* skip this block */
-               gigaset_new_rx_skb(bcs);
-               return numbytes;
-       }
-
-       while (procbytes < numbytes && skb->len < bcs->rx_bufsize) {
-               c = *src++;
-               procbytes++;
-
-               if (c == DLE_FLAG) {
-                       if (inputstate & INS_DLE_char) {
-                               /* quoted DLE: clear quote flag */
-                               inputstate &= ~INS_DLE_char;
-                       } else if (cs->dle || (inputstate & INS_DLE_command)) {
-                               /* DLE escape, pass up for handling */
-                               inputstate |= INS_DLE_char;
-                               break;
-                       }
-               }
-
-               /* regular data byte: append to current skb */
-               inputstate |= INS_have_data;
-               __skb_put_u8(skb, bitrev8(c));
-       }
-
-       /* pass data up */
-       if (inputstate & INS_have_data) {
-               gigaset_skb_rcvd(bcs, skb);
-               inputstate &= ~INS_have_data;
-               gigaset_new_rx_skb(bcs);
-       }
-
-       bcs->inputstate = inputstate;
-       return procbytes;
-}
-
-/* process DLE escapes
- * Called whenever a DLE sequence might be encountered in the input stream.
- * Either processes the entire DLE sequence or, if that isn't possible,
- * notes the fact that an initial DLE has been received in the INS_DLE_char
- * inputstate flag and resumes processing of the sequence on the next call.
- */
-static void handle_dle(struct inbuf_t *inbuf)
-{
-       struct cardstate *cs = inbuf->cs;
-
-       if (cs->mstate == MS_LOCKED)
-               return;         /* no DLE processing in lock mode */
-
-       if (!(inbuf->inputstate & INS_DLE_char)) {
-               /* no DLE pending */
-               if (inbuf->data[inbuf->head] == DLE_FLAG &&
-                   (cs->dle || inbuf->inputstate & INS_DLE_command)) {
-                       /* start of DLE sequence */
-                       inbuf->head++;
-                       if (inbuf->head == inbuf->tail ||
-                           inbuf->head == RBUFSIZE) {
-                               /* end of buffer, save for later processing */
-                               inbuf->inputstate |= INS_DLE_char;
-                               return;
-                       }
-               } else {
-                       /* regular data byte */
-                       return;
-               }
-       }
-
-       /* consume pending DLE */
-       inbuf->inputstate &= ~INS_DLE_char;
-
-       switch (inbuf->data[inbuf->head]) {
-       case 'X':       /* begin of event message */
-               if (inbuf->inputstate & INS_command)
-                       dev_notice(cs->dev,
-                                  "received <DLE>X in command mode\n");
-               inbuf->inputstate |= INS_command | INS_DLE_command;
-               inbuf->head++;  /* byte consumed */
-               break;
-       case '.':       /* end of event message */
-               if (!(inbuf->inputstate & INS_DLE_command))
-                       dev_notice(cs->dev,
-                                  "received <DLE>. without <DLE>X\n");
-               inbuf->inputstate &= ~INS_DLE_command;
-               /* return to data mode if in DLE mode */
-               if (cs->dle)
-                       inbuf->inputstate &= ~INS_command;
-               inbuf->head++;  /* byte consumed */
-               break;
-       case DLE_FLAG:  /* DLE in data stream */
-               /* mark as quoted */
-               inbuf->inputstate |= INS_DLE_char;
-               if (!(cs->dle || inbuf->inputstate & INS_DLE_command))
-                       dev_notice(cs->dev,
-                                  "received <DLE><DLE> not in DLE mode\n");
-               break;  /* quoted byte left in buffer */
-       default:
-               dev_notice(cs->dev, "received <DLE><%02x>\n",
-                          inbuf->data[inbuf->head]);
-               /* quoted byte left in buffer */
-       }
-}
-
-/**
- * gigaset_m10x_input() - process a block of data received from the device
- * @inbuf:     received data and device descriptor structure.
- *
- * Called by hardware module {ser,usb}_gigaset with a block of received
- * bytes. Separates the bytes received over the serial data channel into
- * user data and command replies (locked/unlocked) according to the
- * current state of the interface.
- */
-void gigaset_m10x_input(struct inbuf_t *inbuf)
-{
-       struct cardstate *cs = inbuf->cs;
-       unsigned numbytes, procbytes;
-
-       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail);
-
-       while (inbuf->head != inbuf->tail) {
-               /* check for DLE escape */
-               handle_dle(inbuf);
-
-               /* process a contiguous block of bytes */
-               numbytes = (inbuf->head > inbuf->tail ?
-                           RBUFSIZE : inbuf->tail) - inbuf->head;
-               gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
-               /*
-                * numbytes may be 0 if handle_dle() ate the last byte.
-                * This does no harm, *_loop() will just return 0 immediately.
-                */
-
-               if (cs->mstate == MS_LOCKED)
-                       procbytes = lock_loop(numbytes, inbuf);
-               else if (inbuf->inputstate & INS_command)
-                       procbytes = cmd_loop(numbytes, inbuf);
-               else if (cs->bcs->proto2 == L2_HDLC)
-                       procbytes = hdlc_loop(numbytes, inbuf);
-               else
-                       procbytes = iraw_loop(numbytes, inbuf);
-               inbuf->head += procbytes;
-
-               /* check for buffer wraparound */
-               if (inbuf->head >= RBUFSIZE)
-                       inbuf->head = 0;
-
-               gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head);
-       }
-}
-EXPORT_SYMBOL_GPL(gigaset_m10x_input);
-
-
-/* == data output ========================================================== */
-
-/*
- * Encode a data packet into an octet stuffed HDLC frame with FCS,
- * opening and closing flags, preserving headroom data.
- * parameters:
- *     skb             skb containing original packet (freed upon return)
- * Return value:
- *     pointer to newly allocated skb containing the result frame
- *     and the original link layer header, NULL on error
- */
-static struct sk_buff *HDLC_Encode(struct sk_buff *skb)
-{
-       struct sk_buff *hdlc_skb;
-       __u16 fcs;
-       unsigned char c;
-       unsigned char *cp;
-       int len;
-       unsigned int stuf_cnt;
-
-       stuf_cnt = 0;
-       fcs = PPP_INITFCS;
-       cp = skb->data;
-       len = skb->len;
-       while (len--) {
-               if (muststuff(*cp))
-                       stuf_cnt++;
-               fcs = crc_ccitt_byte(fcs, *cp++);
-       }
-       fcs ^= 0xffff;                  /* complement */
-
-       /* size of new buffer: original size + number of stuffing bytes
-        * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
-        * + room for link layer header
-        */
-       hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len);
-       if (!hdlc_skb) {
-               dev_kfree_skb_any(skb);
-               return NULL;
-       }
-
-       /* Copy link layer header into new skb */
-       skb_reset_mac_header(hdlc_skb);
-       skb_reserve(hdlc_skb, skb->mac_len);
-       memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len);
-       hdlc_skb->mac_len = skb->mac_len;
-
-       /* Add flag sequence in front of everything.. */
-       skb_put_u8(hdlc_skb, PPP_FLAG);
-
-       /* Perform byte stuffing while copying data. */
-       while (skb->len--) {
-               if (muststuff(*skb->data)) {
-                       skb_put_u8(hdlc_skb, PPP_ESCAPE);
-                       skb_put_u8(hdlc_skb, (*skb->data++) ^ PPP_TRANS);
-               } else
-                       skb_put_u8(hdlc_skb, *skb->data++);
-       }
-
-       /* Finally add FCS (byte stuffed) and flag sequence */
-       c = (fcs & 0x00ff);     /* least significant byte first */
-       if (muststuff(c)) {
-               skb_put_u8(hdlc_skb, PPP_ESCAPE);
-               c ^= PPP_TRANS;
-       }
-       skb_put_u8(hdlc_skb, c);
-
-       c = ((fcs >> 8) & 0x00ff);
-       if (muststuff(c)) {
-               skb_put_u8(hdlc_skb, PPP_ESCAPE);
-               c ^= PPP_TRANS;
-       }
-       skb_put_u8(hdlc_skb, c);
-
-       skb_put_u8(hdlc_skb, PPP_FLAG);
-
-       dev_kfree_skb_any(skb);
-       return hdlc_skb;
-}
-
-/*
- * Encode a data packet into an octet stuffed raw bit inverted frame,
- * preserving headroom data.
- * parameters:
- *     skb             skb containing original packet (freed upon return)
- * Return value:
- *     pointer to newly allocated skb containing the result frame
- *     and the original link layer header, NULL on error
- */
-static struct sk_buff *iraw_encode(struct sk_buff *skb)
-{
-       struct sk_buff *iraw_skb;
-       unsigned char c;
-       unsigned char *cp;
-       int len;
-
-       /* size of new buffer (worst case = every byte must be stuffed):
-        * 2 * original size + room for link layer header
-        */
-       iraw_skb = dev_alloc_skb(2 * skb->len + skb->mac_len);
-       if (!iraw_skb) {
-               dev_kfree_skb_any(skb);
-               return NULL;
-       }
-
-       /* copy link layer header into new skb */
-       skb_reset_mac_header(iraw_skb);
-       skb_reserve(iraw_skb, skb->mac_len);
-       memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len);
-       iraw_skb->mac_len = skb->mac_len;
-
-       /* copy and stuff data */
-       cp = skb->data;
-       len = skb->len;
-       while (len--) {
-               c = bitrev8(*cp++);
-               if (c == DLE_FLAG)
-                       skb_put_u8(iraw_skb, c);
-               skb_put_u8(iraw_skb, c);
-       }
-       dev_kfree_skb_any(skb);
-       return iraw_skb;
-}
-
-/**
- * gigaset_m10x_send_skb() - queue an skb for sending
- * @bcs:       B channel descriptor structure.
- * @skb:       data to send.
- *
- * Called by LL to encode and queue an skb for sending, and start
- * transmission if necessary.
- * Once the payload data has been transmitted completely, gigaset_skb_sent()
- * will be called with the skb's link layer header preserved.
- *
- * Return value:
- *     number of bytes accepted for sending (skb->len) if ok,
- *     error code < 0 (eg. -ENOMEM) on error
- */
-int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
-{
-       struct cardstate *cs = bcs->cs;
-       unsigned len = skb->len;
-       unsigned long flags;
-
-       if (bcs->proto2 == L2_HDLC)
-               skb = HDLC_Encode(skb);
-       else
-               skb = iraw_encode(skb);
-       if (!skb) {
-               dev_err(cs->dev,
-                       "unable to allocate memory for encoding!\n");
-               return -ENOMEM;
-       }
-
-       skb_queue_tail(&bcs->squeue, skb);
-       spin_lock_irqsave(&cs->lock, flags);
-       if (cs->connected)
-               tasklet_schedule(&cs->write_tasklet);
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       return len;     /* ok so far */
-}
-EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
deleted file mode 100644 (file)
index 149b1ac..0000000
+++ /dev/null
@@ -1,2675 +0,0 @@
-/*
- * USB driver for Gigaset 307x base via direct USB connection.
- *
- * Copyright (c) 2001 by Hansjoerg Lipp <hjlipp@web.de>,
- *                       Tilman Schmidt <tilman@imap.cc>,
- *                       Stefan Eilers.
- *
- * =====================================================================
- *     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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/usb.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-/* Version Information */
-#define DRIVER_AUTHOR "Tilman Schmidt <tilman@imap.cc>, Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
-#define DRIVER_DESC "USB Driver for Gigaset 307x"
-
-
-/* Module parameters */
-
-static int startmode = SM_ISDN;
-static int cidmode = 1;
-
-module_param(startmode, int, S_IRUGO);
-module_param(cidmode, int, S_IRUGO);
-MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
-MODULE_PARM_DESC(cidmode, "Call-ID mode");
-
-#define GIGASET_MINORS     1
-#define GIGASET_MINOR      16
-#define GIGASET_MODULENAME "bas_gigaset"
-#define GIGASET_DEVNAME    "ttyGB"
-
-/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
-#define IF_WRITEBUF 264
-
-/* interrupt pipe message size according to ibid. ch. 2.2 */
-#define IP_MSGSIZE 3
-
-/* Values for the Gigaset 307x */
-#define USB_GIGA_VENDOR_ID      0x0681
-#define USB_3070_PRODUCT_ID     0x0001
-#define USB_3075_PRODUCT_ID     0x0002
-#define USB_SX303_PRODUCT_ID    0x0021
-#define USB_SX353_PRODUCT_ID    0x0022
-
-/* table of devices that work with this driver */
-static const struct usb_device_id gigaset_table[] = {
-       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) },
-       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) },
-       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) },
-       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX353_PRODUCT_ID) },
-       { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, gigaset_table);
-
-/*======================= local function prototypes ==========================*/
-
-/* function called if a new device belonging to this driver is connected */
-static int gigaset_probe(struct usb_interface *interface,
-                        const struct usb_device_id *id);
-
-/* Function will be called if the device is unplugged */
-static void gigaset_disconnect(struct usb_interface *interface);
-
-/* functions called before/after suspend */
-static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
-static int gigaset_resume(struct usb_interface *intf);
-
-/* functions called before/after device reset */
-static int gigaset_pre_reset(struct usb_interface *intf);
-static int gigaset_post_reset(struct usb_interface *intf);
-
-static int atread_submit(struct cardstate *, int);
-static void stopurbs(struct bas_bc_state *);
-static int req_submit(struct bc_state *, int, int, int);
-static int atwrite_submit(struct cardstate *, unsigned char *, int);
-static int start_cbsend(struct cardstate *);
-
-/*============================================================================*/
-
-struct bas_cardstate {
-       struct usb_device       *udev;          /* USB device pointer */
-       struct cardstate        *cs;
-       struct usb_interface    *interface;     /* interface for this device */
-       unsigned char           minor;          /* starting minor number */
-
-       struct urb              *urb_ctrl;      /* control pipe default URB */
-       struct usb_ctrlrequest  dr_ctrl;
-       struct timer_list       timer_ctrl;     /* control request timeout */
-       int                     retry_ctrl;
-
-       struct timer_list       timer_atrdy;    /* AT command ready timeout */
-       struct urb              *urb_cmd_out;   /* for sending AT commands */
-       struct usb_ctrlrequest  dr_cmd_out;
-       int                     retry_cmd_out;
-
-       struct urb              *urb_cmd_in;    /* for receiving AT replies */
-       struct usb_ctrlrequest  dr_cmd_in;
-       struct timer_list       timer_cmd_in;   /* receive request timeout */
-       unsigned char           *rcvbuf;        /* AT reply receive buffer */
-
-       struct urb              *urb_int_in;    /* URB for interrupt pipe */
-       unsigned char           *int_in_buf;
-       struct work_struct      int_in_wq;      /* for usb_clear_halt() */
-       struct timer_list       timer_int_in;   /* int read retry delay */
-       int                     retry_int_in;
-
-       spinlock_t              lock;           /* locks all following */
-       int                     basstate;       /* bitmap (BS_*) */
-       int                     pending;        /* uncompleted base request */
-       wait_queue_head_t       waitqueue;
-       int                     rcvbuf_size;    /* size of AT receive buffer */
-                                               /* 0: no receive in progress */
-       int                     retry_cmd_in;   /* receive req retry count */
-};
-
-/* status of direct USB connection to 307x base (bits in basstate) */
-#define BS_ATOPEN      0x001   /* AT channel open */
-#define BS_B1OPEN      0x002   /* B channel 1 open */
-#define BS_B2OPEN      0x004   /* B channel 2 open */
-#define BS_ATREADY     0x008   /* base ready for AT command */
-#define BS_INIT                0x010   /* base has signalled INIT_OK */
-#define BS_ATTIMER     0x020   /* waiting for HD_READY_SEND_ATDATA */
-#define BS_ATRDPEND    0x040   /* urb_cmd_in in use */
-#define BS_ATWRPEND    0x080   /* urb_cmd_out in use */
-#define BS_SUSPEND     0x100   /* USB port suspended */
-#define BS_RESETTING   0x200   /* waiting for HD_RESET_INTERRUPT_PIPE_ACK */
-
-
-static struct gigaset_driver *driver;
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver gigaset_usb_driver = {
-       .name =         GIGASET_MODULENAME,
-       .probe =        gigaset_probe,
-       .disconnect =   gigaset_disconnect,
-       .id_table =     gigaset_table,
-       .suspend =      gigaset_suspend,
-       .resume =       gigaset_resume,
-       .reset_resume = gigaset_post_reset,
-       .pre_reset =    gigaset_pre_reset,
-       .post_reset =   gigaset_post_reset,
-       .disable_hub_initiated_lpm = 1,
-};
-
-/* get message text for usb_submit_urb return code
- */
-static char *get_usb_rcmsg(int rc)
-{
-       static char unkmsg[28];
-
-       switch (rc) {
-       case 0:
-               return "success";
-       case -ENOMEM:
-               return "out of memory";
-       case -ENODEV:
-               return "device not present";
-       case -ENOENT:
-               return "endpoint not present";
-       case -ENXIO:
-               return "URB type not supported";
-       case -EINVAL:
-               return "invalid argument";
-       case -EAGAIN:
-               return "start frame too early or too much scheduled";
-       case -EFBIG:
-               return "too many isoc frames requested";
-       case -EPIPE:
-               return "endpoint stalled";
-       case -EMSGSIZE:
-               return "invalid packet size";
-       case -ENOSPC:
-               return "would overcommit USB bandwidth";
-       case -ESHUTDOWN:
-               return "device shut down";
-       case -EPERM:
-               return "reject flag set";
-       case -EHOSTUNREACH:
-               return "device suspended";
-       default:
-               snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", rc);
-               return unkmsg;
-       }
-}
-
-/* get message text for USB status code
- */
-static char *get_usb_statmsg(int status)
-{
-       static char unkmsg[28];
-
-       switch (status) {
-       case 0:
-               return "success";
-       case -ENOENT:
-               return "unlinked (sync)";
-       case -EINPROGRESS:
-               return "URB still pending";
-       case -EPROTO:
-               return "bitstuff error, timeout, or unknown USB error";
-       case -EILSEQ:
-               return "CRC mismatch, timeout, or unknown USB error";
-       case -ETIME:
-               return "USB response timeout";
-       case -EPIPE:
-               return "endpoint stalled";
-       case -ECOMM:
-               return "IN buffer overrun";
-       case -ENOSR:
-               return "OUT buffer underrun";
-       case -EOVERFLOW:
-               return "endpoint babble";
-       case -EREMOTEIO:
-               return "short packet";
-       case -ENODEV:
-               return "device removed";
-       case -EXDEV:
-               return "partial isoc transfer";
-       case -EINVAL:
-               return "ISO madness";
-       case -ECONNRESET:
-               return "unlinked (async)";
-       case -ESHUTDOWN:
-               return "device shut down";
-       default:
-               snprintf(unkmsg, sizeof(unkmsg), "unknown status %d", status);
-               return unkmsg;
-       }
-}
-
-/* usb_pipetype_str
- * retrieve string representation of USB pipe type
- */
-static inline char *usb_pipetype_str(int pipe)
-{
-       if (usb_pipeisoc(pipe))
-               return "Isoc";
-       if (usb_pipeint(pipe))
-               return "Int";
-       if (usb_pipecontrol(pipe))
-               return "Ctrl";
-       if (usb_pipebulk(pipe))
-               return "Bulk";
-       return "?";
-}
-
-/* dump_urb
- * write content of URB to syslog for debugging
- */
-static inline void dump_urb(enum debuglevel level, const char *tag,
-                           struct urb *urb)
-{
-#ifdef CONFIG_GIGASET_DEBUG
-       int i;
-       gig_dbg(level, "%s urb(0x%08lx)->{", tag, (unsigned long) urb);
-       if (urb) {
-               gig_dbg(level,
-                       "  dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, "
-                       "hcpriv=0x%08lx, transfer_flags=0x%x,",
-                       (unsigned long) urb->dev,
-                       usb_pipetype_str(urb->pipe),
-                       usb_pipeendpoint(urb->pipe), usb_pipedevice(urb->pipe),
-                       usb_pipein(urb->pipe) ? "in" : "out",
-                       (unsigned long) urb->hcpriv,
-                       urb->transfer_flags);
-               gig_dbg(level,
-                       "  transfer_buffer=0x%08lx[%d], actual_length=%d, "
-                       "setup_packet=0x%08lx,",
-                       (unsigned long) urb->transfer_buffer,
-                       urb->transfer_buffer_length, urb->actual_length,
-                       (unsigned long) urb->setup_packet);
-               gig_dbg(level,
-                       "  start_frame=%d, number_of_packets=%d, interval=%d, "
-                       "error_count=%d,",
-                       urb->start_frame, urb->number_of_packets, urb->interval,
-                       urb->error_count);
-               gig_dbg(level,
-                       "  context=0x%08lx, complete=0x%08lx, "
-                       "iso_frame_desc[]={",
-                       (unsigned long) urb->context,
-                       (unsigned long) urb->complete);
-               for (i = 0; i < urb->number_of_packets; i++) {
-                       struct usb_iso_packet_descriptor *pifd
-                               = &urb->iso_frame_desc[i];
-                       gig_dbg(level,
-                               "    {offset=%u, length=%u, actual_length=%u, "
-                               "status=%u}",
-                               pifd->offset, pifd->length, pifd->actual_length,
-                               pifd->status);
-               }
-       }
-       gig_dbg(level, "}}");
-#endif
-}
-
-/* read/set modem control bits etc. (m10x only) */
-static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
-                                 unsigned new_state)
-{
-       return -EINVAL;
-}
-
-static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
-{
-       return -EINVAL;
-}
-
-static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
-{
-       return -EINVAL;
-}
-
-/* set/clear bits in base connection state, return previous state
- */
-static inline int update_basstate(struct bas_cardstate *ucs,
-                                 int set, int clear)
-{
-       unsigned long flags;
-       int state;
-
-       spin_lock_irqsave(&ucs->lock, flags);
-       state = ucs->basstate;
-       ucs->basstate = (state & ~clear) | set;
-       spin_unlock_irqrestore(&ucs->lock, flags);
-       return state;
-}
-
-/* error_hangup
- * hang up any existing connection because of an unrecoverable error
- * This function may be called from any context and takes care of scheduling
- * the necessary actions for execution outside of interrupt context.
- * cs->lock must not be held.
- * argument:
- *     B channel control structure
- */
-static inline void error_hangup(struct bc_state *bcs)
-{
-       struct cardstate *cs = bcs->cs;
-
-       gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL);
-       gigaset_schedule_event(cs);
-}
-
-/* error_reset
- * reset Gigaset device because of an unrecoverable error
- * This function may be called from any context, and takes care of
- * scheduling the necessary actions for execution outside of interrupt context.
- * cs->hw.bas->lock must not be held.
- * argument:
- *     controller state structure
- */
-static inline void error_reset(struct cardstate *cs)
-{
-       /* reset interrupt pipe to recover (ignore errors) */
-       update_basstate(cs->hw.bas, BS_RESETTING, 0);
-       if (req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT))
-               /* submission failed, escalate to USB port reset */
-               usb_queue_reset_device(cs->hw.bas->interface);
-}
-
-/* check_pending
- * check for completion of pending control request
- * parameter:
- *     ucs     hardware specific controller state structure
- */
-static void check_pending(struct bas_cardstate *ucs)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&ucs->lock, flags);
-       switch (ucs->pending) {
-       case 0:
-               break;
-       case HD_OPEN_ATCHANNEL:
-               if (ucs->basstate & BS_ATOPEN)
-                       ucs->pending = 0;
-               break;
-       case HD_OPEN_B1CHANNEL:
-               if (ucs->basstate & BS_B1OPEN)
-                       ucs->pending = 0;
-               break;
-       case HD_OPEN_B2CHANNEL:
-               if (ucs->basstate & BS_B2OPEN)
-                       ucs->pending = 0;
-               break;
-       case HD_CLOSE_ATCHANNEL:
-               if (!(ucs->basstate & BS_ATOPEN))
-                       ucs->pending = 0;
-               break;
-       case HD_CLOSE_B1CHANNEL:
-               if (!(ucs->basstate & BS_B1OPEN))
-                       ucs->pending = 0;
-               break;
-       case HD_CLOSE_B2CHANNEL:
-               if (!(ucs->basstate & BS_B2OPEN))
-                       ucs->pending = 0;
-               break;
-       case HD_DEVICE_INIT_ACK:                /* no reply expected */
-               ucs->pending = 0;
-               break;
-       case HD_RESET_INTERRUPT_PIPE:
-               if (!(ucs->basstate & BS_RESETTING))
-                       ucs->pending = 0;
-               break;
-       /*
-        * HD_READ_ATMESSAGE and HD_WRITE_ATMESSAGE are handled separately
-        * and should never end up here
-        */
-       default:
-               dev_warn(&ucs->interface->dev,
-                        "unknown pending request 0x%02x cleared\n",
-                        ucs->pending);
-               ucs->pending = 0;
-       }
-
-       if (!ucs->pending)
-               del_timer(&ucs->timer_ctrl);
-
-       spin_unlock_irqrestore(&ucs->lock, flags);
-}
-
-/* cmd_in_timeout
- * timeout routine for command input request
- * argument:
- *     controller state structure
- */
-static void cmd_in_timeout(struct timer_list *t)
-{
-       struct bas_cardstate *ucs = from_timer(ucs, t, timer_cmd_in);
-       struct cardstate *cs = ucs->cs;
-       int rc;
-
-       if (!ucs->rcvbuf_size) {
-               gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__);
-               return;
-       }
-
-       if (ucs->retry_cmd_in++ >= BAS_RETRY) {
-               dev_err(cs->dev,
-                       "control read: timeout, giving up after %d tries\n",
-                       ucs->retry_cmd_in);
-               kfree(ucs->rcvbuf);
-               ucs->rcvbuf = NULL;
-               ucs->rcvbuf_size = 0;
-               error_reset(cs);
-               return;
-       }
-
-       gig_dbg(DEBUG_USBREQ, "%s: timeout, retry %d",
-               __func__, ucs->retry_cmd_in);
-       rc = atread_submit(cs, BAS_TIMEOUT);
-       if (rc < 0) {
-               kfree(ucs->rcvbuf);
-               ucs->rcvbuf = NULL;
-               ucs->rcvbuf_size = 0;
-               if (rc != -ENODEV)
-                       error_reset(cs);
-       }
-}
-
-/* read_ctrl_callback
- * USB completion handler for control pipe input
- * called by the USB subsystem in interrupt context
- * parameter:
- *     urb     USB request block
- *             urb->context = inbuf structure for controller state
- */
-static void read_ctrl_callback(struct urb *urb)
-{
-       struct inbuf_t *inbuf = urb->context;
-       struct cardstate *cs = inbuf->cs;
-       struct bas_cardstate *ucs = cs->hw.bas;
-       int status = urb->status;
-       unsigned numbytes;
-       int rc;
-
-       update_basstate(ucs, 0, BS_ATRDPEND);
-       wake_up(&ucs->waitqueue);
-       del_timer(&ucs->timer_cmd_in);
-
-       switch (status) {
-       case 0:                         /* normal completion */
-               numbytes = urb->actual_length;
-               if (unlikely(numbytes != ucs->rcvbuf_size)) {
-                       dev_warn(cs->dev,
-                                "control read: received %d chars, expected %d\n",
-                                numbytes, ucs->rcvbuf_size);
-                       if (numbytes > ucs->rcvbuf_size)
-                               numbytes = ucs->rcvbuf_size;
-               }
-
-               /* copy received bytes to inbuf, notify event layer */
-               if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) {
-                       gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
-                       gigaset_schedule_event(cs);
-               }
-               break;
-
-       case -ENOENT:                   /* cancelled */
-       case -ECONNRESET:               /* cancelled (async) */
-       case -EINPROGRESS:              /* pending */
-       case -ENODEV:                   /* device removed */
-       case -ESHUTDOWN:                /* device shut down */
-               /* no further action necessary */
-               gig_dbg(DEBUG_USBREQ, "%s: %s",
-                       __func__, get_usb_statmsg(status));
-               break;
-
-       default:                        /* other errors: retry */
-               if (ucs->retry_cmd_in++ < BAS_RETRY) {
-                       gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__,
-                               get_usb_statmsg(status), ucs->retry_cmd_in);
-                       rc = atread_submit(cs, BAS_TIMEOUT);
-                       if (rc >= 0)
-                               /* successfully resubmitted, skip freeing */
-                               return;
-                       if (rc == -ENODEV)
-                               /* disconnect, no further action necessary */
-                               break;
-               }
-               dev_err(cs->dev, "control read: %s, giving up after %d tries\n",
-                       get_usb_statmsg(status), ucs->retry_cmd_in);
-               error_reset(cs);
-       }
-
-       /* read finished, free buffer */
-       kfree(ucs->rcvbuf);
-       ucs->rcvbuf = NULL;
-       ucs->rcvbuf_size = 0;
-}
-
-/* atread_submit
- * submit an HD_READ_ATMESSAGE command URB and optionally start a timeout
- * parameters:
- *     cs      controller state structure
- *     timeout timeout in 1/10 sec., 0: none
- * return value:
- *     0 on success
- *     -EBUSY if another request is pending
- *     any URB submission error code
- */
-static int atread_submit(struct cardstate *cs, int timeout)
-{
-       struct bas_cardstate *ucs = cs->hw.bas;
-       int basstate;
-       int ret;
-
-       gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)",
-               ucs->rcvbuf_size);
-
-       basstate = update_basstate(ucs, BS_ATRDPEND, 0);
-       if (basstate & BS_ATRDPEND) {
-               dev_err(cs->dev,
-                       "could not submit HD_READ_ATMESSAGE: URB busy\n");
-               return -EBUSY;
-       }
-
-       if (basstate & BS_SUSPEND) {
-               dev_notice(cs->dev,
-                          "HD_READ_ATMESSAGE not submitted, "
-                          "suspend in progress\n");
-               update_basstate(ucs, 0, BS_ATRDPEND);
-               /* treat like disconnect */
-               return -ENODEV;
-       }
-
-       ucs->dr_cmd_in.bRequestType = IN_VENDOR_REQ;
-       ucs->dr_cmd_in.bRequest = HD_READ_ATMESSAGE;
-       ucs->dr_cmd_in.wValue = 0;
-       ucs->dr_cmd_in.wIndex = 0;
-       ucs->dr_cmd_in.wLength = cpu_to_le16(ucs->rcvbuf_size);
-       usb_fill_control_urb(ucs->urb_cmd_in, ucs->udev,
-                            usb_rcvctrlpipe(ucs->udev, 0),
-                            (unsigned char *) &ucs->dr_cmd_in,
-                            ucs->rcvbuf, ucs->rcvbuf_size,
-                            read_ctrl_callback, cs->inbuf);
-
-       ret = usb_submit_urb(ucs->urb_cmd_in, GFP_ATOMIC);
-       if (ret != 0) {
-               update_basstate(ucs, 0, BS_ATRDPEND);
-               dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n",
-                       get_usb_rcmsg(ret));
-               return ret;
-       }
-
-       if (timeout > 0) {
-               gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout);
-               mod_timer(&ucs->timer_cmd_in, jiffies + timeout * HZ / 10);
-       }
-       return 0;
-}
-
-/* int_in_work
- * workqueue routine to clear halt on interrupt in endpoint
- */
-
-static void int_in_work(struct work_struct *work)
-{
-       struct bas_cardstate *ucs =
-               container_of(work, struct bas_cardstate, int_in_wq);
-       struct urb *urb = ucs->urb_int_in;
-       struct cardstate *cs = urb->context;
-       int rc;
-
-       /* clear halt condition */
-       rc = usb_clear_halt(ucs->udev, urb->pipe);
-       gig_dbg(DEBUG_USBREQ, "clear_halt: %s", get_usb_rcmsg(rc));
-       if (rc == 0)
-               /* success, resubmit interrupt read URB */
-               rc = usb_submit_urb(urb, GFP_ATOMIC);
-
-       switch (rc) {
-       case 0:         /* success */
-       case -ENODEV:   /* device gone */
-       case -EINVAL:   /* URB already resubmitted, or terminal badness */
-               break;
-       default:        /* failure: try to recover by resetting the device */
-               dev_err(cs->dev, "clear halt failed: %s\n", get_usb_rcmsg(rc));
-               rc = usb_lock_device_for_reset(ucs->udev, ucs->interface);
-               if (rc == 0) {
-                       rc = usb_reset_device(ucs->udev);
-                       usb_unlock_device(ucs->udev);
-               }
-       }
-       ucs->retry_int_in = 0;
-}
-
-/* int_in_resubmit
- * timer routine for interrupt read delayed resubmit
- * argument:
- *     controller state structure
- */
-static void int_in_resubmit(struct timer_list *t)
-{
-       struct bas_cardstate *ucs = from_timer(ucs, t, timer_int_in);
-       struct cardstate *cs = ucs->cs;
-       int rc;
-
-       if (ucs->retry_int_in++ >= BAS_RETRY) {
-               dev_err(cs->dev, "interrupt read: giving up after %d tries\n",
-                       ucs->retry_int_in);
-               usb_queue_reset_device(ucs->interface);
-               return;
-       }
-
-       gig_dbg(DEBUG_USBREQ, "%s: retry %d", __func__, ucs->retry_int_in);
-       rc = usb_submit_urb(ucs->urb_int_in, GFP_ATOMIC);
-       if (rc != 0 && rc != -ENODEV) {
-               dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
-                       get_usb_rcmsg(rc));
-               usb_queue_reset_device(ucs->interface);
-       }
-}
-
-/* read_int_callback
- * USB completion handler for interrupt pipe input
- * called by the USB subsystem in interrupt context
- * parameter:
- *     urb     USB request block
- *             urb->context = controller state structure
- */
-static void read_int_callback(struct urb *urb)
-{
-       struct cardstate *cs = urb->context;
-       struct bas_cardstate *ucs = cs->hw.bas;
-       struct bc_state *bcs;
-       int status = urb->status;
-       unsigned long flags;
-       int rc;
-       unsigned l;
-       int channel;
-
-       switch (status) {
-       case 0:                 /* success */
-               ucs->retry_int_in = 0;
-               break;
-       case -EPIPE:                    /* endpoint stalled */
-               schedule_work(&ucs->int_in_wq);
-               /* fall through */
-       case -ENOENT:                   /* cancelled */
-       case -ECONNRESET:               /* cancelled (async) */
-       case -EINPROGRESS:              /* pending */
-       case -ENODEV:                   /* device removed */
-       case -ESHUTDOWN:                /* device shut down */
-               /* no further action necessary */
-               gig_dbg(DEBUG_USBREQ, "%s: %s",
-                       __func__, get_usb_statmsg(status));
-               return;
-       case -EPROTO:                   /* protocol error or unplug */
-       case -EILSEQ:
-       case -ETIME:
-               /* resubmit after delay */
-               gig_dbg(DEBUG_USBREQ, "%s: %s",
-                       __func__, get_usb_statmsg(status));
-               mod_timer(&ucs->timer_int_in, jiffies + HZ / 10);
-               return;
-       default:                /* other errors: just resubmit */
-               dev_warn(cs->dev, "interrupt read: %s\n",
-                        get_usb_statmsg(status));
-               goto resubmit;
-       }
-
-       /* drop incomplete packets even if the missing bytes wouldn't matter */
-       if (unlikely(urb->actual_length < IP_MSGSIZE)) {
-               dev_warn(cs->dev, "incomplete interrupt packet (%d bytes)\n",
-                        urb->actual_length);
-               goto resubmit;
-       }
-
-       l = (unsigned) ucs->int_in_buf[1] +
-               (((unsigned) ucs->int_in_buf[2]) << 8);
-
-       gig_dbg(DEBUG_USBREQ, "<-------%d: 0x%02x (%u [0x%02x 0x%02x])",
-               urb->actual_length, (int)ucs->int_in_buf[0], l,
-               (int)ucs->int_in_buf[1], (int)ucs->int_in_buf[2]);
-
-       channel = 0;
-
-       switch (ucs->int_in_buf[0]) {
-       case HD_DEVICE_INIT_OK:
-               update_basstate(ucs, BS_INIT, 0);
-               break;
-
-       case HD_READY_SEND_ATDATA:
-               del_timer(&ucs->timer_atrdy);
-               update_basstate(ucs, BS_ATREADY, BS_ATTIMER);
-               start_cbsend(cs);
-               break;
-
-       case HD_OPEN_B2CHANNEL_ACK:
-               ++channel;
-               /* fall through */
-       case HD_OPEN_B1CHANNEL_ACK:
-               bcs = cs->bcs + channel;
-               update_basstate(ucs, BS_B1OPEN << channel, 0);
-               gigaset_bchannel_up(bcs);
-               break;
-
-       case HD_OPEN_ATCHANNEL_ACK:
-               update_basstate(ucs, BS_ATOPEN, 0);
-               start_cbsend(cs);
-               break;
-
-       case HD_CLOSE_B2CHANNEL_ACK:
-               ++channel;
-               /* fall through */
-       case HD_CLOSE_B1CHANNEL_ACK:
-               bcs = cs->bcs + channel;
-               update_basstate(ucs, 0, BS_B1OPEN << channel);
-               stopurbs(bcs->hw.bas);
-               gigaset_bchannel_down(bcs);
-               break;
-
-       case HD_CLOSE_ATCHANNEL_ACK:
-               update_basstate(ucs, 0, BS_ATOPEN);
-               break;
-
-       case HD_B2_FLOW_CONTROL:
-               ++channel;
-               /* fall through */
-       case HD_B1_FLOW_CONTROL:
-               bcs = cs->bcs + channel;
-               atomic_add((l - BAS_NORMFRAME) * BAS_CORRFRAMES,
-                          &bcs->hw.bas->corrbytes);
-               gig_dbg(DEBUG_ISO,
-                       "Flow control (channel %d, sub %d): 0x%02x => %d",
-                       channel, bcs->hw.bas->numsub, l,
-                       atomic_read(&bcs->hw.bas->corrbytes));
-               break;
-
-       case HD_RECEIVEATDATA_ACK:      /* AT response ready to be received */
-               if (!l) {
-                       dev_warn(cs->dev,
-                                "HD_RECEIVEATDATA_ACK with length 0 ignored\n");
-                       break;
-               }
-               spin_lock_irqsave(&cs->lock, flags);
-               if (ucs->basstate & BS_ATRDPEND) {
-                       spin_unlock_irqrestore(&cs->lock, flags);
-                       dev_warn(cs->dev,
-                                "HD_RECEIVEATDATA_ACK(%d) during HD_READ_ATMESSAGE(%d) ignored\n",
-                                l, ucs->rcvbuf_size);
-                       break;
-               }
-               if (ucs->rcvbuf_size) {
-                       /* throw away previous buffer - we have no queue */
-                       dev_err(cs->dev,
-                               "receive AT data overrun, %d bytes lost\n",
-                               ucs->rcvbuf_size);
-                       kfree(ucs->rcvbuf);
-                       ucs->rcvbuf_size = 0;
-               }
-               ucs->rcvbuf = kmalloc(l, GFP_ATOMIC);
-               if (ucs->rcvbuf == NULL) {
-                       spin_unlock_irqrestore(&cs->lock, flags);
-                       dev_err(cs->dev, "out of memory receiving AT data\n");
-                       break;
-               }
-               ucs->rcvbuf_size = l;
-               ucs->retry_cmd_in = 0;
-               rc = atread_submit(cs, BAS_TIMEOUT);
-               if (rc < 0) {
-                       kfree(ucs->rcvbuf);
-                       ucs->rcvbuf = NULL;
-                       ucs->rcvbuf_size = 0;
-               }
-               spin_unlock_irqrestore(&cs->lock, flags);
-               if (rc < 0 && rc != -ENODEV)
-                       error_reset(cs);
-               break;
-
-       case HD_RESET_INTERRUPT_PIPE_ACK:
-               update_basstate(ucs, 0, BS_RESETTING);
-               dev_notice(cs->dev, "interrupt pipe reset\n");
-               break;
-
-       case HD_SUSPEND_END:
-               gig_dbg(DEBUG_USBREQ, "HD_SUSPEND_END");
-               break;
-
-       default:
-               dev_warn(cs->dev,
-                        "unknown Gigaset signal 0x%02x (%u) ignored\n",
-                        (int) ucs->int_in_buf[0], l);
-       }
-
-       check_pending(ucs);
-       wake_up(&ucs->waitqueue);
-
-resubmit:
-       rc = usb_submit_urb(urb, GFP_ATOMIC);
-       if (unlikely(rc != 0 && rc != -ENODEV)) {
-               dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
-                       get_usb_rcmsg(rc));
-               error_reset(cs);
-       }
-}
-
-/* read_iso_callback
- * USB completion handler for B channel isochronous input
- * called by the USB subsystem in interrupt context
- * parameter:
- *     urb     USB request block of completed request
- *             urb->context = bc_state structure
- */
-static void read_iso_callback(struct urb *urb)
-{
-       struct bc_state *bcs;
-       struct bas_bc_state *ubc;
-       int status = urb->status;
-       unsigned long flags;
-       int i, rc;
-
-       /* status codes not worth bothering the tasklet with */
-       if (unlikely(status == -ENOENT ||
-                    status == -ECONNRESET ||
-                    status == -EINPROGRESS ||
-                    status == -ENODEV ||
-                    status == -ESHUTDOWN)) {
-               gig_dbg(DEBUG_ISO, "%s: %s",
-                       __func__, get_usb_statmsg(status));
-               return;
-       }
-
-       bcs = urb->context;
-       ubc = bcs->hw.bas;
-
-       spin_lock_irqsave(&ubc->isoinlock, flags);
-       if (likely(ubc->isoindone == NULL)) {
-               /* pass URB to tasklet */
-               ubc->isoindone = urb;
-               ubc->isoinstatus = status;
-               tasklet_hi_schedule(&ubc->rcvd_tasklet);
-       } else {
-               /* tasklet still busy, drop data and resubmit URB */
-               gig_dbg(DEBUG_ISO, "%s: overrun", __func__);
-               ubc->loststatus = status;
-               for (i = 0; i < BAS_NUMFRAMES; i++) {
-                       ubc->isoinlost += urb->iso_frame_desc[i].actual_length;
-                       if (unlikely(urb->iso_frame_desc[i].status != 0 &&
-                                    urb->iso_frame_desc[i].status != -EINPROGRESS))
-                               ubc->loststatus = urb->iso_frame_desc[i].status;
-                       urb->iso_frame_desc[i].status = 0;
-                       urb->iso_frame_desc[i].actual_length = 0;
-               }
-               if (likely(ubc->running)) {
-                       /* urb->dev is clobbered by USB subsystem */
-                       urb->dev = bcs->cs->hw.bas->udev;
-                       urb->transfer_flags = URB_ISO_ASAP;
-                       urb->number_of_packets = BAS_NUMFRAMES;
-                       rc = usb_submit_urb(urb, GFP_ATOMIC);
-                       if (unlikely(rc != 0 && rc != -ENODEV)) {
-                               dev_err(bcs->cs->dev,
-                                       "could not resubmit isoc read URB: %s\n",
-                                       get_usb_rcmsg(rc));
-                               dump_urb(DEBUG_ISO, "isoc read", urb);
-                               error_hangup(bcs);
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&ubc->isoinlock, flags);
-}
-
-/* write_iso_callback
- * USB completion handler for B channel isochronous output
- * called by the USB subsystem in interrupt context
- * parameter:
- *     urb     USB request block of completed request
- *             urb->context = isow_urbctx_t structure
- */
-static void write_iso_callback(struct urb *urb)
-{
-       struct isow_urbctx_t *ucx;
-       struct bas_bc_state *ubc;
-       int status = urb->status;
-       unsigned long flags;
-
-       /* status codes not worth bothering the tasklet with */
-       if (unlikely(status == -ENOENT ||
-                    status == -ECONNRESET ||
-                    status == -EINPROGRESS ||
-                    status == -ENODEV ||
-                    status == -ESHUTDOWN)) {
-               gig_dbg(DEBUG_ISO, "%s: %s",
-                       __func__, get_usb_statmsg(status));
-               return;
-       }
-
-       /* pass URB context to tasklet */
-       ucx = urb->context;
-       ubc = ucx->bcs->hw.bas;
-       ucx->status = status;
-
-       spin_lock_irqsave(&ubc->isooutlock, flags);
-       ubc->isooutovfl = ubc->isooutdone;
-       ubc->isooutdone = ucx;
-       spin_unlock_irqrestore(&ubc->isooutlock, flags);
-       tasklet_hi_schedule(&ubc->sent_tasklet);
-}
-
-/* starturbs
- * prepare and submit USB request blocks for isochronous input and output
- * argument:
- *     B channel control structure
- * return value:
- *     0 on success
- *     < 0 on error (no URBs submitted)
- */
-static int starturbs(struct bc_state *bcs)
-{
-       struct usb_device *udev = bcs->cs->hw.bas->udev;
-       struct bas_bc_state *ubc = bcs->hw.bas;
-       struct urb *urb;
-       int j, k;
-       int rc;
-
-       /* initialize L2 reception */
-       if (bcs->proto2 == L2_HDLC)
-               bcs->inputstate |= INS_flag_hunt;
-
-       /* submit all isochronous input URBs */
-       ubc->running = 1;
-       for (k = 0; k < BAS_INURBS; k++) {
-               urb = ubc->isoinurbs[k];
-               if (!urb) {
-                       rc = -EFAULT;
-                       goto error;
-               }
-               usb_fill_int_urb(urb, udev,
-                                usb_rcvisocpipe(udev, 3 + 2 * bcs->channel),
-                                ubc->isoinbuf + k * BAS_INBUFSIZE,
-                                BAS_INBUFSIZE, read_iso_callback, bcs,
-                                BAS_FRAMETIME);
-
-               urb->transfer_flags = URB_ISO_ASAP;
-               urb->number_of_packets = BAS_NUMFRAMES;
-               for (j = 0; j < BAS_NUMFRAMES; j++) {
-                       urb->iso_frame_desc[j].offset = j * BAS_MAXFRAME;
-                       urb->iso_frame_desc[j].length = BAS_MAXFRAME;
-                       urb->iso_frame_desc[j].status = 0;
-                       urb->iso_frame_desc[j].actual_length = 0;
-               }
-
-               dump_urb(DEBUG_ISO, "Initial isoc read", urb);
-               rc = usb_submit_urb(urb, GFP_ATOMIC);
-               if (rc != 0)
-                       goto error;
-       }
-
-       /* initialize L2 transmission */
-       gigaset_isowbuf_init(ubc->isooutbuf, PPP_FLAG);
-
-       /* set up isochronous output URBs for flag idling */
-       for (k = 0; k < BAS_OUTURBS; ++k) {
-               urb = ubc->isoouturbs[k].urb;
-               if (!urb) {
-                       rc = -EFAULT;
-                       goto error;
-               }
-               usb_fill_int_urb(urb, udev,
-                                usb_sndisocpipe(udev, 4 + 2 * bcs->channel),
-                                ubc->isooutbuf->data,
-                                sizeof(ubc->isooutbuf->data),
-                                write_iso_callback, &ubc->isoouturbs[k],
-                                BAS_FRAMETIME);
-
-               urb->transfer_flags = URB_ISO_ASAP;
-               urb->number_of_packets = BAS_NUMFRAMES;
-               for (j = 0; j < BAS_NUMFRAMES; ++j) {
-                       urb->iso_frame_desc[j].offset = BAS_OUTBUFSIZE;
-                       urb->iso_frame_desc[j].length = BAS_NORMFRAME;
-                       urb->iso_frame_desc[j].status = 0;
-                       urb->iso_frame_desc[j].actual_length = 0;
-               }
-               ubc->isoouturbs[k].limit = -1;
-       }
-
-       /* keep one URB free, submit the others */
-       for (k = 0; k < BAS_OUTURBS - 1; ++k) {
-               dump_urb(DEBUG_ISO, "Initial isoc write", urb);
-               rc = usb_submit_urb(ubc->isoouturbs[k].urb, GFP_ATOMIC);
-               if (rc != 0)
-                       goto error;
-       }
-       dump_urb(DEBUG_ISO, "Initial isoc write (free)", urb);
-       ubc->isooutfree = &ubc->isoouturbs[BAS_OUTURBS - 1];
-       ubc->isooutdone = ubc->isooutovfl = NULL;
-       return 0;
-error:
-       stopurbs(ubc);
-       return rc;
-}
-
-/* stopurbs
- * cancel the USB request blocks for isochronous input and output
- * errors are silently ignored
- * argument:
- *     B channel control structure
- */
-static void stopurbs(struct bas_bc_state *ubc)
-{
-       int k, rc;
-
-       ubc->running = 0;
-
-       for (k = 0; k < BAS_INURBS; ++k) {
-               rc = usb_unlink_urb(ubc->isoinurbs[k]);
-               gig_dbg(DEBUG_ISO,
-                       "%s: isoc input URB %d unlinked, result = %s",
-                       __func__, k, get_usb_rcmsg(rc));
-       }
-
-       for (k = 0; k < BAS_OUTURBS; ++k) {
-               rc = usb_unlink_urb(ubc->isoouturbs[k].urb);
-               gig_dbg(DEBUG_ISO,
-                       "%s: isoc output URB %d unlinked, result = %s",
-                       __func__, k, get_usb_rcmsg(rc));
-       }
-}
-
-/* Isochronous Write - Bottom Half */
-/* =============================== */
-
-/* submit_iso_write_urb
- * fill and submit the next isochronous write URB
- * parameters:
- *     ucx     context structure containing URB
- * return value:
- *     number of frames submitted in URB
- *     0 if URB not submitted because no data available (isooutbuf busy)
- *     error code < 0 on error
- */
-static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
-{
-       struct urb *urb = ucx->urb;
-       struct bas_bc_state *ubc = ucx->bcs->hw.bas;
-       struct usb_iso_packet_descriptor *ifd;
-       int corrbytes, nframe, rc;
-
-       /* urb->dev is clobbered by USB subsystem */
-       urb->dev = ucx->bcs->cs->hw.bas->udev;
-       urb->transfer_flags = URB_ISO_ASAP;
-       urb->transfer_buffer = ubc->isooutbuf->data;
-       urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data);
-
-       for (nframe = 0; nframe < BAS_NUMFRAMES; nframe++) {
-               ifd = &urb->iso_frame_desc[nframe];
-
-               /* compute frame length according to flow control */
-               ifd->length = BAS_NORMFRAME;
-               corrbytes = atomic_read(&ubc->corrbytes);
-               if (corrbytes != 0) {
-                       gig_dbg(DEBUG_ISO, "%s: corrbytes=%d",
-                               __func__, corrbytes);
-                       if (corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME)
-                               corrbytes = BAS_HIGHFRAME - BAS_NORMFRAME;
-                       else if (corrbytes < BAS_LOWFRAME - BAS_NORMFRAME)
-                               corrbytes = BAS_LOWFRAME - BAS_NORMFRAME;
-                       ifd->length += corrbytes;
-                       atomic_add(-corrbytes, &ubc->corrbytes);
-               }
-
-               /* retrieve block of data to send */
-               rc = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length);
-               if (rc < 0) {
-                       if (rc == -EBUSY) {
-                               gig_dbg(DEBUG_ISO,
-                                       "%s: buffer busy at frame %d",
-                                       __func__, nframe);
-                               /* tasklet will be restarted from
-                                  gigaset_isoc_send_skb() */
-                       } else {
-                               dev_err(ucx->bcs->cs->dev,
-                                       "%s: buffer error %d at frame %d\n",
-                                       __func__, rc, nframe);
-                               return rc;
-                       }
-                       break;
-               }
-               ifd->offset = rc;
-               ucx->limit = ubc->isooutbuf->nextread;
-               ifd->status = 0;
-               ifd->actual_length = 0;
-       }
-       if (unlikely(nframe == 0))
-               return 0;       /* no data to send */
-       urb->number_of_packets = nframe;
-
-       rc = usb_submit_urb(urb, GFP_ATOMIC);
-       if (unlikely(rc)) {
-               if (rc == -ENODEV)
-                       /* device removed - give up silently */
-                       gig_dbg(DEBUG_ISO, "%s: disconnected", __func__);
-               else
-                       dev_err(ucx->bcs->cs->dev,
-                               "could not submit isoc write URB: %s\n",
-                               get_usb_rcmsg(rc));
-               return rc;
-       }
-       ++ubc->numsub;
-       return nframe;
-}
-
-/* write_iso_tasklet
- * tasklet scheduled when an isochronous output URB from the Gigaset device
- * has completed
- * parameter:
- *     data    B channel state structure
- */
-static void write_iso_tasklet(unsigned long data)
-{
-       struct bc_state *bcs = (struct bc_state *) data;
-       struct bas_bc_state *ubc = bcs->hw.bas;
-       struct cardstate *cs = bcs->cs;
-       struct isow_urbctx_t *done, *next, *ovfl;
-       struct urb *urb;
-       int status;
-       struct usb_iso_packet_descriptor *ifd;
-       unsigned long flags;
-       int i;
-       struct sk_buff *skb;
-       int len;
-       int rc;
-
-       /* loop while completed URBs arrive in time */
-       for (;;) {
-               if (unlikely(!(ubc->running))) {
-                       gig_dbg(DEBUG_ISO, "%s: not running", __func__);
-                       return;
-               }
-
-               /* retrieve completed URBs */
-               spin_lock_irqsave(&ubc->isooutlock, flags);
-               done = ubc->isooutdone;
-               ubc->isooutdone = NULL;
-               ovfl = ubc->isooutovfl;
-               ubc->isooutovfl = NULL;
-               spin_unlock_irqrestore(&ubc->isooutlock, flags);
-               if (ovfl) {
-                       dev_err(cs->dev, "isoc write underrun\n");
-                       error_hangup(bcs);
-                       break;
-               }
-               if (!done)
-                       break;
-
-               /* submit free URB if available */
-               spin_lock_irqsave(&ubc->isooutlock, flags);
-               next = ubc->isooutfree;
-               ubc->isooutfree = NULL;
-               spin_unlock_irqrestore(&ubc->isooutlock, flags);
-               if (next) {
-                       rc = submit_iso_write_urb(next);
-                       if (unlikely(rc <= 0 && rc != -ENODEV)) {
-                               /* could not submit URB, put it back */
-                               spin_lock_irqsave(&ubc->isooutlock, flags);
-                               if (ubc->isooutfree == NULL) {
-                                       ubc->isooutfree = next;
-                                       next = NULL;
-                               }
-                               spin_unlock_irqrestore(&ubc->isooutlock, flags);
-                               if (next) {
-                                       /* couldn't put it back */
-                                       dev_err(cs->dev,
-                                               "losing isoc write URB\n");
-                                       error_hangup(bcs);
-                               }
-                       }
-               }
-
-               /* process completed URB */
-               urb = done->urb;
-               status = done->status;
-               switch (status) {
-               case -EXDEV:                    /* partial completion */
-                       gig_dbg(DEBUG_ISO, "%s: URB partially completed",
-                               __func__);
-                       /* fall through - what's the difference anyway? */
-               case 0:                         /* normal completion */
-                       /* inspect individual frames
-                        * assumptions (for lack of documentation):
-                        * - actual_length bytes of first frame in error are
-                        *   successfully sent
-                        * - all following frames are not sent at all
-                        */
-                       for (i = 0; i < BAS_NUMFRAMES; i++) {
-                               ifd = &urb->iso_frame_desc[i];
-                               if (ifd->status ||
-                                   ifd->actual_length != ifd->length) {
-                                       dev_warn(cs->dev,
-                                                "isoc write: frame %d[%d/%d]: %s\n",
-                                                i, ifd->actual_length,
-                                                ifd->length,
-                                                get_usb_statmsg(ifd->status));
-                                       break;
-                               }
-                       }
-                       break;
-               case -EPIPE:                    /* stall - probably underrun */
-                       dev_err(cs->dev, "isoc write: stalled\n");
-                       error_hangup(bcs);
-                       break;
-               default:                        /* other errors */
-                       dev_warn(cs->dev, "isoc write: %s\n",
-                                get_usb_statmsg(status));
-               }
-
-               /* mark the write buffer area covered by this URB as free */
-               if (done->limit >= 0)
-                       ubc->isooutbuf->read = done->limit;
-
-               /* mark URB as free */
-               spin_lock_irqsave(&ubc->isooutlock, flags);
-               next = ubc->isooutfree;
-               ubc->isooutfree = done;
-               spin_unlock_irqrestore(&ubc->isooutlock, flags);
-               if (next) {
-                       /* only one URB still active - resubmit one */
-                       rc = submit_iso_write_urb(next);
-                       if (unlikely(rc <= 0 && rc != -ENODEV)) {
-                               /* couldn't submit */
-                               error_hangup(bcs);
-                       }
-               }
-       }
-
-       /* process queued SKBs */
-       while ((skb = skb_dequeue(&bcs->squeue))) {
-               /* copy to output buffer, doing L2 encapsulation */
-               len = skb->len;
-               if (gigaset_isoc_buildframe(bcs, skb->data, len) == -EAGAIN) {
-                       /* insufficient buffer space, push back onto queue */
-                       skb_queue_head(&bcs->squeue, skb);
-                       gig_dbg(DEBUG_ISO, "%s: skb requeued, qlen=%d",
-                               __func__, skb_queue_len(&bcs->squeue));
-                       break;
-               }
-               skb_pull(skb, len);
-               gigaset_skb_sent(bcs, skb);
-               dev_kfree_skb_any(skb);
-       }
-}
-
-/* Isochronous Read - Bottom Half */
-/* ============================== */
-
-/* read_iso_tasklet
- * tasklet scheduled when an isochronous input URB from the Gigaset device
- * has completed
- * parameter:
- *     data    B channel state structure
- */
-static void read_iso_tasklet(unsigned long data)
-{
-       struct bc_state *bcs = (struct bc_state *) data;
-       struct bas_bc_state *ubc = bcs->hw.bas;
-       struct cardstate *cs = bcs->cs;
-       struct urb *urb;
-       int status;
-       struct usb_iso_packet_descriptor *ifd;
-       char *rcvbuf;
-       unsigned long flags;
-       int totleft, numbytes, offset, frame, rc;
-
-       /* loop while more completed URBs arrive in the meantime */
-       for (;;) {
-               /* retrieve URB */
-               spin_lock_irqsave(&ubc->isoinlock, flags);
-               urb = ubc->isoindone;
-               if (!urb) {
-                       spin_unlock_irqrestore(&ubc->isoinlock, flags);
-                       return;
-               }
-               status = ubc->isoinstatus;
-               ubc->isoindone = NULL;
-               if (unlikely(ubc->loststatus != -EINPROGRESS)) {
-                       dev_warn(cs->dev,
-                                "isoc read overrun, URB dropped (status: %s, %d bytes)\n",
-                                get_usb_statmsg(ubc->loststatus),
-                                ubc->isoinlost);
-                       ubc->loststatus = -EINPROGRESS;
-               }
-               spin_unlock_irqrestore(&ubc->isoinlock, flags);
-
-               if (unlikely(!(ubc->running))) {
-                       gig_dbg(DEBUG_ISO,
-                               "%s: channel not running, "
-                               "dropped URB with status: %s",
-                               __func__, get_usb_statmsg(status));
-                       return;
-               }
-
-               switch (status) {
-               case 0:                         /* normal completion */
-                       break;
-               case -EXDEV:                    /* inspect individual frames
-                                                  (we do that anyway) */
-                       gig_dbg(DEBUG_ISO, "%s: URB partially completed",
-                               __func__);
-                       break;
-               case -ENOENT:
-               case -ECONNRESET:
-               case -EINPROGRESS:
-                       gig_dbg(DEBUG_ISO, "%s: %s",
-                               __func__, get_usb_statmsg(status));
-                       continue;               /* -> skip */
-               case -EPIPE:
-                       dev_err(cs->dev, "isoc read: stalled\n");
-                       error_hangup(bcs);
-                       continue;               /* -> skip */
-               default:                        /* other error */
-                       dev_warn(cs->dev, "isoc read: %s\n",
-                                get_usb_statmsg(status));
-                       goto error;
-               }
-
-               rcvbuf = urb->transfer_buffer;
-               totleft = urb->actual_length;
-               for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) {
-                       ifd = &urb->iso_frame_desc[frame];
-                       numbytes = ifd->actual_length;
-                       switch (ifd->status) {
-                       case 0:                 /* success */
-                               break;
-                       case -EPROTO:           /* protocol error or unplug */
-                       case -EILSEQ:
-                       case -ETIME:
-                               /* probably just disconnected, ignore */
-                               gig_dbg(DEBUG_ISO,
-                                       "isoc read: frame %d[%d]: %s\n",
-                                       frame, numbytes,
-                                       get_usb_statmsg(ifd->status));
-                               break;
-                       default:                /* other error */
-                               /* report, assume transferred bytes are ok */
-                               dev_warn(cs->dev,
-                                        "isoc read: frame %d[%d]: %s\n",
-                                        frame, numbytes,
-                                        get_usb_statmsg(ifd->status));
-                       }
-                       if (unlikely(numbytes > BAS_MAXFRAME))
-                               dev_warn(cs->dev,
-                                        "isoc read: frame %d[%d]: %s\n",
-                                        frame, numbytes,
-                                        "exceeds max frame size");
-                       if (unlikely(numbytes > totleft)) {
-                               dev_warn(cs->dev,
-                                        "isoc read: frame %d[%d]: %s\n",
-                                        frame, numbytes,
-                                        "exceeds total transfer length");
-                               numbytes = totleft;
-                       }
-                       offset = ifd->offset;
-                       if (unlikely(offset + numbytes > BAS_INBUFSIZE)) {
-                               dev_warn(cs->dev,
-                                        "isoc read: frame %d[%d]: %s\n",
-                                        frame, numbytes,
-                                        "exceeds end of buffer");
-                               numbytes = BAS_INBUFSIZE - offset;
-                       }
-                       gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs);
-                       totleft -= numbytes;
-               }
-               if (unlikely(totleft > 0))
-                       dev_warn(cs->dev, "isoc read: %d data bytes missing\n",
-                                totleft);
-
-error:
-               /* URB processed, resubmit */
-               for (frame = 0; frame < BAS_NUMFRAMES; frame++) {
-                       urb->iso_frame_desc[frame].status = 0;
-                       urb->iso_frame_desc[frame].actual_length = 0;
-               }
-               /* urb->dev is clobbered by USB subsystem */
-               urb->dev = bcs->cs->hw.bas->udev;
-               urb->transfer_flags = URB_ISO_ASAP;
-               urb->number_of_packets = BAS_NUMFRAMES;
-               rc = usb_submit_urb(urb, GFP_ATOMIC);
-               if (unlikely(rc != 0 && rc != -ENODEV)) {
-                       dev_err(cs->dev,
-                               "could not resubmit isoc read URB: %s\n",
-                               get_usb_rcmsg(rc));
-                       dump_urb(DEBUG_ISO, "resubmit isoc read", urb);
-                       error_hangup(bcs);
-               }
-       }
-}
-
-/* Channel Operations */
-/* ================== */
-
-/* req_timeout
- * timeout routine for control output request
- * argument:
- *     controller state structure
- */
-static void req_timeout(struct timer_list *t)
-{
-       struct bas_cardstate *ucs = from_timer(ucs, t, timer_ctrl);
-       struct cardstate *cs = ucs->cs;
-       int pending;
-       unsigned long flags;
-
-       check_pending(ucs);
-
-       spin_lock_irqsave(&ucs->lock, flags);
-       pending = ucs->pending;
-       ucs->pending = 0;
-       spin_unlock_irqrestore(&ucs->lock, flags);
-
-       switch (pending) {
-       case 0:                                 /* no pending request */
-               gig_dbg(DEBUG_USBREQ, "%s: no request pending", __func__);
-               break;
-
-       case HD_OPEN_ATCHANNEL:
-               dev_err(cs->dev, "timeout opening AT channel\n");
-               error_reset(cs);
-               break;
-
-       case HD_OPEN_B1CHANNEL:
-               dev_err(cs->dev, "timeout opening channel 1\n");
-               error_hangup(&cs->bcs[0]);
-               break;
-
-       case HD_OPEN_B2CHANNEL:
-               dev_err(cs->dev, "timeout opening channel 2\n");
-               error_hangup(&cs->bcs[1]);
-               break;
-
-       case HD_CLOSE_ATCHANNEL:
-               dev_err(cs->dev, "timeout closing AT channel\n");
-               error_reset(cs);
-               break;
-
-       case HD_CLOSE_B1CHANNEL:
-               dev_err(cs->dev, "timeout closing channel 1\n");
-               error_reset(cs);
-               break;
-
-       case HD_CLOSE_B2CHANNEL:
-               dev_err(cs->dev, "timeout closing channel 2\n");
-               error_reset(cs);
-               break;
-
-       case HD_RESET_INTERRUPT_PIPE:
-               /* error recovery escalation */
-               dev_err(cs->dev,
-                       "reset interrupt pipe timeout, attempting USB reset\n");
-               usb_queue_reset_device(ucs->interface);
-               break;
-
-       default:
-               dev_warn(cs->dev, "request 0x%02x timed out, clearing\n",
-                        pending);
-       }
-
-       wake_up(&ucs->waitqueue);
-}
-
-/* write_ctrl_callback
- * USB completion handler for control pipe output
- * called by the USB subsystem in interrupt context
- * parameter:
- *     urb     USB request block of completed request
- *             urb->context = hardware specific controller state structure
- */
-static void write_ctrl_callback(struct urb *urb)
-{
-       struct bas_cardstate *ucs = urb->context;
-       int status = urb->status;
-       int rc;
-       unsigned long flags;
-
-       /* check status */
-       switch (status) {
-       case 0:                                 /* normal completion */
-               spin_lock_irqsave(&ucs->lock, flags);
-               switch (ucs->pending) {
-               case HD_DEVICE_INIT_ACK:        /* no reply expected */
-                       del_timer(&ucs->timer_ctrl);
-                       ucs->pending = 0;
-                       break;
-               }
-               spin_unlock_irqrestore(&ucs->lock, flags);
-               return;
-
-       case -ENOENT:                   /* cancelled */
-       case -ECONNRESET:               /* cancelled (async) */
-       case -EINPROGRESS:              /* pending */
-       case -ENODEV:                   /* device removed */
-       case -ESHUTDOWN:                /* device shut down */
-               /* ignore silently */
-               gig_dbg(DEBUG_USBREQ, "%s: %s",
-                       __func__, get_usb_statmsg(status));
-               break;
-
-       default:                                /* any failure */
-               /* don't retry if suspend requested */
-               if (++ucs->retry_ctrl > BAS_RETRY ||
-                   (ucs->basstate & BS_SUSPEND)) {
-                       dev_err(&ucs->interface->dev,
-                               "control request 0x%02x failed: %s\n",
-                               ucs->dr_ctrl.bRequest,
-                               get_usb_statmsg(status));
-                       break;          /* give up */
-               }
-               dev_notice(&ucs->interface->dev,
-                          "control request 0x%02x: %s, retry %d\n",
-                          ucs->dr_ctrl.bRequest, get_usb_statmsg(status),
-                          ucs->retry_ctrl);
-               /* urb->dev is clobbered by USB subsystem */
-               urb->dev = ucs->udev;
-               rc = usb_submit_urb(urb, GFP_ATOMIC);
-               if (unlikely(rc)) {
-                       dev_err(&ucs->interface->dev,
-                               "could not resubmit request 0x%02x: %s\n",
-                               ucs->dr_ctrl.bRequest, get_usb_rcmsg(rc));
-                       break;
-               }
-               /* resubmitted */
-               return;
-       }
-
-       /* failed, clear pending request */
-       spin_lock_irqsave(&ucs->lock, flags);
-       del_timer(&ucs->timer_ctrl);
-       ucs->pending = 0;
-       spin_unlock_irqrestore(&ucs->lock, flags);
-       wake_up(&ucs->waitqueue);
-}
-
-/* req_submit
- * submit a control output request without message buffer to the Gigaset base
- * and optionally start a timeout
- * parameters:
- *     bcs     B channel control structure
- *     req     control request code (HD_*)
- *     val     control request parameter value (set to 0 if unused)
- *     timeout timeout in seconds (0: no timeout)
- * return value:
- *     0 on success
- *     -EBUSY if another request is pending
- *     any URB submission error code
- */
-static int req_submit(struct bc_state *bcs, int req, int val, int timeout)
-{
-       struct bas_cardstate *ucs = bcs->cs->hw.bas;
-       int ret;
-       unsigned long flags;
-
-       gig_dbg(DEBUG_USBREQ, "-------> 0x%02x (%d)", req, val);
-
-       spin_lock_irqsave(&ucs->lock, flags);
-       if (ucs->pending) {
-               spin_unlock_irqrestore(&ucs->lock, flags);
-               dev_err(bcs->cs->dev,
-                       "submission of request 0x%02x failed: "
-                       "request 0x%02x still pending\n",
-                       req, ucs->pending);
-               return -EBUSY;
-       }
-
-       ucs->dr_ctrl.bRequestType = OUT_VENDOR_REQ;
-       ucs->dr_ctrl.bRequest = req;
-       ucs->dr_ctrl.wValue = cpu_to_le16(val);
-       ucs->dr_ctrl.wIndex = 0;
-       ucs->dr_ctrl.wLength = 0;
-       usb_fill_control_urb(ucs->urb_ctrl, ucs->udev,
-                            usb_sndctrlpipe(ucs->udev, 0),
-                            (unsigned char *) &ucs->dr_ctrl, NULL, 0,
-                            write_ctrl_callback, ucs);
-       ucs->retry_ctrl = 0;
-       ret = usb_submit_urb(ucs->urb_ctrl, GFP_ATOMIC);
-       if (unlikely(ret)) {
-               dev_err(bcs->cs->dev, "could not submit request 0x%02x: %s\n",
-                       req, get_usb_rcmsg(ret));
-               spin_unlock_irqrestore(&ucs->lock, flags);
-               return ret;
-       }
-       ucs->pending = req;
-
-       if (timeout > 0) {
-               gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout);
-               mod_timer(&ucs->timer_ctrl, jiffies + timeout * HZ / 10);
-       }
-
-       spin_unlock_irqrestore(&ucs->lock, flags);
-       return 0;
-}
-
-/* gigaset_init_bchannel
- * called by common.c to connect a B channel
- * initialize isochronous I/O and tell the Gigaset base to open the channel
- * argument:
- *     B channel control structure
- * return value:
- *     0 on success, error code < 0 on error
- */
-static int gigaset_init_bchannel(struct bc_state *bcs)
-{
-       struct cardstate *cs = bcs->cs;
-       int req, ret;
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (unlikely(!cs->connected)) {
-               gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
-               spin_unlock_irqrestore(&cs->lock, flags);
-               return -ENODEV;
-       }
-
-       if (cs->hw.bas->basstate & BS_SUSPEND) {
-               dev_notice(cs->dev,
-                          "not starting isoc I/O, suspend in progress\n");
-               spin_unlock_irqrestore(&cs->lock, flags);
-               return -EHOSTUNREACH;
-       }
-
-       ret = starturbs(bcs);
-       if (ret < 0) {
-               spin_unlock_irqrestore(&cs->lock, flags);
-               dev_err(cs->dev,
-                       "could not start isoc I/O for channel B%d: %s\n",
-                       bcs->channel + 1,
-                       ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret));
-               if (ret != -ENODEV)
-                       error_hangup(bcs);
-               return ret;
-       }
-
-       req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL;
-       ret = req_submit(bcs, req, 0, BAS_TIMEOUT);
-       if (ret < 0) {
-               dev_err(cs->dev, "could not open channel B%d\n",
-                       bcs->channel + 1);
-               stopurbs(bcs->hw.bas);
-       }
-
-       spin_unlock_irqrestore(&cs->lock, flags);
-       if (ret < 0 && ret != -ENODEV)
-               error_hangup(bcs);
-       return ret;
-}
-
-/* gigaset_close_bchannel
- * called by common.c to disconnect a B channel
- * tell the Gigaset base to close the channel
- * stopping isochronous I/O and LL notification will be done when the
- * acknowledgement for the close arrives
- * argument:
- *     B channel control structure
- * return value:
- *     0 on success, error code < 0 on error
- */
-static int gigaset_close_bchannel(struct bc_state *bcs)
-{
-       struct cardstate *cs = bcs->cs;
-       int req, ret;
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (unlikely(!cs->connected)) {
-               spin_unlock_irqrestore(&cs->lock, flags);
-               gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
-               return -ENODEV;
-       }
-
-       if (!(cs->hw.bas->basstate & (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) {
-               /* channel not running: just signal common.c */
-               spin_unlock_irqrestore(&cs->lock, flags);
-               gigaset_bchannel_down(bcs);
-               return 0;
-       }
-
-       /* channel running: tell device to close it */
-       req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL;
-       ret = req_submit(bcs, req, 0, BAS_TIMEOUT);
-       if (ret < 0)
-               dev_err(cs->dev, "closing channel B%d failed\n",
-                       bcs->channel + 1);
-
-       spin_unlock_irqrestore(&cs->lock, flags);
-       return ret;
-}
-
-/* Device Operations */
-/* ================= */
-
-/* complete_cb
- * unqueue first command buffer from queue, waking any sleepers
- * must be called with cs->cmdlock held
- * parameter:
- *     cs      controller state structure
- */
-static void complete_cb(struct cardstate *cs)
-{
-       struct cmdbuf_t *cb = cs->cmdbuf;
-
-       /* unqueue completed buffer */
-       cs->cmdbytes -= cs->curlen;
-       gig_dbg(DEBUG_OUTPUT, "write_command: sent %u bytes, %u left",
-               cs->curlen, cs->cmdbytes);
-       if (cb->next != NULL) {
-               cs->cmdbuf = cb->next;
-               cs->cmdbuf->prev = NULL;
-               cs->curlen = cs->cmdbuf->len;
-       } else {
-               cs->cmdbuf = NULL;
-               cs->lastcmdbuf = NULL;
-               cs->curlen = 0;
-       }
-
-       if (cb->wake_tasklet)
-               tasklet_schedule(cb->wake_tasklet);
-
-       kfree(cb);
-}
-
-/* write_command_callback
- * USB completion handler for AT command transmission
- * called by the USB subsystem in interrupt context
- * parameter:
- *     urb     USB request block of completed request
- *             urb->context = controller state structure
- */
-static void write_command_callback(struct urb *urb)
-{
-       struct cardstate *cs = urb->context;
-       struct bas_cardstate *ucs = cs->hw.bas;
-       int status = urb->status;
-       unsigned long flags;
-
-       update_basstate(ucs, 0, BS_ATWRPEND);
-       wake_up(&ucs->waitqueue);
-
-       /* check status */
-       switch (status) {
-       case 0:                                 /* normal completion */
-               break;
-       case -ENOENT:                   /* cancelled */
-       case -ECONNRESET:               /* cancelled (async) */
-       case -EINPROGRESS:              /* pending */
-       case -ENODEV:                   /* device removed */
-       case -ESHUTDOWN:                /* device shut down */
-               /* ignore silently */
-               gig_dbg(DEBUG_USBREQ, "%s: %s",
-                       __func__, get_usb_statmsg(status));
-               return;
-       default:                                /* any failure */
-               if (++ucs->retry_cmd_out > BAS_RETRY) {
-                       dev_warn(cs->dev,
-                                "command write: %s, "
-                                "giving up after %d retries\n",
-                                get_usb_statmsg(status),
-                                ucs->retry_cmd_out);
-                       break;
-               }
-               if (ucs->basstate & BS_SUSPEND) {
-                       dev_warn(cs->dev,
-                                "command write: %s, "
-                                "won't retry - suspend requested\n",
-                                get_usb_statmsg(status));
-                       break;
-               }
-               if (cs->cmdbuf == NULL) {
-                       dev_warn(cs->dev,
-                                "command write: %s, "
-                                "cannot retry - cmdbuf gone\n",
-                                get_usb_statmsg(status));
-                       break;
-               }
-               dev_notice(cs->dev, "command write: %s, retry %d\n",
-                          get_usb_statmsg(status), ucs->retry_cmd_out);
-               if (atwrite_submit(cs, cs->cmdbuf->buf, cs->cmdbuf->len) >= 0)
-                       /* resubmitted - bypass regular exit block */
-                       return;
-               /* command send failed, assume base still waiting */
-               update_basstate(ucs, BS_ATREADY, 0);
-       }
-
-       spin_lock_irqsave(&cs->cmdlock, flags);
-       if (cs->cmdbuf != NULL)
-               complete_cb(cs);
-       spin_unlock_irqrestore(&cs->cmdlock, flags);
-}
-
-/* atrdy_timeout
- * timeout routine for AT command transmission
- * argument:
- *     controller state structure
- */
-static void atrdy_timeout(struct timer_list *t)
-{
-       struct bas_cardstate *ucs = from_timer(ucs, t, timer_atrdy);
-       struct cardstate *cs = ucs->cs;
-
-       dev_warn(cs->dev, "timeout waiting for HD_READY_SEND_ATDATA\n");
-
-       /* fake the missing signal - what else can I do? */
-       update_basstate(ucs, BS_ATREADY, BS_ATTIMER);
-       start_cbsend(cs);
-}
-
-/* atwrite_submit
- * submit an HD_WRITE_ATMESSAGE command URB
- * parameters:
- *     cs      controller state structure
- *     buf     buffer containing command to send
- *     len     length of command to send
- * return value:
- *     0 on success
- *     -EBUSY if another request is pending
- *     any URB submission error code
- */
-static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len)
-{
-       struct bas_cardstate *ucs = cs->hw.bas;
-       int rc;
-
-       gig_dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len);
-
-       if (update_basstate(ucs, BS_ATWRPEND, 0) & BS_ATWRPEND) {
-               dev_err(cs->dev,
-                       "could not submit HD_WRITE_ATMESSAGE: URB busy\n");
-               return -EBUSY;
-       }
-
-       ucs->dr_cmd_out.bRequestType = OUT_VENDOR_REQ;
-       ucs->dr_cmd_out.bRequest = HD_WRITE_ATMESSAGE;
-       ucs->dr_cmd_out.wValue = 0;
-       ucs->dr_cmd_out.wIndex = 0;
-       ucs->dr_cmd_out.wLength = cpu_to_le16(len);
-       usb_fill_control_urb(ucs->urb_cmd_out, ucs->udev,
-                            usb_sndctrlpipe(ucs->udev, 0),
-                            (unsigned char *) &ucs->dr_cmd_out, buf, len,
-                            write_command_callback, cs);
-       rc = usb_submit_urb(ucs->urb_cmd_out, GFP_ATOMIC);
-       if (unlikely(rc)) {
-               update_basstate(ucs, 0, BS_ATWRPEND);
-               dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: %s\n",
-                       get_usb_rcmsg(rc));
-               return rc;
-       }
-
-       /* submitted successfully, start timeout if necessary */
-       if (!(update_basstate(ucs, BS_ATTIMER, BS_ATREADY) & BS_ATTIMER)) {
-               gig_dbg(DEBUG_OUTPUT, "setting ATREADY timeout of %d/10 secs",
-                       ATRDY_TIMEOUT);
-               mod_timer(&ucs->timer_atrdy, jiffies + ATRDY_TIMEOUT * HZ / 10);
-       }
-       return 0;
-}
-
-/* start_cbsend
- * start transmission of AT command queue if necessary
- * parameter:
- *     cs              controller state structure
- * return value:
- *     0 on success
- *     error code < 0 on error
- */
-static int start_cbsend(struct cardstate *cs)
-{
-       struct cmdbuf_t *cb;
-       struct bas_cardstate *ucs = cs->hw.bas;
-       unsigned long flags;
-       int rc;
-       int retval = 0;
-
-       /* check if suspend requested */
-       if (ucs->basstate & BS_SUSPEND) {
-               gig_dbg(DEBUG_OUTPUT, "suspending");
-               return -EHOSTUNREACH;
-       }
-
-       /* check if AT channel is open */
-       if (!(ucs->basstate & BS_ATOPEN)) {
-               gig_dbg(DEBUG_OUTPUT, "AT channel not open");
-               rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT);
-               if (rc < 0) {
-                       /* flush command queue */
-                       spin_lock_irqsave(&cs->cmdlock, flags);
-                       while (cs->cmdbuf != NULL)
-                               complete_cb(cs);
-                       spin_unlock_irqrestore(&cs->cmdlock, flags);
-               }
-               return rc;
-       }
-
-       /* try to send first command in queue */
-       spin_lock_irqsave(&cs->cmdlock, flags);
-
-       while ((cb = cs->cmdbuf) != NULL && (ucs->basstate & BS_ATREADY)) {
-               ucs->retry_cmd_out = 0;
-               rc = atwrite_submit(cs, cb->buf, cb->len);
-               if (unlikely(rc)) {
-                       retval = rc;
-                       complete_cb(cs);
-               }
-       }
-
-       spin_unlock_irqrestore(&cs->cmdlock, flags);
-       return retval;
-}
-
-/* gigaset_write_cmd
- * This function is called by the device independent part of the driver
- * to transmit an AT command string to the Gigaset device.
- * It encapsulates the device specific method for transmission over the
- * direct USB connection to the base.
- * The command string is added to the queue of commands to send, and
- * USB transmission is started if necessary.
- * parameters:
- *     cs              controller state structure
- *     cb              command buffer structure
- * return value:
- *     number of bytes queued on success
- *     error code < 0 on error
- */
-static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
-{
-       unsigned long flags;
-       int rc;
-
-       gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
-                          DEBUG_TRANSCMD : DEBUG_LOCKCMD,
-                          "CMD Transmit", cb->len, cb->buf);
-
-       /* translate "+++" escape sequence sent as a single separate command
-        * into "close AT channel" command for error recovery
-        * The next command will reopen the AT channel automatically.
-        */
-       if (cb->len == 3 && !memcmp(cb->buf, "+++", 3)) {
-               /* If an HD_RECEIVEATDATA_ACK message remains unhandled
-                * because of an error, the base never sends another one.
-                * The response channel is thus effectively blocked.
-                * Closing and reopening the AT channel does *not* clear
-                * this condition.
-                * As a stopgap measure, submit a zero-length AT read
-                * before closing the AT channel. This has the undocumented
-                * effect of triggering a new HD_RECEIVEATDATA_ACK message
-                * from the base if necessary.
-                * The subsequent AT channel close then discards any pending
-                * messages.
-                */
-               spin_lock_irqsave(&cs->lock, flags);
-               if (!(cs->hw.bas->basstate & BS_ATRDPEND)) {
-                       kfree(cs->hw.bas->rcvbuf);
-                       cs->hw.bas->rcvbuf = NULL;
-                       cs->hw.bas->rcvbuf_size = 0;
-                       cs->hw.bas->retry_cmd_in = 0;
-                       atread_submit(cs, 0);
-               }
-               spin_unlock_irqrestore(&cs->lock, flags);
-
-               rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
-               if (cb->wake_tasklet)
-                       tasklet_schedule(cb->wake_tasklet);
-               if (!rc)
-                       rc = cb->len;
-               kfree(cb);
-               return rc;
-       }
-
-       spin_lock_irqsave(&cs->cmdlock, flags);
-       cb->prev = cs->lastcmdbuf;
-       if (cs->lastcmdbuf)
-               cs->lastcmdbuf->next = cb;
-       else {
-               cs->cmdbuf = cb;
-               cs->curlen = cb->len;
-       }
-       cs->cmdbytes += cb->len;
-       cs->lastcmdbuf = cb;
-       spin_unlock_irqrestore(&cs->cmdlock, flags);
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (unlikely(!cs->connected)) {
-               spin_unlock_irqrestore(&cs->lock, flags);
-               gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
-               /* flush command queue */
-               spin_lock_irqsave(&cs->cmdlock, flags);
-               while (cs->cmdbuf != NULL)
-                       complete_cb(cs);
-               spin_unlock_irqrestore(&cs->cmdlock, flags);
-               return -ENODEV;
-       }
-       rc = start_cbsend(cs);
-       spin_unlock_irqrestore(&cs->lock, flags);
-       return rc < 0 ? rc : cb->len;
-}
-
-/* gigaset_write_room
- * tty_driver.write_room interface routine
- * return number of characters the driver will accept to be written via
- * gigaset_write_cmd
- * parameter:
- *     controller state structure
- * return value:
- *     number of characters
- */
-static int gigaset_write_room(struct cardstate *cs)
-{
-       return IF_WRITEBUF;
-}
-
-/* gigaset_chars_in_buffer
- * tty_driver.chars_in_buffer interface routine
- * return number of characters waiting to be sent
- * parameter:
- *     controller state structure
- * return value:
- *     number of characters
- */
-static int gigaset_chars_in_buffer(struct cardstate *cs)
-{
-       return cs->cmdbytes;
-}
-
-/* gigaset_brkchars
- * implementation of ioctl(GIGASET_BRKCHARS)
- * parameter:
- *     controller state structure
- * return value:
- *     -EINVAL (unimplemented function)
- */
-static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
-{
-       return -EINVAL;
-}
-
-
-/* Device Initialization/Shutdown */
-/* ============================== */
-
-/* Free hardware dependent part of the B channel structure
- * parameter:
- *     bcs     B channel structure
- */
-static void gigaset_freebcshw(struct bc_state *bcs)
-{
-       struct bas_bc_state *ubc = bcs->hw.bas;
-       int i;
-
-       if (!ubc)
-               return;
-
-       /* kill URBs and tasklets before freeing - better safe than sorry */
-       ubc->running = 0;
-       gig_dbg(DEBUG_INIT, "%s: killing isoc URBs", __func__);
-       for (i = 0; i < BAS_OUTURBS; ++i) {
-               usb_kill_urb(ubc->isoouturbs[i].urb);
-               usb_free_urb(ubc->isoouturbs[i].urb);
-       }
-       for (i = 0; i < BAS_INURBS; ++i) {
-               usb_kill_urb(ubc->isoinurbs[i]);
-               usb_free_urb(ubc->isoinurbs[i]);
-       }
-       tasklet_kill(&ubc->sent_tasklet);
-       tasklet_kill(&ubc->rcvd_tasklet);
-       kfree(ubc->isooutbuf);
-       kfree(ubc);
-       bcs->hw.bas = NULL;
-}
-
-/* Initialize hardware dependent part of the B channel structure
- * parameter:
- *     bcs     B channel structure
- * return value:
- *     0 on success, error code < 0 on failure
- */
-static int gigaset_initbcshw(struct bc_state *bcs)
-{
-       int i;
-       struct bas_bc_state *ubc;
-
-       bcs->hw.bas = ubc = kmalloc(sizeof(struct bas_bc_state), GFP_KERNEL);
-       if (!ubc) {
-               pr_err("out of memory\n");
-               return -ENOMEM;
-       }
-
-       ubc->running = 0;
-       atomic_set(&ubc->corrbytes, 0);
-       spin_lock_init(&ubc->isooutlock);
-       for (i = 0; i < BAS_OUTURBS; ++i) {
-               ubc->isoouturbs[i].urb = NULL;
-               ubc->isoouturbs[i].bcs = bcs;
-       }
-       ubc->isooutdone = ubc->isooutfree = ubc->isooutovfl = NULL;
-       ubc->numsub = 0;
-       ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL);
-       if (!ubc->isooutbuf) {
-               pr_err("out of memory\n");
-               kfree(ubc);
-               bcs->hw.bas = NULL;
-               return -ENOMEM;
-       }
-       tasklet_init(&ubc->sent_tasklet,
-                    write_iso_tasklet, (unsigned long) bcs);
-
-       spin_lock_init(&ubc->isoinlock);
-       for (i = 0; i < BAS_INURBS; ++i)
-               ubc->isoinurbs[i] = NULL;
-       ubc->isoindone = NULL;
-       ubc->loststatus = -EINPROGRESS;
-       ubc->isoinlost = 0;
-       ubc->seqlen = 0;
-       ubc->inbyte = 0;
-       ubc->inbits = 0;
-       ubc->goodbytes = 0;
-       ubc->alignerrs = 0;
-       ubc->fcserrs = 0;
-       ubc->frameerrs = 0;
-       ubc->giants = 0;
-       ubc->runts = 0;
-       ubc->aborts = 0;
-       ubc->shared0s = 0;
-       ubc->stolen0s = 0;
-       tasklet_init(&ubc->rcvd_tasklet,
-                    read_iso_tasklet, (unsigned long) bcs);
-       return 0;
-}
-
-static void gigaset_reinitbcshw(struct bc_state *bcs)
-{
-       struct bas_bc_state *ubc = bcs->hw.bas;
-
-       bcs->hw.bas->running = 0;
-       atomic_set(&bcs->hw.bas->corrbytes, 0);
-       bcs->hw.bas->numsub = 0;
-       spin_lock_init(&ubc->isooutlock);
-       spin_lock_init(&ubc->isoinlock);
-       ubc->loststatus = -EINPROGRESS;
-}
-
-static void gigaset_freecshw(struct cardstate *cs)
-{
-       /* timers, URBs and rcvbuf are disposed of in disconnect */
-       kfree(cs->hw.bas->int_in_buf);
-       kfree(cs->hw.bas);
-       cs->hw.bas = NULL;
-}
-
-/* Initialize hardware dependent part of the cardstate structure
- * parameter:
- *     cs      cardstate structure
- * return value:
- *     0 on success, error code < 0 on failure
- */
-static int gigaset_initcshw(struct cardstate *cs)
-{
-       struct bas_cardstate *ucs;
-
-       cs->hw.bas = ucs = kzalloc(sizeof(*ucs), GFP_KERNEL);
-       if (!ucs) {
-               pr_err("out of memory\n");
-               return -ENOMEM;
-       }
-       ucs->int_in_buf = kmalloc(IP_MSGSIZE, GFP_KERNEL);
-       if (!ucs->int_in_buf) {
-               kfree(ucs);
-               pr_err("out of memory\n");
-               return -ENOMEM;
-       }
-
-       spin_lock_init(&ucs->lock);
-       ucs->cs = cs;
-       timer_setup(&ucs->timer_ctrl, req_timeout, 0);
-       timer_setup(&ucs->timer_atrdy, atrdy_timeout, 0);
-       timer_setup(&ucs->timer_cmd_in, cmd_in_timeout, 0);
-       timer_setup(&ucs->timer_int_in, int_in_resubmit, 0);
-       init_waitqueue_head(&ucs->waitqueue);
-       INIT_WORK(&ucs->int_in_wq, int_in_work);
-
-       return 0;
-}
-
-/* freeurbs
- * unlink and deallocate all URBs unconditionally
- * caller must make sure that no commands are still in progress
- * parameter:
- *     cs      controller state structure
- */
-static void freeurbs(struct cardstate *cs)
-{
-       struct bas_cardstate *ucs = cs->hw.bas;
-       struct bas_bc_state *ubc;
-       int i, j;
-
-       gig_dbg(DEBUG_INIT, "%s: killing URBs", __func__);
-       for (j = 0; j < BAS_CHANNELS; ++j) {
-               ubc = cs->bcs[j].hw.bas;
-               for (i = 0; i < BAS_OUTURBS; ++i) {
-                       usb_kill_urb(ubc->isoouturbs[i].urb);
-                       usb_free_urb(ubc->isoouturbs[i].urb);
-                       ubc->isoouturbs[i].urb = NULL;
-               }
-               for (i = 0; i < BAS_INURBS; ++i) {
-                       usb_kill_urb(ubc->isoinurbs[i]);
-                       usb_free_urb(ubc->isoinurbs[i]);
-                       ubc->isoinurbs[i] = NULL;
-               }
-       }
-       usb_kill_urb(ucs->urb_int_in);
-       usb_free_urb(ucs->urb_int_in);
-       ucs->urb_int_in = NULL;
-       usb_kill_urb(ucs->urb_cmd_out);
-       usb_free_urb(ucs->urb_cmd_out);
-       ucs->urb_cmd_out = NULL;
-       usb_kill_urb(ucs->urb_cmd_in);
-       usb_free_urb(ucs->urb_cmd_in);
-       ucs->urb_cmd_in = NULL;
-       usb_kill_urb(ucs->urb_ctrl);
-       usb_free_urb(ucs->urb_ctrl);
-       ucs->urb_ctrl = NULL;
-}
-
-/* gigaset_probe
- * This function is called when a new USB device is connected.
- * It checks whether the new device is handled by this driver.
- */
-static int gigaset_probe(struct usb_interface *interface,
-                        const struct usb_device_id *id)
-{
-       struct usb_host_interface *hostif;
-       struct usb_device *udev = interface_to_usbdev(interface);
-       struct cardstate *cs = NULL;
-       struct bas_cardstate *ucs = NULL;
-       struct bas_bc_state *ubc;
-       struct usb_endpoint_descriptor *endpoint;
-       int i, j;
-       int rc;
-
-       gig_dbg(DEBUG_INIT,
-               "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)",
-               __func__, le16_to_cpu(udev->descriptor.idVendor),
-               le16_to_cpu(udev->descriptor.idProduct));
-
-       /* set required alternate setting */
-       hostif = interface->cur_altsetting;
-       if (hostif->desc.bAlternateSetting != 3) {
-               gig_dbg(DEBUG_INIT,
-                       "%s: wrong alternate setting %d - trying to switch",
-                       __func__, hostif->desc.bAlternateSetting);
-               if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3)
-                   < 0) {
-                       dev_warn(&udev->dev, "usb_set_interface failed, "
-                                "device %d interface %d altsetting %d\n",
-                                udev->devnum, hostif->desc.bInterfaceNumber,
-                                hostif->desc.bAlternateSetting);
-                       return -ENODEV;
-               }
-               hostif = interface->cur_altsetting;
-       }
-
-       /* Reject application specific interfaces
-        */
-       if (hostif->desc.bInterfaceClass != 255) {
-               dev_warn(&udev->dev, "%s: bInterfaceClass == %d\n",
-                        __func__, hostif->desc.bInterfaceClass);
-               return -ENODEV;
-       }
-
-       if (hostif->desc.bNumEndpoints < 1)
-               return -ENODEV;
-
-       dev_info(&udev->dev,
-                "%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n",
-                __func__, le16_to_cpu(udev->descriptor.idVendor),
-                le16_to_cpu(udev->descriptor.idProduct));
-
-       /* allocate memory for our device state and initialize it */
-       cs = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
-                           GIGASET_MODULENAME);
-       if (!cs)
-               return -ENODEV;
-       ucs = cs->hw.bas;
-
-       /* save off device structure ptrs for later use */
-       usb_get_dev(udev);
-       ucs->udev = udev;
-       ucs->interface = interface;
-       cs->dev = &interface->dev;
-
-       /* allocate URBs:
-        * - one for the interrupt pipe
-        * - three for the different uses of the default control pipe
-        * - three for each isochronous pipe
-        */
-       if (!(ucs->urb_int_in = usb_alloc_urb(0, GFP_KERNEL)) ||
-           !(ucs->urb_cmd_in = usb_alloc_urb(0, GFP_KERNEL)) ||
-           !(ucs->urb_cmd_out = usb_alloc_urb(0, GFP_KERNEL)) ||
-           !(ucs->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL)))
-               goto allocerr;
-
-       for (j = 0; j < BAS_CHANNELS; ++j) {
-               ubc = cs->bcs[j].hw.bas;
-               for (i = 0; i < BAS_OUTURBS; ++i)
-                       if (!(ubc->isoouturbs[i].urb =
-                             usb_alloc_urb(BAS_NUMFRAMES, GFP_KERNEL)))
-                               goto allocerr;
-               for (i = 0; i < BAS_INURBS; ++i)
-                       if (!(ubc->isoinurbs[i] =
-                             usb_alloc_urb(BAS_NUMFRAMES, GFP_KERNEL)))
-                               goto allocerr;
-       }
-
-       ucs->rcvbuf = NULL;
-       ucs->rcvbuf_size = 0;
-
-       /* Fill the interrupt urb and send it to the core */
-       endpoint = &hostif->endpoint[0].desc;
-       usb_fill_int_urb(ucs->urb_int_in, udev,
-                        usb_rcvintpipe(udev,
-                                       usb_endpoint_num(endpoint)),
-                        ucs->int_in_buf, IP_MSGSIZE, read_int_callback, cs,
-                        endpoint->bInterval);
-       rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
-       if (rc != 0) {
-               dev_err(cs->dev, "could not submit interrupt URB: %s\n",
-                       get_usb_rcmsg(rc));
-               goto error;
-       }
-       ucs->retry_int_in = 0;
-
-       /* tell the device that the driver is ready */
-       rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0);
-       if (rc != 0)
-               goto error;
-
-       /* tell common part that the device is ready */
-       if (startmode == SM_LOCKED)
-               cs->mstate = MS_LOCKED;
-
-       /* save address of controller structure */
-       usb_set_intfdata(interface, cs);
-
-       rc = gigaset_start(cs);
-       if (rc < 0)
-               goto error;
-
-       return 0;
-
-allocerr:
-       dev_err(cs->dev, "could not allocate URBs\n");
-       rc = -ENOMEM;
-error:
-       freeurbs(cs);
-       usb_set_intfdata(interface, NULL);
-       usb_put_dev(udev);
-       gigaset_freecs(cs);
-       return rc;
-}
-
-/* gigaset_disconnect
- * This function is called when the Gigaset base is unplugged.
- */
-static void gigaset_disconnect(struct usb_interface *interface)
-{
-       struct cardstate *cs;
-       struct bas_cardstate *ucs;
-       int j;
-
-       cs = usb_get_intfdata(interface);
-
-       ucs = cs->hw.bas;
-
-       dev_info(cs->dev, "disconnecting Gigaset base\n");
-
-       /* mark base as not ready, all channels disconnected */
-       ucs->basstate = 0;
-
-       /* tell LL all channels are down */
-       for (j = 0; j < BAS_CHANNELS; ++j)
-               gigaset_bchannel_down(cs->bcs + j);
-
-       /* stop driver (common part) */
-       gigaset_stop(cs);
-
-       /* stop delayed work and URBs, free ressources */
-       del_timer_sync(&ucs->timer_ctrl);
-       del_timer_sync(&ucs->timer_atrdy);
-       del_timer_sync(&ucs->timer_cmd_in);
-       del_timer_sync(&ucs->timer_int_in);
-       cancel_work_sync(&ucs->int_in_wq);
-       freeurbs(cs);
-       usb_set_intfdata(interface, NULL);
-       kfree(ucs->rcvbuf);
-       ucs->rcvbuf = NULL;
-       ucs->rcvbuf_size = 0;
-       usb_put_dev(ucs->udev);
-       ucs->interface = NULL;
-       ucs->udev = NULL;
-       cs->dev = NULL;
-       gigaset_freecs(cs);
-}
-
-/* gigaset_suspend
- * This function is called before the USB connection is suspended
- * or before the USB device is reset.
- * In the latter case, message == PMSG_ON.
- */
-static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
-{
-       struct cardstate *cs = usb_get_intfdata(intf);
-       struct bas_cardstate *ucs = cs->hw.bas;
-       int rc;
-
-       /* set suspend flag; this stops AT command/response traffic */
-       if (update_basstate(ucs, BS_SUSPEND, 0) & BS_SUSPEND) {
-               gig_dbg(DEBUG_SUSPEND, "already suspended");
-               return 0;
-       }
-
-       /* wait a bit for blocking conditions to go away */
-       rc = wait_event_timeout(ucs->waitqueue,
-                               !(ucs->basstate &
-                                 (BS_B1OPEN | BS_B2OPEN | BS_ATRDPEND | BS_ATWRPEND)),
-                               BAS_TIMEOUT * HZ / 10);
-       gig_dbg(DEBUG_SUSPEND, "wait_event_timeout() -> %d", rc);
-
-       /* check for conditions preventing suspend */
-       if (ucs->basstate & (BS_B1OPEN | BS_B2OPEN | BS_ATRDPEND | BS_ATWRPEND)) {
-               dev_warn(cs->dev, "cannot suspend:\n");
-               if (ucs->basstate & BS_B1OPEN)
-                       dev_warn(cs->dev, " B channel 1 open\n");
-               if (ucs->basstate & BS_B2OPEN)
-                       dev_warn(cs->dev, " B channel 2 open\n");
-               if (ucs->basstate & BS_ATRDPEND)
-                       dev_warn(cs->dev, " receiving AT reply\n");
-               if (ucs->basstate & BS_ATWRPEND)
-                       dev_warn(cs->dev, " sending AT command\n");
-               update_basstate(ucs, 0, BS_SUSPEND);
-               return -EBUSY;
-       }
-
-       /* close AT channel if open */
-       if (ucs->basstate & BS_ATOPEN) {
-               gig_dbg(DEBUG_SUSPEND, "closing AT channel");
-               rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, 0);
-               if (rc) {
-                       update_basstate(ucs, 0, BS_SUSPEND);
-                       return rc;
-               }
-               wait_event_timeout(ucs->waitqueue, !ucs->pending,
-                                  BAS_TIMEOUT * HZ / 10);
-               /* in case of timeout, proceed anyway */
-       }
-
-       /* kill all URBs and delayed work that might still be pending */
-       usb_kill_urb(ucs->urb_ctrl);
-       usb_kill_urb(ucs->urb_int_in);
-       del_timer_sync(&ucs->timer_ctrl);
-       del_timer_sync(&ucs->timer_atrdy);
-       del_timer_sync(&ucs->timer_cmd_in);
-       del_timer_sync(&ucs->timer_int_in);
-
-       /* don't try to cancel int_in_wq from within reset as it
-        * might be the one requesting the reset
-        */
-       if (message.event != PM_EVENT_ON)
-               cancel_work_sync(&ucs->int_in_wq);
-
-       gig_dbg(DEBUG_SUSPEND, "suspend complete");
-       return 0;
-}
-
-/* gigaset_resume
- * This function is called after the USB connection has been resumed.
- */
-static int gigaset_resume(struct usb_interface *intf)
-{
-       struct cardstate *cs = usb_get_intfdata(intf);
-       struct bas_cardstate *ucs = cs->hw.bas;
-       int rc;
-
-       /* resubmit interrupt URB for spontaneous messages from base */
-       rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
-       if (rc) {
-               dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
-                       get_usb_rcmsg(rc));
-               return rc;
-       }
-       ucs->retry_int_in = 0;
-
-       /* clear suspend flag to reallow activity */
-       update_basstate(ucs, 0, BS_SUSPEND);
-
-       gig_dbg(DEBUG_SUSPEND, "resume complete");
-       return 0;
-}
-
-/* gigaset_pre_reset
- * This function is called before the USB connection is reset.
- */
-static int gigaset_pre_reset(struct usb_interface *intf)
-{
-       /* handle just like suspend */
-       return gigaset_suspend(intf, PMSG_ON);
-}
-
-/* gigaset_post_reset
- * This function is called after the USB connection has been reset.
- */
-static int gigaset_post_reset(struct usb_interface *intf)
-{
-       /* FIXME: send HD_DEVICE_INIT_ACK? */
-
-       /* resume operations */
-       return gigaset_resume(intf);
-}
-
-
-static const struct gigaset_ops gigops = {
-       .write_cmd = gigaset_write_cmd,
-       .write_room = gigaset_write_room,
-       .chars_in_buffer = gigaset_chars_in_buffer,
-       .brkchars = gigaset_brkchars,
-       .init_bchannel = gigaset_init_bchannel,
-       .close_bchannel = gigaset_close_bchannel,
-       .initbcshw = gigaset_initbcshw,
-       .freebcshw = gigaset_freebcshw,
-       .reinitbcshw = gigaset_reinitbcshw,
-       .initcshw = gigaset_initcshw,
-       .freecshw = gigaset_freecshw,
-       .set_modem_ctrl = gigaset_set_modem_ctrl,
-       .baud_rate = gigaset_baud_rate,
-       .set_line_ctrl = gigaset_set_line_ctrl,
-       .send_skb = gigaset_isoc_send_skb,
-       .handle_input = gigaset_isoc_input,
-};
-
-/* bas_gigaset_init
- * This function is called after the kernel module is loaded.
- */
-static int __init bas_gigaset_init(void)
-{
-       int result;
-
-       /* allocate memory for our driver state and initialize it */
-       driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
-                                   GIGASET_MODULENAME, GIGASET_DEVNAME,
-                                   &gigops, THIS_MODULE);
-       if (driver == NULL)
-               goto error;
-
-       /* register this driver with the USB subsystem */
-       result = usb_register(&gigaset_usb_driver);
-       if (result < 0) {
-               pr_err("error %d registering USB driver\n", -result);
-               goto error;
-       }
-
-       pr_info(DRIVER_DESC "\n");
-       return 0;
-
-error:
-       if (driver)
-               gigaset_freedriver(driver);
-       driver = NULL;
-       return -1;
-}
-
-/* bas_gigaset_exit
- * This function is called before the kernel module is unloaded.
- */
-static void __exit bas_gigaset_exit(void)
-{
-       struct bas_cardstate *ucs;
-       int i;
-
-       gigaset_blockdriver(driver); /* => probe will fail
-                                     * => no gigaset_start any more
-                                     */
-
-       /* stop all connected devices */
-       for (i = 0; i < driver->minors; i++) {
-               if (gigaset_shutdown(driver->cs + i) < 0)
-                       continue;               /* no device */
-               /* from now on, no isdn callback should be possible */
-
-               /* close all still open channels */
-               ucs = driver->cs[i].hw.bas;
-               if (ucs->basstate & BS_B1OPEN) {
-                       gig_dbg(DEBUG_INIT, "closing B1 channel");
-                       usb_control_msg(ucs->udev,
-                                       usb_sndctrlpipe(ucs->udev, 0),
-                                       HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ,
-                                       0, 0, NULL, 0, BAS_TIMEOUT);
-               }
-               if (ucs->basstate & BS_B2OPEN) {
-                       gig_dbg(DEBUG_INIT, "closing B2 channel");
-                       usb_control_msg(ucs->udev,
-                                       usb_sndctrlpipe(ucs->udev, 0),
-                                       HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ,
-                                       0, 0, NULL, 0, BAS_TIMEOUT);
-               }
-               if (ucs->basstate & BS_ATOPEN) {
-                       gig_dbg(DEBUG_INIT, "closing AT channel");
-                       usb_control_msg(ucs->udev,
-                                       usb_sndctrlpipe(ucs->udev, 0),
-                                       HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ,
-                                       0, 0, NULL, 0, BAS_TIMEOUT);
-               }
-               ucs->basstate = 0;
-       }
-
-       /* deregister this driver with the USB subsystem */
-       usb_deregister(&gigaset_usb_driver);
-       /* this will call the disconnect-callback */
-       /* from now on, no disconnect/probe callback should be running */
-
-       gigaset_freedriver(driver);
-       driver = NULL;
-}
-
-
-module_init(bas_gigaset_init);
-module_exit(bas_gigaset_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
deleted file mode 100644 (file)
index 9cb2ab5..0000000
+++ /dev/null
@@ -1,2520 +0,0 @@
-/*
- * Kernel CAPI interface for the Gigaset driver
- *
- * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
- *
- * =====================================================================
- *     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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/ratelimit.h>
-#include <linux/isdn/capilli.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/export.h>
-
-/* missing from kernelcapi.h */
-#define CapiNcpiNotSupportedByProtocol 0x0001
-#define CapiFlagsNotSupportedByProtocol        0x0002
-#define CapiAlertAlreadySent           0x0003
-#define CapiFacilitySpecificFunctionNotSupported       0x3011
-
-/* missing from capicmd.h */
-#define CAPI_CONNECT_IND_BASELEN       (CAPI_MSG_BASELEN + 4 + 2 + 8 * 1)
-#define CAPI_CONNECT_ACTIVE_IND_BASELEN        (CAPI_MSG_BASELEN + 4 + 3 * 1)
-#define CAPI_CONNECT_B3_IND_BASELEN    (CAPI_MSG_BASELEN + 4 + 1)
-#define CAPI_CONNECT_B3_ACTIVE_IND_BASELEN     (CAPI_MSG_BASELEN + 4 + 1)
-#define CAPI_DATA_B3_REQ_LEN64         (CAPI_MSG_BASELEN + 4 + 4 + 2 + 2 + 2 + 8)
-#define CAPI_DATA_B3_CONF_LEN          (CAPI_MSG_BASELEN + 4 + 2 + 2)
-#define CAPI_DISCONNECT_IND_LEN                (CAPI_MSG_BASELEN + 4 + 2)
-#define CAPI_DISCONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN + 4 + 2 + 1)
-#define CAPI_FACILITY_CONF_BASELEN     (CAPI_MSG_BASELEN + 4 + 2 + 2 + 1)
-/* most _CONF messages contain only Controller/PLCI/NCCI and Info parameters */
-#define CAPI_STDCONF_LEN               (CAPI_MSG_BASELEN + 4 + 2)
-
-#define CAPI_FACILITY_HANDSET  0x0000
-#define CAPI_FACILITY_DTMF     0x0001
-#define CAPI_FACILITY_V42BIS   0x0002
-#define CAPI_FACILITY_SUPPSVC  0x0003
-#define CAPI_FACILITY_WAKEUP   0x0004
-#define CAPI_FACILITY_LI       0x0005
-
-#define CAPI_SUPPSVC_GETSUPPORTED      0x0000
-#define CAPI_SUPPSVC_LISTEN            0x0001
-
-/* missing from capiutil.h */
-#define CAPIMSG_PLCI_PART(m)   CAPIMSG_U8(m, 9)
-#define CAPIMSG_NCCI_PART(m)   CAPIMSG_U16(m, 10)
-#define CAPIMSG_HANDLE_REQ(m)  CAPIMSG_U16(m, 18) /* DATA_B3_REQ/_IND only! */
-#define CAPIMSG_FLAGS(m)       CAPIMSG_U16(m, 20)
-#define CAPIMSG_SETCONTROLLER(m, contr)        capimsg_setu8(m, 8, contr)
-#define CAPIMSG_SETPLCI_PART(m, plci)  capimsg_setu8(m, 9, plci)
-#define CAPIMSG_SETNCCI_PART(m, ncci)  capimsg_setu16(m, 10, ncci)
-#define CAPIMSG_SETFLAGS(m, flags)     capimsg_setu16(m, 20, flags)
-
-/* parameters with differing location in DATA_B3_CONF/_RESP: */
-#define CAPIMSG_SETHANDLE_CONF(m, handle)      capimsg_setu16(m, 12, handle)
-#define        CAPIMSG_SETINFO_CONF(m, info)           capimsg_setu16(m, 14, info)
-
-/* Flags (DATA_B3_REQ/_IND) */
-#define CAPI_FLAGS_DELIVERY_CONFIRMATION       0x04
-#define CAPI_FLAGS_RESERVED                    (~0x1f)
-
-/* buffer sizes */
-#define MAX_BC_OCTETS 11
-#define MAX_HLC_OCTETS 3
-#define MAX_NUMBER_DIGITS 20
-#define MAX_FMT_IE_LEN 20
-
-/* values for bcs->apconnstate */
-#define APCONN_NONE    0       /* inactive/listening */
-#define APCONN_SETUP   1       /* connecting */
-#define APCONN_ACTIVE  2       /* B channel up */
-
-/* registered application data structure */
-struct gigaset_capi_appl {
-       struct list_head ctrlist;
-       struct gigaset_capi_appl *bcnext;
-       u16 id;
-       struct capi_register_params rp;
-       u16 nextMessageNumber;
-       u32 listenInfoMask;
-       u32 listenCIPmask;
-};
-
-/* CAPI specific controller data structure */
-struct gigaset_capi_ctr {
-       struct capi_ctr ctr;
-       struct list_head appls;
-       struct sk_buff_head sendqueue;
-       atomic_t sendqlen;
-       /* two _cmsg structures possibly used concurrently: */
-       _cmsg hcmsg;    /* for message composition triggered from hardware */
-       _cmsg acmsg;    /* for dissection of messages sent from application */
-       u8 bc_buf[MAX_BC_OCTETS + 1];
-       u8 hlc_buf[MAX_HLC_OCTETS + 1];
-       u8 cgpty_buf[MAX_NUMBER_DIGITS + 3];
-       u8 cdpty_buf[MAX_NUMBER_DIGITS + 2];
-};
-
-/* CIP Value table (from CAPI 2.0 standard, ch. 6.1) */
-static struct {
-       u8 *bc;
-       u8 *hlc;
-} cip2bchlc[] = {
-       [1] = { "8090A3", NULL },       /* Speech (A-law) */
-       [2] = { "8890", NULL },         /* Unrestricted digital information */
-       [3] = { "8990", NULL },         /* Restricted digital information */
-       [4] = { "9090A3", NULL },       /* 3,1 kHz audio (A-law) */
-       [5] = { "9190", NULL },         /* 7 kHz audio */
-       [6] = { "9890", NULL },         /* Video */
-       [7] = { "88C0C6E6", NULL },     /* Packet mode */
-       [8] = { "8890218F", NULL },     /* 56 kbit/s rate adaptation */
-       [9] = { "9190A5", NULL },       /* Unrestricted digital information
-                                        * with tones/announcements */
-       [16] = { "8090A3", "9181" },    /* Telephony */
-       [17] = { "9090A3", "9184" },    /* Group 2/3 facsimile */
-       [18] = { "8890", "91A1" },      /* Group 4 facsimile Class 1 */
-       [19] = { "8890", "91A4" },      /* Teletex service basic and mixed mode
-                                        * and Group 4 facsimile service
-                                        * Classes II and III */
-       [20] = { "8890", "91A8" },      /* Teletex service basic and
-                                        * processable mode */
-       [21] = { "8890", "91B1" },      /* Teletex service basic mode */
-       [22] = { "8890", "91B2" },      /* International interworking for
-                                        * Videotex */
-       [23] = { "8890", "91B5" },      /* Telex */
-       [24] = { "8890", "91B8" },      /* Message Handling Systems
-                                        * in accordance with X.400 */
-       [25] = { "8890", "91C1" },      /* OSI application
-                                        * in accordance with X.200 */
-       [26] = { "9190A5", "9181" },    /* 7 kHz telephony */
-       [27] = { "9190A5", "916001" },  /* Video telephony, first connection */
-       [28] = { "8890", "916002" },    /* Video telephony, second connection */
-};
-
-/*
- * helper functions
- * ================
- */
-
-/*
- * emit unsupported parameter warning
- */
-static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param,
-                                       char *msgname, char *paramname)
-{
-       if (param && *param)
-               dev_warn(cs->dev, "%s: ignoring unsupported parameter: %s\n",
-                        msgname, paramname);
-}
-
-/*
- * convert an IE from Gigaset hex string to ETSI binary representation
- * including length byte
- * return value: result length, -1 on error
- */
-static int encode_ie(char *in, u8 *out, int maxlen)
-{
-       int l = 0;
-       while (*in) {
-               if (!isxdigit(in[0]) || !isxdigit(in[1]) || l >= maxlen)
-                       return -1;
-               out[++l] = (hex_to_bin(in[0]) << 4) + hex_to_bin(in[1]);
-               in += 2;
-       }
-       out[0] = l;
-       return l;
-}
-
-/*
- * convert an IE from ETSI binary representation including length byte
- * to Gigaset hex string
- */
-static void decode_ie(u8 *in, char *out)
-{
-       int i = *in;
-       while (i-- > 0) {
-               /* ToDo: conversion to upper case necessary? */
-               *out++ = toupper(hex_asc_hi(*++in));
-               *out++ = toupper(hex_asc_lo(*in));
-       }
-}
-
-/*
- * retrieve application data structure for an application ID
- */
-static inline struct gigaset_capi_appl *
-get_appl(struct gigaset_capi_ctr *iif, u16 appl)
-{
-       struct gigaset_capi_appl *ap;
-
-       list_for_each_entry(ap, &iif->appls, ctrlist)
-               if (ap->id == appl)
-                       return ap;
-       return NULL;
-}
-
-/*
- * dump CAPI message to kernel messages for debugging
- */
-static inline void dump_cmsg(enum debuglevel level, const char *tag, _cmsg *p)
-{
-#ifdef CONFIG_GIGASET_DEBUG
-       /* dump at most 20 messages in 20 secs */
-       static DEFINE_RATELIMIT_STATE(msg_dump_ratelimit, 20 * HZ, 20);
-       _cdebbuf *cdb;
-
-       if (!(gigaset_debuglevel & level))
-               return;
-       if (!___ratelimit(&msg_dump_ratelimit, tag))
-               return;
-
-       cdb = capi_cmsg2str(p);
-       if (cdb) {
-               gig_dbg(level, "%s: [%d] %s", tag, p->ApplId, cdb->buf);
-               cdebbuf_free(cdb);
-       } else {
-               gig_dbg(level, "%s: [%d] %s", tag, p->ApplId,
-                       capi_cmd2str(p->Command, p->Subcommand));
-       }
-#endif
-}
-
-static inline void dump_rawmsg(enum debuglevel level, const char *tag,
-                              unsigned char *data)
-{
-#ifdef CONFIG_GIGASET_DEBUG
-       char *dbgline;
-       int i, l;
-
-       if (!(gigaset_debuglevel & level))
-               return;
-
-       l = CAPIMSG_LEN(data);
-       if (l < 12) {
-               gig_dbg(level, "%s: ??? LEN=%04d", tag, l);
-               return;
-       }
-       gig_dbg(level, "%s: 0x%02x:0x%02x: ID=%03d #0x%04x LEN=%04d NCCI=0x%x",
-               tag, CAPIMSG_COMMAND(data), CAPIMSG_SUBCOMMAND(data),
-               CAPIMSG_APPID(data), CAPIMSG_MSGID(data), l,
-               CAPIMSG_CONTROL(data));
-       l -= 12;
-       if (l <= 0)
-               return;
-       if (l > 64)
-               l = 64; /* arbitrary limit */
-       dbgline = kmalloc_array(3, l, GFP_ATOMIC);
-       if (!dbgline)
-               return;
-       for (i = 0; i < l; i++) {
-               dbgline[3 * i] = hex_asc_hi(data[12 + i]);
-               dbgline[3 * i + 1] = hex_asc_lo(data[12 + i]);
-               dbgline[3 * i + 2] = ' ';
-       }
-       dbgline[3 * l - 1] = '\0';
-       gig_dbg(level, "  %s", dbgline);
-       kfree(dbgline);
-       if (CAPIMSG_COMMAND(data) == CAPI_DATA_B3 &&
-           (CAPIMSG_SUBCOMMAND(data) == CAPI_REQ ||
-            CAPIMSG_SUBCOMMAND(data) == CAPI_IND)) {
-               l = CAPIMSG_DATALEN(data);
-               gig_dbg(level, "   DataLength=%d", l);
-               if (l <= 0 || !(gigaset_debuglevel & DEBUG_LLDATA))
-                       return;
-               if (l > 64)
-                       l = 64; /* arbitrary limit */
-               dbgline = kmalloc_array(3, l, GFP_ATOMIC);
-               if (!dbgline)
-                       return;
-               data += CAPIMSG_LEN(data);
-               for (i = 0; i < l; i++) {
-                       dbgline[3 * i] = hex_asc_hi(data[i]);
-                       dbgline[3 * i + 1] = hex_asc_lo(data[i]);
-                       dbgline[3 * i + 2] = ' ';
-               }
-               dbgline[3 * l - 1] = '\0';
-               gig_dbg(level, "  %s", dbgline);
-               kfree(dbgline);
-       }
-#endif
-}
-
-/*
- * format CAPI IE as string
- */
-
-#ifdef CONFIG_GIGASET_DEBUG
-static const char *format_ie(const char *ie)
-{
-       static char result[3 * MAX_FMT_IE_LEN];
-       int len, count;
-       char *pout = result;
-
-       if (!ie)
-               return "NULL";
-
-       count = len = ie[0];
-       if (count > MAX_FMT_IE_LEN)
-               count = MAX_FMT_IE_LEN - 1;
-       while (count--) {
-               *pout++ = hex_asc_hi(*++ie);
-               *pout++ = hex_asc_lo(*ie);
-               *pout++ = ' ';
-       }
-       if (len > MAX_FMT_IE_LEN) {
-               *pout++ = '.';
-               *pout++ = '.';
-               *pout++ = '.';
-       }
-       *--pout = 0;
-       return result;
-}
-#endif
-
-/*
- * emit DATA_B3_CONF message
- */
-static void send_data_b3_conf(struct cardstate *cs, struct capi_ctr *ctr,
-                             u16 appl, u16 msgid, int channel,
-                             u16 handle, u16 info)
-{
-       struct sk_buff *cskb;
-       u8 *msg;
-
-       cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC);
-       if (!cskb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       /* frequent message, avoid _cmsg overhead */
-       msg = __skb_put(cskb, CAPI_DATA_B3_CONF_LEN);
-       CAPIMSG_SETLEN(msg, CAPI_DATA_B3_CONF_LEN);
-       CAPIMSG_SETAPPID(msg, appl);
-       CAPIMSG_SETCOMMAND(msg, CAPI_DATA_B3);
-       CAPIMSG_SETSUBCOMMAND(msg,  CAPI_CONF);
-       CAPIMSG_SETMSGID(msg, msgid);
-       CAPIMSG_SETCONTROLLER(msg, ctr->cnr);
-       CAPIMSG_SETPLCI_PART(msg, channel);
-       CAPIMSG_SETNCCI_PART(msg, 1);
-       CAPIMSG_SETHANDLE_CONF(msg, handle);
-       CAPIMSG_SETINFO_CONF(msg, info);
-
-       /* emit message */
-       dump_rawmsg(DEBUG_MCMD, __func__, msg);
-       capi_ctr_handle_message(ctr, appl, cskb);
-}
-
-
-/*
- * driver interface functions
- * ==========================
- */
-
-/**
- * gigaset_skb_sent() - acknowledge transmission of outgoing skb
- * @bcs:       B channel descriptor structure.
- * @skb:       sent data.
- *
- * Called by hardware module {bas,ser,usb}_gigaset when the data in a
- * skb has been successfully sent, for signalling completion to the LL.
- */
-void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
-{
-       struct cardstate *cs = bcs->cs;
-       struct gigaset_capi_ctr *iif = cs->iif;
-       struct gigaset_capi_appl *ap = bcs->ap;
-       unsigned char *req = skb_mac_header(dskb);
-       u16 flags;
-
-       /* update statistics */
-       ++bcs->trans_up;
-
-       if (!ap) {
-               gig_dbg(DEBUG_MCMD, "%s: application gone", __func__);
-               return;
-       }
-
-       /* don't send further B3 messages if disconnected */
-       if (bcs->apconnstate < APCONN_ACTIVE) {
-               gig_dbg(DEBUG_MCMD, "%s: disconnected", __func__);
-               return;
-       }
-
-       /*
-        * send DATA_B3_CONF if "delivery confirmation" bit was set in request;
-        * otherwise it has already been sent by do_data_b3_req()
-        */
-       flags = CAPIMSG_FLAGS(req);
-       if (flags & CAPI_FLAGS_DELIVERY_CONFIRMATION)
-               send_data_b3_conf(cs, &iif->ctr, ap->id, CAPIMSG_MSGID(req),
-                                 bcs->channel + 1, CAPIMSG_HANDLE_REQ(req),
-                                 (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION) ?
-                                 CapiFlagsNotSupportedByProtocol :
-                                 CAPI_NOERROR);
-}
-EXPORT_SYMBOL_GPL(gigaset_skb_sent);
-
-/**
- * gigaset_skb_rcvd() - pass received skb to LL
- * @bcs:       B channel descriptor structure.
- * @skb:       received data.
- *
- * Called by hardware module {bas,ser,usb}_gigaset when user data has
- * been successfully received, for passing to the LL.
- * Warning: skb must not be accessed anymore!
- */
-void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
-{
-       struct cardstate *cs = bcs->cs;
-       struct gigaset_capi_ctr *iif = cs->iif;
-       struct gigaset_capi_appl *ap = bcs->ap;
-       int len = skb->len;
-
-       /* update statistics */
-       bcs->trans_down++;
-
-       if (!ap) {
-               gig_dbg(DEBUG_MCMD, "%s: application gone", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-
-       /* don't send further B3 messages if disconnected */
-       if (bcs->apconnstate < APCONN_ACTIVE) {
-               gig_dbg(DEBUG_MCMD, "%s: disconnected", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-
-       /*
-        * prepend DATA_B3_IND message to payload
-        * Parameters: NCCI = 1, all others 0/unused
-        * frequent message, avoid _cmsg overhead
-        */
-       skb_push(skb, CAPI_DATA_B3_REQ_LEN);
-       CAPIMSG_SETLEN(skb->data, CAPI_DATA_B3_REQ_LEN);
-       CAPIMSG_SETAPPID(skb->data, ap->id);
-       CAPIMSG_SETCOMMAND(skb->data, CAPI_DATA_B3);
-       CAPIMSG_SETSUBCOMMAND(skb->data,  CAPI_IND);
-       CAPIMSG_SETMSGID(skb->data, ap->nextMessageNumber++);
-       CAPIMSG_SETCONTROLLER(skb->data, iif->ctr.cnr);
-       CAPIMSG_SETPLCI_PART(skb->data, bcs->channel + 1);
-       CAPIMSG_SETNCCI_PART(skb->data, 1);
-       /* Data parameter not used */
-       CAPIMSG_SETDATALEN(skb->data, len);
-       /* Data handle parameter not used */
-       CAPIMSG_SETFLAGS(skb->data, 0);
-       /* Data64 parameter not present */
-
-       /* emit message */
-       dump_rawmsg(DEBUG_MCMD, __func__, skb->data);
-       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-}
-EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
-
-/**
- * gigaset_isdn_rcv_err() - signal receive error
- * @bcs:       B channel descriptor structure.
- *
- * Called by hardware module {bas,ser,usb}_gigaset when a receive error
- * has occurred, for signalling to the LL.
- */
-void gigaset_isdn_rcv_err(struct bc_state *bcs)
-{
-       /* if currently ignoring packets, just count down */
-       if (bcs->ignore) {
-               bcs->ignore--;
-               return;
-       }
-
-       /* update statistics */
-       bcs->corrupted++;
-
-       /* ToDo: signal error -> LL */
-}
-EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
-
-/**
- * gigaset_isdn_icall() - signal incoming call
- * @at_state:  connection state structure.
- *
- * Called by main module at tasklet level to notify the LL that an incoming
- * call has been received. @at_state contains the parameters of the call.
- *
- * Return value: call disposition (ICALL_*)
- */
-int gigaset_isdn_icall(struct at_state_t *at_state)
-{
-       struct cardstate *cs = at_state->cs;
-       struct bc_state *bcs = at_state->bcs;
-       struct gigaset_capi_ctr *iif = cs->iif;
-       struct gigaset_capi_appl *ap;
-       u32 actCIPmask;
-       struct sk_buff *skb;
-       unsigned int msgsize;
-       unsigned long flags;
-       int i;
-
-       /*
-        * ToDo: signal calls without a free B channel, too
-        * (requires a u8 handle for the at_state structure that can
-        * be stored in the PLCI and used in the CONNECT_RESP message
-        * handler to retrieve it)
-        */
-       if (!bcs)
-               return ICALL_IGNORE;
-
-       /* prepare CONNECT_IND message, using B channel number as PLCI */
-       capi_cmsg_header(&iif->hcmsg, 0, CAPI_CONNECT, CAPI_IND, 0,
-                        iif->ctr.cnr | ((bcs->channel + 1) << 8));
-
-       /* minimum size, all structs empty */
-       msgsize = CAPI_CONNECT_IND_BASELEN;
-
-       /* Bearer Capability (mandatory) */
-       if (at_state->str_var[STR_ZBC]) {
-               /* pass on BC from Gigaset */
-               if (encode_ie(at_state->str_var[STR_ZBC], iif->bc_buf,
-                             MAX_BC_OCTETS) < 0) {
-                       dev_warn(cs->dev, "RING ignored - bad BC %s\n",
-                                at_state->str_var[STR_ZBC]);
-                       return ICALL_IGNORE;
-               }
-
-               /* look up corresponding CIP value */
-               iif->hcmsg.CIPValue = 0;        /* default if nothing found */
-               for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
-                       if (cip2bchlc[i].bc != NULL &&
-                           cip2bchlc[i].hlc == NULL &&
-                           !strcmp(cip2bchlc[i].bc,
-                                   at_state->str_var[STR_ZBC])) {
-                               iif->hcmsg.CIPValue = i;
-                               break;
-                       }
-       } else {
-               /* no BC (internal call): assume CIP 1 (speech, A-law) */
-               iif->hcmsg.CIPValue = 1;
-               encode_ie(cip2bchlc[1].bc, iif->bc_buf, MAX_BC_OCTETS);
-       }
-       iif->hcmsg.BC = iif->bc_buf;
-       msgsize += iif->hcmsg.BC[0];
-
-       /* High Layer Compatibility (optional) */
-       if (at_state->str_var[STR_ZHLC]) {
-               /* pass on HLC from Gigaset */
-               if (encode_ie(at_state->str_var[STR_ZHLC], iif->hlc_buf,
-                             MAX_HLC_OCTETS) < 0) {
-                       dev_warn(cs->dev, "RING ignored - bad HLC %s\n",
-                                at_state->str_var[STR_ZHLC]);
-                       return ICALL_IGNORE;
-               }
-               iif->hcmsg.HLC = iif->hlc_buf;
-               msgsize += iif->hcmsg.HLC[0];
-
-               /* look up corresponding CIP value */
-               /* keep BC based CIP value if none found */
-               if (at_state->str_var[STR_ZBC])
-                       for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
-                               if (cip2bchlc[i].hlc != NULL &&
-                                   !strcmp(cip2bchlc[i].hlc,
-                                           at_state->str_var[STR_ZHLC]) &&
-                                   !strcmp(cip2bchlc[i].bc,
-                                           at_state->str_var[STR_ZBC])) {
-                                       iif->hcmsg.CIPValue = i;
-                                       break;
-                               }
-       }
-
-       /* Called Party Number (optional) */
-       if (at_state->str_var[STR_ZCPN]) {
-               i = strlen(at_state->str_var[STR_ZCPN]);
-               if (i > MAX_NUMBER_DIGITS) {
-                       dev_warn(cs->dev, "RING ignored - bad number %s\n",
-                                at_state->str_var[STR_ZBC]);
-                       return ICALL_IGNORE;
-               }
-               iif->cdpty_buf[0] = i + 1;
-               iif->cdpty_buf[1] = 0x80; /* type / numbering plan unknown */
-               memcpy(iif->cdpty_buf + 2, at_state->str_var[STR_ZCPN], i);
-               iif->hcmsg.CalledPartyNumber = iif->cdpty_buf;
-               msgsize += iif->hcmsg.CalledPartyNumber[0];
-       }
-
-       /* Calling Party Number (optional) */
-       if (at_state->str_var[STR_NMBR]) {
-               i = strlen(at_state->str_var[STR_NMBR]);
-               if (i > MAX_NUMBER_DIGITS) {
-                       dev_warn(cs->dev, "RING ignored - bad number %s\n",
-                                at_state->str_var[STR_ZBC]);
-                       return ICALL_IGNORE;
-               }
-               iif->cgpty_buf[0] = i + 2;
-               iif->cgpty_buf[1] = 0x00; /* type / numbering plan unknown */
-               iif->cgpty_buf[2] = 0x80; /* pres. allowed, not screened */
-               memcpy(iif->cgpty_buf + 3, at_state->str_var[STR_NMBR], i);
-               iif->hcmsg.CallingPartyNumber = iif->cgpty_buf;
-               msgsize += iif->hcmsg.CallingPartyNumber[0];
-       }
-
-       /* remaining parameters (not supported, always left NULL):
-        * - CalledPartySubaddress
-        * - CallingPartySubaddress
-        * - AdditionalInfo
-        *   - BChannelinformation
-        *   - Keypadfacility
-        *   - Useruserdata
-        *   - Facilitydataarray
-        */
-
-       gig_dbg(DEBUG_CMD, "icall: PLCI %x CIP %d BC %s",
-               iif->hcmsg.adr.adrPLCI, iif->hcmsg.CIPValue,
-               format_ie(iif->hcmsg.BC));
-       gig_dbg(DEBUG_CMD, "icall: HLC %s",
-               format_ie(iif->hcmsg.HLC));
-       gig_dbg(DEBUG_CMD, "icall: CgPty %s",
-               format_ie(iif->hcmsg.CallingPartyNumber));
-       gig_dbg(DEBUG_CMD, "icall: CdPty %s",
-               format_ie(iif->hcmsg.CalledPartyNumber));
-
-       /* scan application list for matching listeners */
-       spin_lock_irqsave(&bcs->aplock, flags);
-       if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE) {
-               dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
-                        __func__, bcs->ap, bcs->apconnstate);
-               bcs->ap = NULL;
-               bcs->apconnstate = APCONN_NONE;
-       }
-       spin_unlock_irqrestore(&bcs->aplock, flags);
-       actCIPmask = 1 | (1 << iif->hcmsg.CIPValue);
-       list_for_each_entry(ap, &iif->appls, ctrlist)
-               if (actCIPmask & ap->listenCIPmask) {
-                       /* build CONNECT_IND message for this application */
-                       iif->hcmsg.ApplId = ap->id;
-                       iif->hcmsg.Messagenumber = ap->nextMessageNumber++;
-
-                       skb = alloc_skb(msgsize, GFP_ATOMIC);
-                       if (!skb) {
-                               dev_err(cs->dev, "%s: out of memory\n",
-                                       __func__);
-                               break;
-                       }
-                       if (capi_cmsg2message(&iif->hcmsg,
-                                             __skb_put(skb, msgsize))) {
-                               dev_err(cs->dev, "%s: message parser failure\n",
-                                       __func__);
-                               dev_kfree_skb_any(skb);
-                               break;
-                       }
-                       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
-
-                       /* add to listeners on this B channel, update state */
-                       spin_lock_irqsave(&bcs->aplock, flags);
-                       ap->bcnext = bcs->ap;
-                       bcs->ap = ap;
-                       bcs->chstate |= CHS_NOTIFY_LL;
-                       bcs->apconnstate = APCONN_SETUP;
-                       spin_unlock_irqrestore(&bcs->aplock, flags);
-
-                       /* emit message */
-                       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-               }
-
-       /*
-        * Return "accept" if any listeners.
-        * Gigaset will send ALERTING.
-        * There doesn't seem to be a way to avoid this.
-        */
-       return bcs->ap ? ICALL_ACCEPT : ICALL_IGNORE;
-}
-
-/*
- * send a DISCONNECT_IND message to an application
- * does not sleep, clobbers the controller's hcmsg structure
- */
-static void send_disconnect_ind(struct bc_state *bcs,
-                               struct gigaset_capi_appl *ap, u16 reason)
-{
-       struct cardstate *cs = bcs->cs;
-       struct gigaset_capi_ctr *iif = cs->iif;
-       struct sk_buff *skb;
-
-       if (bcs->apconnstate == APCONN_NONE)
-               return;
-
-       capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND,
-                        ap->nextMessageNumber++,
-                        iif->ctr.cnr | ((bcs->channel + 1) << 8));
-       iif->hcmsg.Reason = reason;
-       skb = alloc_skb(CAPI_DISCONNECT_IND_LEN, GFP_ATOMIC);
-       if (!skb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       if (capi_cmsg2message(&iif->hcmsg,
-                             __skb_put(skb, CAPI_DISCONNECT_IND_LEN))) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
-       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-}
-
-/*
- * send a DISCONNECT_B3_IND message to an application
- * Parameters: NCCI = 1, NCPI empty, Reason_B3 = 0
- * does not sleep, clobbers the controller's hcmsg structure
- */
-static void send_disconnect_b3_ind(struct bc_state *bcs,
-                                  struct gigaset_capi_appl *ap)
-{
-       struct cardstate *cs = bcs->cs;
-       struct gigaset_capi_ctr *iif = cs->iif;
-       struct sk_buff *skb;
-
-       /* nothing to do if no logical connection active */
-       if (bcs->apconnstate < APCONN_ACTIVE)
-               return;
-       bcs->apconnstate = APCONN_SETUP;
-
-       capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
-                        ap->nextMessageNumber++,
-                        iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
-       skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_ATOMIC);
-       if (!skb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       if (capi_cmsg2message(&iif->hcmsg,
-                         __skb_put(skb, CAPI_DISCONNECT_B3_IND_BASELEN))) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
-       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-}
-
-/**
- * gigaset_isdn_connD() - signal D channel connect
- * @bcs:       B channel descriptor structure.
- *
- * Called by main module at tasklet level to notify the LL that the D channel
- * connection has been established.
- */
-void gigaset_isdn_connD(struct bc_state *bcs)
-{
-       struct cardstate *cs = bcs->cs;
-       struct gigaset_capi_ctr *iif = cs->iif;
-       struct gigaset_capi_appl *ap;
-       struct sk_buff *skb;
-       unsigned int msgsize;
-       unsigned long flags;
-
-       spin_lock_irqsave(&bcs->aplock, flags);
-       ap = bcs->ap;
-       if (!ap) {
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-               gig_dbg(DEBUG_CMD, "%s: application gone", __func__);
-               return;
-       }
-       if (bcs->apconnstate == APCONN_NONE) {
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-               dev_warn(cs->dev, "%s: application %u not connected\n",
-                        __func__, ap->id);
-               return;
-       }
-       spin_unlock_irqrestore(&bcs->aplock, flags);
-       while (ap->bcnext) {
-               /* this should never happen */
-               dev_warn(cs->dev, "%s: dropping extra application %u\n",
-                        __func__, ap->bcnext->id);
-               send_disconnect_ind(bcs, ap->bcnext,
-                                   CapiCallGivenToOtherApplication);
-               ap->bcnext = ap->bcnext->bcnext;
-       }
-
-       /* prepare CONNECT_ACTIVE_IND message
-        * Note: LLC not supported by device
-        */
-       capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_CONNECT_ACTIVE, CAPI_IND,
-                        ap->nextMessageNumber++,
-                        iif->ctr.cnr | ((bcs->channel + 1) << 8));
-
-       /* minimum size, all structs empty */
-       msgsize = CAPI_CONNECT_ACTIVE_IND_BASELEN;
-
-       /* ToDo: set parameter: Connected number
-        * (requires ev-layer state machine extension to collect
-        * ZCON device reply)
-        */
-
-       /* build and emit CONNECT_ACTIVE_IND message */
-       skb = alloc_skb(msgsize, GFP_ATOMIC);
-       if (!skb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       if (capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize))) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
-       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-}
-
-/**
- * gigaset_isdn_hupD() - signal D channel hangup
- * @bcs:       B channel descriptor structure.
- *
- * Called by main module at tasklet level to notify the LL that the D channel
- * connection has been shut down.
- */
-void gigaset_isdn_hupD(struct bc_state *bcs)
-{
-       struct gigaset_capi_appl *ap;
-       unsigned long flags;
-
-       /*
-        * ToDo: pass on reason code reported by device
-        * (requires ev-layer state machine extension to collect
-        * ZCAU device reply)
-        */
-       spin_lock_irqsave(&bcs->aplock, flags);
-       while (bcs->ap != NULL) {
-               ap = bcs->ap;
-               bcs->ap = ap->bcnext;
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-               send_disconnect_b3_ind(bcs, ap);
-               send_disconnect_ind(bcs, ap, 0);
-               spin_lock_irqsave(&bcs->aplock, flags);
-       }
-       bcs->apconnstate = APCONN_NONE;
-       spin_unlock_irqrestore(&bcs->aplock, flags);
-}
-
-/**
- * gigaset_isdn_connB() - signal B channel connect
- * @bcs:       B channel descriptor structure.
- *
- * Called by main module at tasklet level to notify the LL that the B channel
- * connection has been established.
- */
-void gigaset_isdn_connB(struct bc_state *bcs)
-{
-       struct cardstate *cs = bcs->cs;
-       struct gigaset_capi_ctr *iif = cs->iif;
-       struct gigaset_capi_appl *ap;
-       struct sk_buff *skb;
-       unsigned long flags;
-       unsigned int msgsize;
-       u8 command;
-
-       spin_lock_irqsave(&bcs->aplock, flags);
-       ap = bcs->ap;
-       if (!ap) {
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-               gig_dbg(DEBUG_CMD, "%s: application gone", __func__);
-               return;
-       }
-       if (!bcs->apconnstate) {
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-               dev_warn(cs->dev, "%s: application %u not connected\n",
-                        __func__, ap->id);
-               return;
-       }
-
-       /*
-        * emit CONNECT_B3_ACTIVE_IND if we already got CONNECT_B3_REQ;
-        * otherwise we have to emit CONNECT_B3_IND first, and follow up with
-        * CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP
-        * Parameters in both cases always: NCCI = 1, NCPI empty
-        */
-       if (bcs->apconnstate >= APCONN_ACTIVE) {
-               command = CAPI_CONNECT_B3_ACTIVE;
-               msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
-       } else {
-               command = CAPI_CONNECT_B3;
-               msgsize = CAPI_CONNECT_B3_IND_BASELEN;
-       }
-       bcs->apconnstate = APCONN_ACTIVE;
-
-       spin_unlock_irqrestore(&bcs->aplock, flags);
-
-       while (ap->bcnext) {
-               /* this should never happen */
-               dev_warn(cs->dev, "%s: dropping extra application %u\n",
-                        __func__, ap->bcnext->id);
-               send_disconnect_ind(bcs, ap->bcnext,
-                                   CapiCallGivenToOtherApplication);
-               ap->bcnext = ap->bcnext->bcnext;
-       }
-
-       capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND,
-                        ap->nextMessageNumber++,
-                        iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
-       skb = alloc_skb(msgsize, GFP_ATOMIC);
-       if (!skb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       if (capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize))) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
-       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-}
-
-/**
- * gigaset_isdn_hupB() - signal B channel hangup
- * @bcs:       B channel descriptor structure.
- *
- * Called by main module to notify the LL that the B channel connection has
- * been shut down.
- */
-void gigaset_isdn_hupB(struct bc_state *bcs)
-{
-       struct gigaset_capi_appl *ap = bcs->ap;
-
-       /* ToDo: assure order of DISCONNECT_B3_IND and DISCONNECT_IND ? */
-
-       if (!ap) {
-               gig_dbg(DEBUG_CMD, "%s: application gone", __func__);
-               return;
-       }
-
-       send_disconnect_b3_ind(bcs, ap);
-}
-
-/**
- * gigaset_isdn_start() - signal device availability
- * @cs:                device descriptor structure.
- *
- * Called by main module to notify the LL that the device is available for
- * use.
- */
-void gigaset_isdn_start(struct cardstate *cs)
-{
-       struct gigaset_capi_ctr *iif = cs->iif;
-
-       /* fill profile data: manufacturer name */
-       strcpy(iif->ctr.manu, "Siemens");
-       /* CAPI and device version */
-       iif->ctr.version.majorversion = 2;              /* CAPI 2.0 */
-       iif->ctr.version.minorversion = 0;
-       /* ToDo: check/assert cs->gotfwver? */
-       iif->ctr.version.majormanuversion = cs->fwver[0];
-       iif->ctr.version.minormanuversion = cs->fwver[1];
-       /* number of B channels supported */
-       iif->ctr.profile.nbchannel = cs->channels;
-       /* global options: internal controller, supplementary services */
-       iif->ctr.profile.goptions = 0x11;
-       /* B1 protocols: 64 kbit/s HDLC or transparent */
-       iif->ctr.profile.support1 =  0x03;
-       /* B2 protocols: transparent only */
-       /* ToDo: X.75 SLP ? */
-       iif->ctr.profile.support2 =  0x02;
-       /* B3 protocols: transparent only */
-       iif->ctr.profile.support3 =  0x01;
-       /* no serial number */
-       strcpy(iif->ctr.serial, "0");
-       capi_ctr_ready(&iif->ctr);
-}
-
-/**
- * gigaset_isdn_stop() - signal device unavailability
- * @cs:                device descriptor structure.
- *
- * Called by main module to notify the LL that the device is no longer
- * available for use.
- */
-void gigaset_isdn_stop(struct cardstate *cs)
-{
-       struct gigaset_capi_ctr *iif = cs->iif;
-       capi_ctr_down(&iif->ctr);
-}
-
-/*
- * kernel CAPI callback methods
- * ============================
- */
-
-/*
- * register CAPI application
- */
-static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl,
-                                 capi_register_params *rp)
-{
-       struct gigaset_capi_ctr *iif
-               = container_of(ctr, struct gigaset_capi_ctr, ctr);
-       struct cardstate *cs = ctr->driverdata;
-       struct gigaset_capi_appl *ap;
-
-       gig_dbg(DEBUG_CMD, "%s [%u] l3cnt=%u blkcnt=%u blklen=%u",
-               __func__, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
-
-       list_for_each_entry(ap, &iif->appls, ctrlist)
-               if (ap->id == appl) {
-                       dev_notice(cs->dev,
-                                  "application %u already registered\n", appl);
-                       return;
-               }
-
-       ap = kzalloc(sizeof(*ap), GFP_KERNEL);
-       if (!ap) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       ap->id = appl;
-       ap->rp = *rp;
-
-       list_add(&ap->ctrlist, &iif->appls);
-       dev_info(cs->dev, "application %u registered\n", ap->id);
-}
-
-/*
- * remove CAPI application from channel
- * helper function to keep indentation levels down and stay in 80 columns
- */
-
-static inline void remove_appl_from_channel(struct bc_state *bcs,
-                                           struct gigaset_capi_appl *ap)
-{
-       struct cardstate *cs = bcs->cs;
-       struct gigaset_capi_appl *bcap;
-       unsigned long flags;
-       int prevconnstate;
-
-       spin_lock_irqsave(&bcs->aplock, flags);
-       bcap = bcs->ap;
-       if (bcap == NULL) {
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-               return;
-       }
-
-       /* check first application on channel */
-       if (bcap == ap) {
-               bcs->ap = ap->bcnext;
-               if (bcs->ap != NULL) {
-                       spin_unlock_irqrestore(&bcs->aplock, flags);
-                       return;
-               }
-
-               /* none left, clear channel state */
-               prevconnstate = bcs->apconnstate;
-               bcs->apconnstate = APCONN_NONE;
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-
-               if (prevconnstate == APCONN_ACTIVE) {
-                       dev_notice(cs->dev, "%s: hanging up channel %u\n",
-                                  __func__, bcs->channel);
-                       gigaset_add_event(cs, &bcs->at_state,
-                                         EV_HUP, NULL, 0, NULL);
-                       gigaset_schedule_event(cs);
-               }
-               return;
-       }
-
-       /* check remaining list */
-       do {
-               if (bcap->bcnext == ap) {
-                       bcap->bcnext = bcap->bcnext->bcnext;
-                       spin_unlock_irqrestore(&bcs->aplock, flags);
-                       return;
-               }
-               bcap = bcap->bcnext;
-       } while (bcap != NULL);
-       spin_unlock_irqrestore(&bcs->aplock, flags);
-}
-
-/*
- * release CAPI application
- */
-static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl)
-{
-       struct gigaset_capi_ctr *iif
-               = container_of(ctr, struct gigaset_capi_ctr, ctr);
-       struct cardstate *cs = iif->ctr.driverdata;
-       struct gigaset_capi_appl *ap, *tmp;
-       unsigned ch;
-
-       gig_dbg(DEBUG_CMD, "%s [%u]", __func__, appl);
-
-       list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist)
-               if (ap->id == appl) {
-                       /* remove from any channels */
-                       for (ch = 0; ch < cs->channels; ch++)
-                               remove_appl_from_channel(&cs->bcs[ch], ap);
-
-                       /* remove from registration list */
-                       list_del(&ap->ctrlist);
-                       kfree(ap);
-                       dev_info(cs->dev, "application %u released\n", appl);
-               }
-}
-
-/*
- * =====================================================================
- * outgoing CAPI message handler
- * =====================================================================
- */
-
-/*
- * helper function: emit reply message with given Info value
- */
-static void send_conf(struct gigaset_capi_ctr *iif,
-                     struct gigaset_capi_appl *ap,
-                     struct sk_buff *skb,
-                     u16 info)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-
-       /*
-        * _CONF replies always only have NCCI and Info parameters
-        * so they'll fit into the _REQ message skb
-        */
-       capi_cmsg_answer(&iif->acmsg);
-       iif->acmsg.Info = info;
-       if (capi_cmsg2message(&iif->acmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       __skb_trim(skb, CAPI_STDCONF_LEN);
-       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
-       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-}
-
-/*
- * process FACILITY_REQ message
- */
-static void do_facility_req(struct gigaset_capi_ctr *iif,
-                           struct gigaset_capi_appl *ap,
-                           struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       _cmsg *cmsg = &iif->acmsg;
-       struct sk_buff *cskb;
-       u8 *pparam;
-       unsigned int msgsize = CAPI_FACILITY_CONF_BASELEN;
-       u16 function, info;
-       static u8 confparam[10];        /* max. 9 octets + length byte */
-
-       /* decode message */
-       if (capi_message2cmsg(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-
-       /*
-        * Facility Request Parameter is not decoded by capi_message2cmsg()
-        * encoding depends on Facility Selector
-        */
-       switch (cmsg->FacilitySelector) {
-       case CAPI_FACILITY_DTMF:        /* ToDo */
-               info = CapiFacilityNotSupported;
-               confparam[0] = 2;       /* length */
-               /* DTMF information: Unknown DTMF request */
-               capimsg_setu16(confparam, 1, 2);
-               break;
-
-       case CAPI_FACILITY_V42BIS:      /* not supported */
-               info = CapiFacilityNotSupported;
-               confparam[0] = 2;       /* length */
-               /* V.42 bis information: not available */
-               capimsg_setu16(confparam, 1, 1);
-               break;
-
-       case CAPI_FACILITY_SUPPSVC:
-               /* decode Function parameter */
-               pparam = cmsg->FacilityRequestParameter;
-               if (pparam == NULL || pparam[0] < 2) {
-                       dev_notice(cs->dev, "%s: %s missing\n", "FACILITY_REQ",
-                                  "Facility Request Parameter");
-                       send_conf(iif, ap, skb, CapiIllMessageParmCoding);
-                       return;
-               }
-               function = CAPIMSG_U16(pparam, 1);
-               switch (function) {
-               case CAPI_SUPPSVC_GETSUPPORTED:
-                       info = CapiSuccess;
-                       /* Supplementary Service specific parameter */
-                       confparam[3] = 6;       /* length */
-                       /* Supplementary services info: Success */
-                       capimsg_setu16(confparam, 4, CapiSuccess);
-                       /* Supported Services: none */
-                       capimsg_setu32(confparam, 6, 0);
-                       break;
-               case CAPI_SUPPSVC_LISTEN:
-                       if (pparam[0] < 7 || pparam[3] < 4) {
-                               dev_notice(cs->dev, "%s: %s missing\n",
-                                          "FACILITY_REQ", "Notification Mask");
-                               send_conf(iif, ap, skb,
-                                         CapiIllMessageParmCoding);
-                               return;
-                       }
-                       if (CAPIMSG_U32(pparam, 4) != 0) {
-                               dev_notice(cs->dev,
-                                          "%s: unsupported supplementary service notification mask 0x%x\n",
-                                          "FACILITY_REQ", CAPIMSG_U32(pparam, 4));
-                               info = CapiFacilitySpecificFunctionNotSupported;
-                               confparam[3] = 2;       /* length */
-                               capimsg_setu16(confparam, 4,
-                                              CapiSupplementaryServiceNotSupported);
-                               break;
-                       }
-                       info = CapiSuccess;
-                       confparam[3] = 2;       /* length */
-                       capimsg_setu16(confparam, 4, CapiSuccess);
-                       break;
-
-               /* ToDo: add supported services */
-
-               default:
-                       dev_notice(cs->dev,
-                                  "%s: unsupported supplementary service function 0x%04x\n",
-                                  "FACILITY_REQ", function);
-                       info = CapiFacilitySpecificFunctionNotSupported;
-                       /* Supplementary Service specific parameter */
-                       confparam[3] = 2;       /* length */
-                       /* Supplementary services info: not supported */
-                       capimsg_setu16(confparam, 4,
-                                      CapiSupplementaryServiceNotSupported);
-               }
-
-               /* Facility confirmation parameter */
-               confparam[0] = confparam[3] + 3;        /* total length */
-               /* Function: copy from _REQ message */
-               capimsg_setu16(confparam, 1, function);
-               /* Supplementary Service specific parameter already set above */
-               break;
-
-       case CAPI_FACILITY_WAKEUP:      /* ToDo */
-               info = CapiFacilityNotSupported;
-               confparam[0] = 2;       /* length */
-               /* Number of accepted awake request parameters: 0 */
-               capimsg_setu16(confparam, 1, 0);
-               break;
-
-       default:
-               info = CapiFacilityNotSupported;
-               confparam[0] = 0;       /* empty struct */
-       }
-
-       /* send FACILITY_CONF with given Info and confirmation parameter */
-       dev_kfree_skb_any(skb);
-       capi_cmsg_answer(cmsg);
-       cmsg->Info = info;
-       cmsg->FacilityConfirmationParameter = confparam;
-       msgsize += confparam[0];        /* length */
-       cskb = alloc_skb(msgsize, GFP_ATOMIC);
-       if (!cskb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       if (capi_cmsg2message(cmsg, __skb_put(cskb, msgsize))) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(cskb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-       capi_ctr_handle_message(&iif->ctr, ap->id, cskb);
-}
-
-
-/*
- * process LISTEN_REQ message
- * just store the masks in the application data structure
- */
-static void do_listen_req(struct gigaset_capi_ctr *iif,
-                         struct gigaset_capi_appl *ap,
-                         struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-
-       /* decode message */
-       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
-
-       /* store listening parameters */
-       ap->listenInfoMask = iif->acmsg.InfoMask;
-       ap->listenCIPmask = iif->acmsg.CIPmask;
-       send_conf(iif, ap, skb, CapiSuccess);
-}
-
-/*
- * process ALERT_REQ message
- * nothing to do, Gigaset always alerts anyway
- */
-static void do_alert_req(struct gigaset_capi_ctr *iif,
-                        struct gigaset_capi_appl *ap,
-                        struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-
-       /* decode message */
-       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
-       send_conf(iif, ap, skb, CapiAlertAlreadySent);
-}
-
-/*
- * process CONNECT_REQ message
- * allocate a B channel, prepare dial commands, queue a DIAL event,
- * emit CONNECT_CONF reply
- */
-static void do_connect_req(struct gigaset_capi_ctr *iif,
-                          struct gigaset_capi_appl *ap,
-                          struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       _cmsg *cmsg = &iif->acmsg;
-       struct bc_state *bcs;
-       char **commands;
-       char *s;
-       u8 *pp;
-       unsigned long flags;
-       int i, l, lbc, lhlc;
-       u16 info;
-
-       /* decode message */
-       if (capi_message2cmsg(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-
-       /* get free B channel & construct PLCI */
-       bcs = gigaset_get_free_channel(cs);
-       if (!bcs) {
-               dev_notice(cs->dev, "%s: no B channel available\n",
-                          "CONNECT_REQ");
-               send_conf(iif, ap, skb, CapiNoPlciAvailable);
-               return;
-       }
-       spin_lock_irqsave(&bcs->aplock, flags);
-       if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE)
-               dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
-                        __func__, bcs->ap, bcs->apconnstate);
-       ap->bcnext = NULL;
-       bcs->ap = ap;
-       bcs->apconnstate = APCONN_SETUP;
-       spin_unlock_irqrestore(&bcs->aplock, flags);
-
-       bcs->rx_bufsize = ap->rp.datablklen;
-       dev_kfree_skb(bcs->rx_skb);
-       gigaset_new_rx_skb(bcs);
-       cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8;
-
-       /* build command table */
-       commands = kcalloc(AT_NUM, sizeof(*commands), GFP_KERNEL);
-       if (!commands)
-               goto oom;
-
-       /* encode parameter: Called party number */
-       pp = cmsg->CalledPartyNumber;
-       if (pp == NULL || *pp == 0) {
-               dev_notice(cs->dev, "%s: %s missing\n",
-                          "CONNECT_REQ", "Called party number");
-               info = CapiIllMessageParmCoding;
-               goto error;
-       }
-       l = *pp++;
-       /* check type of number/numbering plan byte */
-       switch (*pp) {
-       case 0x80:      /* unknown type / unknown numbering plan */
-       case 0x81:      /* unknown type / ISDN/Telephony numbering plan */
-               break;
-       default:        /* others: warn about potential misinterpretation */
-               dev_notice(cs->dev, "%s: %s type/plan 0x%02x unsupported\n",
-                          "CONNECT_REQ", "Called party number", *pp);
-       }
-       pp++;
-       l--;
-       /* translate "**" internal call prefix to CTP value */
-       if (l >= 2 && pp[0] == '*' && pp[1] == '*') {
-               s = "^SCTP=0\r";
-               pp += 2;
-               l -= 2;
-       } else {
-               s = "^SCTP=1\r";
-       }
-       commands[AT_TYPE] = kstrdup(s, GFP_KERNEL);
-       if (!commands[AT_TYPE])
-               goto oom;
-       commands[AT_DIAL] = kmalloc(l + 3, GFP_KERNEL);
-       if (!commands[AT_DIAL])
-               goto oom;
-       snprintf(commands[AT_DIAL], l + 3, "D%.*s\r", l, pp);
-
-       /* encode parameter: Calling party number */
-       pp = cmsg->CallingPartyNumber;
-       if (pp != NULL && *pp > 0) {
-               l = *pp++;
-
-               /* check type of number/numbering plan byte */
-               /* ToDo: allow for/handle Ext=1? */
-               switch (*pp) {
-               case 0x00:      /* unknown type / unknown numbering plan */
-               case 0x01:      /* unknown type / ISDN/Telephony num. plan */
-                       break;
-               default:
-                       dev_notice(cs->dev,
-                                  "%s: %s type/plan 0x%02x unsupported\n",
-                                  "CONNECT_REQ", "Calling party number", *pp);
-               }
-               pp++;
-               l--;
-
-               /* check presentation indicator */
-               if (!l) {
-                       dev_notice(cs->dev, "%s: %s IE truncated\n",
-                                  "CONNECT_REQ", "Calling party number");
-                       info = CapiIllMessageParmCoding;
-                       goto error;
-               }
-               switch (*pp & 0xfc) { /* ignore Screening indicator */
-               case 0x80:      /* Presentation allowed */
-                       s = "^SCLIP=1\r";
-                       break;
-               case 0xa0:      /* Presentation restricted */
-                       s = "^SCLIP=0\r";
-                       break;
-               default:
-                       dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
-                                  "CONNECT_REQ",
-                                  "Presentation/Screening indicator",
-                                  *pp);
-                       s = "^SCLIP=1\r";
-               }
-               commands[AT_CLIP] = kstrdup(s, GFP_KERNEL);
-               if (!commands[AT_CLIP])
-                       goto oom;
-               pp++;
-               l--;
-
-               if (l) {
-                       /* number */
-                       commands[AT_MSN] = kmalloc(l + 8, GFP_KERNEL);
-                       if (!commands[AT_MSN])
-                               goto oom;
-                       snprintf(commands[AT_MSN], l + 8, "^SMSN=%*s\r", l, pp);
-               }
-       }
-
-       /* check parameter: CIP Value */
-       if (cmsg->CIPValue >= ARRAY_SIZE(cip2bchlc) ||
-           (cmsg->CIPValue > 0 && cip2bchlc[cmsg->CIPValue].bc == NULL)) {
-               dev_notice(cs->dev, "%s: unknown CIP value %d\n",
-                          "CONNECT_REQ", cmsg->CIPValue);
-               info = CapiCipValueUnknown;
-               goto error;
-       }
-
-       /*
-        * check/encode parameters: BC & HLC
-        * must be encoded together as device doesn't accept HLC separately
-        * explicit parameters override values derived from CIP
-        */
-
-       /* determine lengths */
-       if (cmsg->BC && cmsg->BC[0])            /* BC specified explicitly */
-               lbc = 2 * cmsg->BC[0];
-       else if (cip2bchlc[cmsg->CIPValue].bc)  /* BC derived from CIP */
-               lbc = strlen(cip2bchlc[cmsg->CIPValue].bc);
-       else                                    /* no BC */
-               lbc = 0;
-       if (cmsg->HLC && cmsg->HLC[0])          /* HLC specified explicitly */
-               lhlc = 2 * cmsg->HLC[0];
-       else if (cip2bchlc[cmsg->CIPValue].hlc) /* HLC derived from CIP */
-               lhlc = strlen(cip2bchlc[cmsg->CIPValue].hlc);
-       else                                    /* no HLC */
-               lhlc = 0;
-
-       if (lbc) {
-               /* have BC: allocate and assemble command string */
-               l = lbc + 7;            /* "^SBC=" + value + "\r" + null byte */
-               if (lhlc)
-                       l += lhlc + 7;  /* ";^SHLC=" + value */
-               commands[AT_BC] = kmalloc(l, GFP_KERNEL);
-               if (!commands[AT_BC])
-                       goto oom;
-               strcpy(commands[AT_BC], "^SBC=");
-               if (cmsg->BC && cmsg->BC[0])    /* BC specified explicitly */
-                       decode_ie(cmsg->BC, commands[AT_BC] + 5);
-               else                            /* BC derived from CIP */
-                       strcpy(commands[AT_BC] + 5,
-                              cip2bchlc[cmsg->CIPValue].bc);
-               if (lhlc) {
-                       strcpy(commands[AT_BC] + lbc + 5, ";^SHLC=");
-                       if (cmsg->HLC && cmsg->HLC[0])
-                               /* HLC specified explicitly */
-                               decode_ie(cmsg->HLC,
-                                         commands[AT_BC] + lbc + 12);
-                       else    /* HLC derived from CIP */
-                               strcpy(commands[AT_BC] + lbc + 12,
-                                      cip2bchlc[cmsg->CIPValue].hlc);
-               }
-               strcpy(commands[AT_BC] + l - 2, "\r");
-       } else {
-               /* no BC */
-               if (lhlc) {
-                       dev_notice(cs->dev, "%s: cannot set HLC without BC\n",
-                                  "CONNECT_REQ");
-                       info = CapiIllMessageParmCoding; /* ? */
-                       goto error;
-               }
-       }
-
-       /* check/encode parameter: B Protocol */
-       if (cmsg->BProtocol == CAPI_DEFAULT) {
-               bcs->proto2 = L2_HDLC;
-               dev_warn(cs->dev,
-                        "B2 Protocol X.75 SLP unsupported, using Transparent\n");
-       } else {
-               switch (cmsg->B1protocol) {
-               case 0:
-                       bcs->proto2 = L2_HDLC;
-                       break;
-               case 1:
-                       bcs->proto2 = L2_VOICE;
-                       break;
-               default:
-                       dev_warn(cs->dev,
-                                "B1 Protocol %u unsupported, using Transparent\n",
-                                cmsg->B1protocol);
-                       bcs->proto2 = L2_VOICE;
-               }
-               if (cmsg->B2protocol != 1)
-                       dev_warn(cs->dev,
-                                "B2 Protocol %u unsupported, using Transparent\n",
-                                cmsg->B2protocol);
-               if (cmsg->B3protocol != 0)
-                       dev_warn(cs->dev,
-                                "B3 Protocol %u unsupported, using Transparent\n",
-                                cmsg->B3protocol);
-               ignore_cstruct_param(cs, cmsg->B1configuration,
-                                    "CONNECT_REQ", "B1 Configuration");
-               ignore_cstruct_param(cs, cmsg->B2configuration,
-                                    "CONNECT_REQ", "B2 Configuration");
-               ignore_cstruct_param(cs, cmsg->B3configuration,
-                                    "CONNECT_REQ", "B3 Configuration");
-       }
-       commands[AT_PROTO] = kmalloc(9, GFP_KERNEL);
-       if (!commands[AT_PROTO])
-               goto oom;
-       snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
-
-       /* ToDo: check/encode remaining parameters */
-       ignore_cstruct_param(cs, cmsg->CalledPartySubaddress,
-                            "CONNECT_REQ", "Called pty subaddr");
-       ignore_cstruct_param(cs, cmsg->CallingPartySubaddress,
-                            "CONNECT_REQ", "Calling pty subaddr");
-       ignore_cstruct_param(cs, cmsg->LLC,
-                            "CONNECT_REQ", "LLC");
-       if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
-               ignore_cstruct_param(cs, cmsg->BChannelinformation,
-                                    "CONNECT_REQ", "B Channel Information");
-               ignore_cstruct_param(cs, cmsg->Keypadfacility,
-                                    "CONNECT_REQ", "Keypad Facility");
-               ignore_cstruct_param(cs, cmsg->Useruserdata,
-                                    "CONNECT_REQ", "User-User Data");
-               ignore_cstruct_param(cs, cmsg->Facilitydataarray,
-                                    "CONNECT_REQ", "Facility Data Array");
-       }
-
-       /* encode parameter: B channel to use */
-       commands[AT_ISO] = kmalloc(9, GFP_KERNEL);
-       if (!commands[AT_ISO])
-               goto oom;
-       snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
-                (unsigned) bcs->channel + 1);
-
-       /* queue & schedule EV_DIAL event */
-       if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
-                              bcs->at_state.seq_index, NULL)) {
-               info = CAPI_MSGOSRESOURCEERR;
-               goto error;
-       }
-       gigaset_schedule_event(cs);
-       send_conf(iif, ap, skb, CapiSuccess);
-       return;
-
-oom:
-       dev_err(cs->dev, "%s: out of memory\n", __func__);
-       info = CAPI_MSGOSRESOURCEERR;
-error:
-       if (commands)
-               for (i = 0; i < AT_NUM; i++)
-                       kfree(commands[i]);
-       kfree(commands);
-       gigaset_free_channel(bcs);
-       send_conf(iif, ap, skb, info);
-}
-
-/*
- * process CONNECT_RESP message
- * checks protocol parameters and queues an ACCEPT or HUP event
- */
-static void do_connect_resp(struct gigaset_capi_ctr *iif,
-                           struct gigaset_capi_appl *ap,
-                           struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       _cmsg *cmsg = &iif->acmsg;
-       struct bc_state *bcs;
-       struct gigaset_capi_appl *oap;
-       unsigned long flags;
-       int channel;
-
-       /* decode message */
-       if (capi_message2cmsg(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-       dev_kfree_skb_any(skb);
-
-       /* extract and check channel number from PLCI */
-       channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
-       if (!channel || channel > cs->channels) {
-               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
-                          "CONNECT_RESP", "PLCI", cmsg->adr.adrPLCI);
-               return;
-       }
-       bcs = cs->bcs + channel - 1;
-
-       switch (cmsg->Reject) {
-       case 0:         /* Accept */
-               /* drop all competing applications, keep only this one */
-               spin_lock_irqsave(&bcs->aplock, flags);
-               while (bcs->ap != NULL) {
-                       oap = bcs->ap;
-                       bcs->ap = oap->bcnext;
-                       if (oap != ap) {
-                               spin_unlock_irqrestore(&bcs->aplock, flags);
-                               send_disconnect_ind(bcs, oap,
-                                                   CapiCallGivenToOtherApplication);
-                               spin_lock_irqsave(&bcs->aplock, flags);
-                       }
-               }
-               ap->bcnext = NULL;
-               bcs->ap = ap;
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-
-               bcs->rx_bufsize = ap->rp.datablklen;
-               dev_kfree_skb(bcs->rx_skb);
-               gigaset_new_rx_skb(bcs);
-               bcs->chstate |= CHS_NOTIFY_LL;
-
-               /* check/encode B channel protocol */
-               if (cmsg->BProtocol == CAPI_DEFAULT) {
-                       bcs->proto2 = L2_HDLC;
-                       dev_warn(cs->dev,
-                                "B2 Protocol X.75 SLP unsupported, using Transparent\n");
-               } else {
-                       switch (cmsg->B1protocol) {
-                       case 0:
-                               bcs->proto2 = L2_HDLC;
-                               break;
-                       case 1:
-                               bcs->proto2 = L2_VOICE;
-                               break;
-                       default:
-                               dev_warn(cs->dev,
-                                        "B1 Protocol %u unsupported, using Transparent\n",
-                                        cmsg->B1protocol);
-                               bcs->proto2 = L2_VOICE;
-                       }
-                       if (cmsg->B2protocol != 1)
-                               dev_warn(cs->dev,
-                                        "B2 Protocol %u unsupported, using Transparent\n",
-                                        cmsg->B2protocol);
-                       if (cmsg->B3protocol != 0)
-                               dev_warn(cs->dev,
-                                        "B3 Protocol %u unsupported, using Transparent\n",
-                                        cmsg->B3protocol);
-                       ignore_cstruct_param(cs, cmsg->B1configuration,
-                                            "CONNECT_RESP", "B1 Configuration");
-                       ignore_cstruct_param(cs, cmsg->B2configuration,
-                                            "CONNECT_RESP", "B2 Configuration");
-                       ignore_cstruct_param(cs, cmsg->B3configuration,
-                                            "CONNECT_RESP", "B3 Configuration");
-               }
-
-               /* ToDo: check/encode remaining parameters */
-               ignore_cstruct_param(cs, cmsg->ConnectedNumber,
-                                    "CONNECT_RESP", "Connected Number");
-               ignore_cstruct_param(cs, cmsg->ConnectedSubaddress,
-                                    "CONNECT_RESP", "Connected Subaddress");
-               ignore_cstruct_param(cs, cmsg->LLC,
-                                    "CONNECT_RESP", "LLC");
-               if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
-                       ignore_cstruct_param(cs, cmsg->BChannelinformation,
-                                            "CONNECT_RESP", "BChannel Information");
-                       ignore_cstruct_param(cs, cmsg->Keypadfacility,
-                                            "CONNECT_RESP", "Keypad Facility");
-                       ignore_cstruct_param(cs, cmsg->Useruserdata,
-                                            "CONNECT_RESP", "User-User Data");
-                       ignore_cstruct_param(cs, cmsg->Facilitydataarray,
-                                            "CONNECT_RESP", "Facility Data Array");
-               }
-
-               /* Accept call */
-               if (!gigaset_add_event(cs, &cs->bcs[channel - 1].at_state,
-                                      EV_ACCEPT, NULL, 0, NULL))
-                       return;
-               gigaset_schedule_event(cs);
-               return;
-
-       case 1:                 /* Ignore */
-               /* send DISCONNECT_IND to this application */
-               send_disconnect_ind(bcs, ap, 0);
-
-               /* remove it from the list of listening apps */
-               spin_lock_irqsave(&bcs->aplock, flags);
-               if (bcs->ap == ap) {
-                       bcs->ap = ap->bcnext;
-                       if (bcs->ap == NULL) {
-                               /* last one: stop ev-layer hupD notifications */
-                               bcs->apconnstate = APCONN_NONE;
-                               bcs->chstate &= ~CHS_NOTIFY_LL;
-                       }
-                       spin_unlock_irqrestore(&bcs->aplock, flags);
-                       return;
-               }
-               for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) {
-                       if (oap->bcnext == ap) {
-                               oap->bcnext = oap->bcnext->bcnext;
-                               spin_unlock_irqrestore(&bcs->aplock, flags);
-                               return;
-                       }
-               }
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-               dev_err(cs->dev, "%s: application %u not found\n",
-                       __func__, ap->id);
-               return;
-
-       default:                /* Reject */
-               /* drop all competing applications, keep only this one */
-               spin_lock_irqsave(&bcs->aplock, flags);
-               while (bcs->ap != NULL) {
-                       oap = bcs->ap;
-                       bcs->ap = oap->bcnext;
-                       if (oap != ap) {
-                               spin_unlock_irqrestore(&bcs->aplock, flags);
-                               send_disconnect_ind(bcs, oap,
-                                                   CapiCallGivenToOtherApplication);
-                               spin_lock_irqsave(&bcs->aplock, flags);
-                       }
-               }
-               ap->bcnext = NULL;
-               bcs->ap = ap;
-               spin_unlock_irqrestore(&bcs->aplock, flags);
-
-               /* reject call - will trigger DISCONNECT_IND for this app */
-               dev_info(cs->dev, "%s: Reject=%x\n",
-                        "CONNECT_RESP", cmsg->Reject);
-               if (!gigaset_add_event(cs, &cs->bcs[channel - 1].at_state,
-                                      EV_HUP, NULL, 0, NULL))
-                       return;
-               gigaset_schedule_event(cs);
-               return;
-       }
-}
-
-/*
- * process CONNECT_B3_REQ message
- * build NCCI and emit CONNECT_B3_CONF reply
- */
-static void do_connect_b3_req(struct gigaset_capi_ctr *iif,
-                             struct gigaset_capi_appl *ap,
-                             struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       _cmsg *cmsg = &iif->acmsg;
-       struct bc_state *bcs;
-       int channel;
-
-       /* decode message */
-       if (capi_message2cmsg(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-
-       /* extract and check channel number from PLCI */
-       channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
-       if (!channel || channel > cs->channels) {
-               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
-                          "CONNECT_B3_REQ", "PLCI", cmsg->adr.adrPLCI);
-               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
-               return;
-       }
-       bcs = &cs->bcs[channel - 1];
-
-       /* mark logical connection active */
-       bcs->apconnstate = APCONN_ACTIVE;
-
-       /* build NCCI: always 1 (one B3 connection only) */
-       cmsg->adr.adrNCCI |= 1 << 16;
-
-       /* NCPI parameter: not applicable for B3 Transparent */
-       ignore_cstruct_param(cs, cmsg->NCPI, "CONNECT_B3_REQ", "NCPI");
-       send_conf(iif, ap, skb,
-                 (cmsg->NCPI && cmsg->NCPI[0]) ?
-                 CapiNcpiNotSupportedByProtocol : CapiSuccess);
-}
-
-/*
- * process CONNECT_B3_RESP message
- * Depending on the Reject parameter, either emit CONNECT_B3_ACTIVE_IND
- * or queue EV_HUP and emit DISCONNECT_B3_IND.
- * The emitted message is always shorter than the received one,
- * allowing to reuse the skb.
- */
-static void do_connect_b3_resp(struct gigaset_capi_ctr *iif,
-                              struct gigaset_capi_appl *ap,
-                              struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       _cmsg *cmsg = &iif->acmsg;
-       struct bc_state *bcs;
-       int channel;
-       unsigned int msgsize;
-       u8 command;
-
-       /* decode message */
-       if (capi_message2cmsg(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-
-       /* extract and check channel number and NCCI */
-       channel = (cmsg->adr.adrNCCI >> 8) & 0xff;
-       if (!channel || channel > cs->channels ||
-           ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) {
-               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
-                          "CONNECT_B3_RESP", "NCCI", cmsg->adr.adrNCCI);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       bcs = &cs->bcs[channel - 1];
-
-       if (cmsg->Reject) {
-               /* Reject: clear B3 connect received flag */
-               bcs->apconnstate = APCONN_SETUP;
-
-               /* trigger hangup, causing eventual DISCONNECT_IND */
-               if (!gigaset_add_event(cs, &bcs->at_state,
-                                      EV_HUP, NULL, 0, NULL)) {
-                       dev_kfree_skb_any(skb);
-                       return;
-               }
-               gigaset_schedule_event(cs);
-
-               /* emit DISCONNECT_B3_IND */
-               command = CAPI_DISCONNECT_B3;
-               msgsize = CAPI_DISCONNECT_B3_IND_BASELEN;
-       } else {
-               /*
-                * Accept: emit CONNECT_B3_ACTIVE_IND immediately, as
-                * we only send CONNECT_B3_IND if the B channel is up
-                */
-               command = CAPI_CONNECT_B3_ACTIVE;
-               msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
-       }
-       capi_cmsg_header(cmsg, ap->id, command, CAPI_IND,
-                        ap->nextMessageNumber++, cmsg->adr.adrNCCI);
-       __skb_trim(skb, msgsize);
-       if (capi_cmsg2message(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
-}
-
-/*
- * process DISCONNECT_REQ message
- * schedule EV_HUP and emit DISCONNECT_B3_IND if necessary,
- * emit DISCONNECT_CONF reply
- */
-static void do_disconnect_req(struct gigaset_capi_ctr *iif,
-                             struct gigaset_capi_appl *ap,
-                             struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       _cmsg *cmsg = &iif->acmsg;
-       struct bc_state *bcs;
-       _cmsg *b3cmsg;
-       struct sk_buff *b3skb;
-       int channel;
-
-       /* decode message */
-       if (capi_message2cmsg(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-
-       /* extract and check channel number from PLCI */
-       channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
-       if (!channel || channel > cs->channels) {
-               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
-                          "DISCONNECT_REQ", "PLCI", cmsg->adr.adrPLCI);
-               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
-               return;
-       }
-       bcs = cs->bcs + channel - 1;
-
-       /* ToDo: process parameter: Additional info */
-       if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
-               ignore_cstruct_param(cs, cmsg->BChannelinformation,
-                                    "DISCONNECT_REQ", "B Channel Information");
-               ignore_cstruct_param(cs, cmsg->Keypadfacility,
-                                    "DISCONNECT_REQ", "Keypad Facility");
-               ignore_cstruct_param(cs, cmsg->Useruserdata,
-                                    "DISCONNECT_REQ", "User-User Data");
-               ignore_cstruct_param(cs, cmsg->Facilitydataarray,
-                                    "DISCONNECT_REQ", "Facility Data Array");
-       }
-
-       /* skip if DISCONNECT_IND already sent */
-       if (!bcs->apconnstate)
-               return;
-
-       /* check for active logical connection */
-       if (bcs->apconnstate >= APCONN_ACTIVE) {
-               /* clear it */
-               bcs->apconnstate = APCONN_SETUP;
-
-               /*
-                * emit DISCONNECT_B3_IND with cause 0x3301
-                * use separate cmsg structure, as the content of iif->acmsg
-                * is still needed for creating the _CONF message
-                */
-               b3cmsg = kmalloc(sizeof(*b3cmsg), GFP_KERNEL);
-               if (!b3cmsg) {
-                       dev_err(cs->dev, "%s: out of memory\n", __func__);
-                       send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
-                       return;
-               }
-               capi_cmsg_header(b3cmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
-                                ap->nextMessageNumber++,
-                                cmsg->adr.adrPLCI | (1 << 16));
-               b3cmsg->Reason_B3 = CapiProtocolErrorLayer1;
-               b3skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_KERNEL);
-               if (b3skb == NULL) {
-                       dev_err(cs->dev, "%s: out of memory\n", __func__);
-                       send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
-                       kfree(b3cmsg);
-                       return;
-               }
-               if (capi_cmsg2message(b3cmsg,
-                                     __skb_put(b3skb, CAPI_DISCONNECT_B3_IND_BASELEN))) {
-                       dev_err(cs->dev, "%s: message parser failure\n",
-                               __func__);
-                       kfree(b3cmsg);
-                       dev_kfree_skb_any(b3skb);
-                       return;
-               }
-               dump_cmsg(DEBUG_CMD, __func__, b3cmsg);
-               kfree(b3cmsg);
-               capi_ctr_handle_message(&iif->ctr, ap->id, b3skb);
-       }
-
-       /* trigger hangup, causing eventual DISCONNECT_IND */
-       if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
-               send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
-               return;
-       }
-       gigaset_schedule_event(cs);
-
-       /* emit reply */
-       send_conf(iif, ap, skb, CapiSuccess);
-}
-
-/*
- * process DISCONNECT_B3_REQ message
- * schedule EV_HUP and emit DISCONNECT_B3_CONF reply
- */
-static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
-                                struct gigaset_capi_appl *ap,
-                                struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       _cmsg *cmsg = &iif->acmsg;
-       struct bc_state *bcs;
-       int channel;
-
-       /* decode message */
-       if (capi_message2cmsg(cmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, cmsg);
-
-       /* extract and check channel number and NCCI */
-       channel = (cmsg->adr.adrNCCI >> 8) & 0xff;
-       if (!channel || channel > cs->channels ||
-           ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) {
-               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
-                          "DISCONNECT_B3_REQ", "NCCI", cmsg->adr.adrNCCI);
-               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
-               return;
-       }
-       bcs = &cs->bcs[channel - 1];
-
-       /* reject if logical connection not active */
-       if (bcs->apconnstate < APCONN_ACTIVE) {
-               send_conf(iif, ap, skb,
-                         CapiMessageNotSupportedInCurrentState);
-               return;
-       }
-
-       /* trigger hangup, causing eventual DISCONNECT_B3_IND */
-       if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
-               send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
-               return;
-       }
-       gigaset_schedule_event(cs);
-
-       /* NCPI parameter: not applicable for B3 Transparent */
-       ignore_cstruct_param(cs, cmsg->NCPI,
-                            "DISCONNECT_B3_REQ", "NCPI");
-       send_conf(iif, ap, skb,
-                 (cmsg->NCPI && cmsg->NCPI[0]) ?
-                 CapiNcpiNotSupportedByProtocol : CapiSuccess);
-}
-
-/*
- * process DATA_B3_REQ message
- */
-static void do_data_b3_req(struct gigaset_capi_ctr *iif,
-                          struct gigaset_capi_appl *ap,
-                          struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-       struct bc_state *bcs;
-       int channel = CAPIMSG_PLCI_PART(skb->data);
-       u16 ncci = CAPIMSG_NCCI_PART(skb->data);
-       u16 msglen = CAPIMSG_LEN(skb->data);
-       u16 datalen = CAPIMSG_DATALEN(skb->data);
-       u16 flags = CAPIMSG_FLAGS(skb->data);
-       u16 msgid = CAPIMSG_MSGID(skb->data);
-       u16 handle = CAPIMSG_HANDLE_REQ(skb->data);
-
-       /* frequent message, avoid _cmsg overhead */
-       dump_rawmsg(DEBUG_MCMD, __func__, skb->data);
-
-       /* check parameters */
-       if (channel == 0 || channel > cs->channels || ncci != 1) {
-               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
-                          "DATA_B3_REQ", "NCCI", CAPIMSG_NCCI(skb->data));
-               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
-               return;
-       }
-       bcs = &cs->bcs[channel - 1];
-       if (msglen != CAPI_DATA_B3_REQ_LEN && msglen != CAPI_DATA_B3_REQ_LEN64)
-               dev_notice(cs->dev, "%s: unexpected length %d\n",
-                          "DATA_B3_REQ", msglen);
-       if (msglen + datalen != skb->len)
-               dev_notice(cs->dev, "%s: length mismatch (%d+%d!=%d)\n",
-                          "DATA_B3_REQ", msglen, datalen, skb->len);
-       if (msglen + datalen > skb->len) {
-               /* message too short for announced data length */
-               send_conf(iif, ap, skb, CapiIllMessageParmCoding); /* ? */
-               return;
-       }
-       if (flags & CAPI_FLAGS_RESERVED) {
-               dev_notice(cs->dev, "%s: reserved flags set (%x)\n",
-                          "DATA_B3_REQ", flags);
-               send_conf(iif, ap, skb, CapiIllMessageParmCoding);
-               return;
-       }
-
-       /* reject if logical connection not active */
-       if (bcs->apconnstate < APCONN_ACTIVE) {
-               send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
-               return;
-       }
-
-       /* pull CAPI message into link layer header */
-       skb_reset_mac_header(skb);
-       skb->mac_len = msglen;
-       skb_pull(skb, msglen);
-
-       /* pass to device-specific module */
-       if (cs->ops->send_skb(bcs, skb) < 0) {
-               send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
-               return;
-       }
-
-       /*
-        * DATA_B3_CONF will be sent by gigaset_skb_sent() only if "delivery
-        * confirmation" bit is set; otherwise we have to send it now
-        */
-       if (!(flags & CAPI_FLAGS_DELIVERY_CONFIRMATION))
-               send_data_b3_conf(cs, &iif->ctr, ap->id, msgid, channel, handle,
-                                 flags ? CapiFlagsNotSupportedByProtocol
-                                 : CAPI_NOERROR);
-}
-
-/*
- * process RESET_B3_REQ message
- * just always reply "not supported by current protocol"
- */
-static void do_reset_b3_req(struct gigaset_capi_ctr *iif,
-                           struct gigaset_capi_appl *ap,
-                           struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-
-       /* decode message */
-       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
-       send_conf(iif, ap, skb,
-                 CapiResetProcedureNotSupportedByCurrentProtocol);
-}
-
-/*
- * unsupported CAPI message handler
- */
-static void do_unsupported(struct gigaset_capi_ctr *iif,
-                          struct gigaset_capi_appl *ap,
-                          struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-
-       /* decode message */
-       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
-       send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
-}
-
-/*
- * CAPI message handler: no-op
- */
-static void do_nothing(struct gigaset_capi_ctr *iif,
-                      struct gigaset_capi_appl *ap,
-                      struct sk_buff *skb)
-{
-       struct cardstate *cs = iif->ctr.driverdata;
-
-       /* decode message */
-       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
-               dev_err(cs->dev, "%s: message parser failure\n", __func__);
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
-       dev_kfree_skb_any(skb);
-}
-
-static void do_data_b3_resp(struct gigaset_capi_ctr *iif,
-                           struct gigaset_capi_appl *ap,
-                           struct sk_buff *skb)
-{
-       dump_rawmsg(DEBUG_MCMD, __func__, skb->data);
-       dev_kfree_skb_any(skb);
-}
-
-/* table of outgoing CAPI message handlers with lookup function */
-typedef void (*capi_send_handler_t)(struct gigaset_capi_ctr *,
-                                   struct gigaset_capi_appl *,
-                                   struct sk_buff *);
-
-static struct {
-       u16 cmd;
-       capi_send_handler_t handler;
-} capi_send_handler_table[] = {
-       /* most frequent messages first for faster lookup */
-       { CAPI_DATA_B3_REQ, do_data_b3_req },
-       { CAPI_DATA_B3_RESP, do_data_b3_resp },
-
-       { CAPI_ALERT_REQ, do_alert_req },
-       { CAPI_CONNECT_ACTIVE_RESP, do_nothing },
-       { CAPI_CONNECT_B3_ACTIVE_RESP, do_nothing },
-       { CAPI_CONNECT_B3_REQ, do_connect_b3_req },
-       { CAPI_CONNECT_B3_RESP, do_connect_b3_resp },
-       { CAPI_CONNECT_B3_T90_ACTIVE_RESP, do_nothing },
-       { CAPI_CONNECT_REQ, do_connect_req },
-       { CAPI_CONNECT_RESP, do_connect_resp },
-       { CAPI_DISCONNECT_B3_REQ, do_disconnect_b3_req },
-       { CAPI_DISCONNECT_B3_RESP, do_nothing },
-       { CAPI_DISCONNECT_REQ, do_disconnect_req },
-       { CAPI_DISCONNECT_RESP, do_nothing },
-       { CAPI_FACILITY_REQ, do_facility_req },
-       { CAPI_FACILITY_RESP, do_nothing },
-       { CAPI_LISTEN_REQ, do_listen_req },
-       { CAPI_SELECT_B_PROTOCOL_REQ, do_unsupported },
-       { CAPI_RESET_B3_REQ, do_reset_b3_req },
-       { CAPI_RESET_B3_RESP, do_nothing },
-
-       /*
-        * ToDo: support overlap sending (requires ev-layer state
-        * machine extension to generate additional ATD commands)
-        */
-       { CAPI_INFO_REQ, do_unsupported },
-       { CAPI_INFO_RESP, do_nothing },
-
-       /*
-        * ToDo: what's the proper response for these?
-        */
-       { CAPI_MANUFACTURER_REQ, do_nothing },
-       { CAPI_MANUFACTURER_RESP, do_nothing },
-};
-
-/* look up handler */
-static inline capi_send_handler_t lookup_capi_send_handler(const u16 cmd)
-{
-       size_t i;
-
-       for (i = 0; i < ARRAY_SIZE(capi_send_handler_table); i++)
-               if (capi_send_handler_table[i].cmd == cmd)
-                       return capi_send_handler_table[i].handler;
-       return NULL;
-}
-
-
-/**
- * gigaset_send_message() - accept a CAPI message from an application
- * @ctr:       controller descriptor structure.
- * @skb:       CAPI message.
- *
- * Return value: CAPI error code
- * Note: capidrv (and probably others, too) only uses the return value to
- * decide whether it has to free the skb (only if result != CAPI_NOERROR (0))
- */
-static u16 gigaset_send_message(struct capi_ctr *ctr, struct sk_buff *skb)
-{
-       struct gigaset_capi_ctr *iif
-               = container_of(ctr, struct gigaset_capi_ctr, ctr);
-       struct cardstate *cs = ctr->driverdata;
-       struct gigaset_capi_appl *ap;
-       capi_send_handler_t handler;
-
-       /* can only handle linear sk_buffs */
-       if (skb_linearize(skb) < 0) {
-               dev_warn(cs->dev, "%s: skb_linearize failed\n", __func__);
-               return CAPI_MSGOSRESOURCEERR;
-       }
-
-       /* retrieve application data structure */
-       ap = get_appl(iif, CAPIMSG_APPID(skb->data));
-       if (!ap) {
-               dev_notice(cs->dev, "%s: application %u not registered\n",
-                          __func__, CAPIMSG_APPID(skb->data));
-               return CAPI_ILLAPPNR;
-       }
-
-       /* look up command */
-       handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data));
-       if (!handler) {
-               /* unknown/unsupported message type */
-               if (printk_ratelimit())
-                       dev_notice(cs->dev, "%s: unsupported message %u\n",
-                                  __func__, CAPIMSG_CMD(skb->data));
-               return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
-       }
-
-       /* serialize */
-       if (atomic_add_return(1, &iif->sendqlen) > 1) {
-               /* queue behind other messages */
-               skb_queue_tail(&iif->sendqueue, skb);
-               return CAPI_NOERROR;
-       }
-
-       /* process message */
-       handler(iif, ap, skb);
-
-       /* process other messages arrived in the meantime */
-       while (atomic_sub_return(1, &iif->sendqlen) > 0) {
-               skb = skb_dequeue(&iif->sendqueue);
-               if (!skb) {
-                       /* should never happen */
-                       dev_err(cs->dev, "%s: send queue empty\n", __func__);
-                       continue;
-               }
-               ap = get_appl(iif, CAPIMSG_APPID(skb->data));
-               if (!ap) {
-                       /* could that happen? */
-                       dev_warn(cs->dev, "%s: application %u vanished\n",
-                                __func__, CAPIMSG_APPID(skb->data));
-                       continue;
-               }
-               handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data));
-               if (!handler) {
-                       /* should never happen */
-                       dev_err(cs->dev, "%s: handler %x vanished\n",
-                               __func__, CAPIMSG_CMD(skb->data));
-                       continue;
-               }
-               handler(iif, ap, skb);
-       }
-
-       return CAPI_NOERROR;
-}
-
-/**
- * gigaset_procinfo() - build single line description for controller
- * @ctr:       controller descriptor structure.
- *
- * Return value: pointer to generated string (null terminated)
- */
-static char *gigaset_procinfo(struct capi_ctr *ctr)
-{
-       return ctr->name;       /* ToDo: more? */
-}
-
-static int gigaset_proc_show(struct seq_file *m, void *v)
-{
-       struct capi_ctr *ctr = m->private;
-       struct cardstate *cs = ctr->driverdata;
-       char *s;
-       int i;
-
-       seq_printf(m, "%-16s %s\n", "name", ctr->name);
-       seq_printf(m, "%-16s %s %s\n", "dev",
-                  dev_driver_string(cs->dev), dev_name(cs->dev));
-       seq_printf(m, "%-16s %d\n", "id", cs->myid);
-       if (cs->gotfwver)
-               seq_printf(m, "%-16s %d.%d.%d.%d\n", "firmware",
-                          cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]);
-       seq_printf(m, "%-16s %d\n", "channels", cs->channels);
-       seq_printf(m, "%-16s %s\n", "onechannel", cs->onechannel ? "yes" : "no");
-
-       switch (cs->mode) {
-       case M_UNKNOWN:
-               s = "unknown";
-               break;
-       case M_CONFIG:
-               s = "config";
-               break;
-       case M_UNIMODEM:
-               s = "Unimodem";
-               break;
-       case M_CID:
-               s = "CID";
-               break;
-       default:
-               s = "??";
-       }
-       seq_printf(m, "%-16s %s\n", "mode", s);
-
-       switch (cs->mstate) {
-       case MS_UNINITIALIZED:
-               s = "uninitialized";
-               break;
-       case MS_INIT:
-               s = "init";
-               break;
-       case MS_LOCKED:
-               s = "locked";
-               break;
-       case MS_SHUTDOWN:
-               s = "shutdown";
-               break;
-       case MS_RECOVER:
-               s = "recover";
-               break;
-       case MS_READY:
-               s = "ready";
-               break;
-       default:
-               s = "??";
-       }
-       seq_printf(m, "%-16s %s\n", "mstate", s);
-
-       seq_printf(m, "%-16s %s\n", "running", cs->running ? "yes" : "no");
-       seq_printf(m, "%-16s %s\n", "connected", cs->connected ? "yes" : "no");
-       seq_printf(m, "%-16s %s\n", "isdn_up", cs->isdn_up ? "yes" : "no");
-       seq_printf(m, "%-16s %s\n", "cidmode", cs->cidmode ? "yes" : "no");
-
-       for (i = 0; i < cs->channels; i++) {
-               seq_printf(m, "[%d]%-13s %d\n", i, "corrupted",
-                          cs->bcs[i].corrupted);
-               seq_printf(m, "[%d]%-13s %d\n", i, "trans_down",
-                          cs->bcs[i].trans_down);
-               seq_printf(m, "[%d]%-13s %d\n", i, "trans_up",
-                          cs->bcs[i].trans_up);
-               seq_printf(m, "[%d]%-13s %d\n", i, "chstate",
-                          cs->bcs[i].chstate);
-               switch (cs->bcs[i].proto2) {
-               case L2_BITSYNC:
-                       s = "bitsync";
-                       break;
-               case L2_HDLC:
-                       s = "HDLC";
-                       break;
-               case L2_VOICE:
-                       s = "voice";
-                       break;
-               default:
-                       s = "??";
-               }
-               seq_printf(m, "[%d]%-13s %s\n", i, "proto2", s);
-       }
-       return 0;
-}
-
-/**
- * gigaset_isdn_regdev() - register device to LL
- * @cs:                device descriptor structure.
- * @isdnid:    device name.
- *
- * Return value: 0 on success, error code < 0 on failure
- */
-int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
-{
-       struct gigaset_capi_ctr *iif;
-       int rc;
-
-       iif = kzalloc(sizeof(*iif), GFP_KERNEL);
-       if (!iif) {
-               pr_err("%s: out of memory\n", __func__);
-               return -ENOMEM;
-       }
-
-       /* prepare controller structure */
-       iif->ctr.owner         = THIS_MODULE;
-       iif->ctr.driverdata    = cs;
-       strncpy(iif->ctr.name, isdnid, sizeof(iif->ctr.name) - 1);
-       iif->ctr.driver_name   = "gigaset";
-       iif->ctr.load_firmware = NULL;
-       iif->ctr.reset_ctr     = NULL;
-       iif->ctr.register_appl = gigaset_register_appl;
-       iif->ctr.release_appl  = gigaset_release_appl;
-       iif->ctr.send_message  = gigaset_send_message;
-       iif->ctr.procinfo      = gigaset_procinfo;
-       iif->ctr.proc_show     = gigaset_proc_show,
-       INIT_LIST_HEAD(&iif->appls);
-       skb_queue_head_init(&iif->sendqueue);
-       atomic_set(&iif->sendqlen, 0);
-
-       /* register controller with CAPI */
-       rc = attach_capi_ctr(&iif->ctr);
-       if (rc) {
-               pr_err("attach_capi_ctr failed (%d)\n", rc);
-               kfree(iif);
-               return rc;
-       }
-
-       cs->iif = iif;
-       cs->hw_hdr_len = CAPI_DATA_B3_REQ_LEN;
-       return 0;
-}
-
-/**
- * gigaset_isdn_unregdev() - unregister device from LL
- * @cs:                device descriptor structure.
- */
-void gigaset_isdn_unregdev(struct cardstate *cs)
-{
-       struct gigaset_capi_ctr *iif = cs->iif;
-
-       detach_capi_ctr(&iif->ctr);
-       kfree(iif);
-       cs->iif = NULL;
-}
-
-static struct capi_driver capi_driver_gigaset = {
-       .name           = "gigaset",
-       .revision       = "1.0",
-};
-
-/**
- * gigaset_isdn_regdrv() - register driver to LL
- */
-void gigaset_isdn_regdrv(void)
-{
-       pr_info("Kernel CAPI interface\n");
-       register_capi_driver(&capi_driver_gigaset);
-}
-
-/**
- * gigaset_isdn_unregdrv() - unregister driver from LL
- */
-void gigaset_isdn_unregdrv(void)
-{
-       unregister_capi_driver(&capi_driver_gigaset);
-}
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
deleted file mode 100644 (file)
index 76b5407..0000000
+++ /dev/null
@@ -1,1156 +0,0 @@
-/*
- * Stuff used by all variants of the driver
- *
- * Copyright (c) 2001 by Stefan Eilers,
- *                       Hansjoerg Lipp <hjlipp@web.de>,
- *                       Tilman Schmidt <tilman@imap.cc>.
- *
- * =====================================================================
- *     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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-/* Version Information */
-#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers"
-#define DRIVER_DESC "Driver for Gigaset 307x"
-
-#ifdef CONFIG_GIGASET_DEBUG
-#define DRIVER_DESC_DEBUG " (debug build)"
-#else
-#define DRIVER_DESC_DEBUG ""
-#endif
-
-/* Module parameters */
-int gigaset_debuglevel;
-EXPORT_SYMBOL_GPL(gigaset_debuglevel);
-module_param_named(debug, gigaset_debuglevel, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "debug level");
-
-/* driver state flags */
-#define VALID_MINOR    0x01
-#define VALID_ID       0x02
-
-/**
- * gigaset_dbg_buffer() - dump data in ASCII and hex for debugging
- * @level:     debugging level.
- * @msg:       message prefix.
- * @len:       number of bytes to dump.
- * @buf:       data to dump.
- *
- * If the current debugging level includes one of the bits set in @level,
- * @len bytes starting at @buf are logged to dmesg at KERN_DEBUG prio,
- * prefixed by the text @msg.
- */
-void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
-                       size_t len, const unsigned char *buf)
-{
-       unsigned char outbuf[80];
-       unsigned char c;
-       size_t space = sizeof outbuf - 1;
-       unsigned char *out = outbuf;
-       size_t numin = len;
-
-       while (numin--) {
-               c = *buf++;
-               if (c == '~' || c == '^' || c == '\\') {
-                       if (!space--)
-                               break;
-                       *out++ = '\\';
-               }
-               if (c & 0x80) {
-                       if (!space--)
-                               break;
-                       *out++ = '~';
-                       c ^= 0x80;
-               }
-               if (c < 0x20 || c == 0x7f) {
-                       if (!space--)
-                               break;
-                       *out++ = '^';
-                       c ^= 0x40;
-               }
-               if (!space--)
-                       break;
-               *out++ = c;
-       }
-       *out = 0;
-
-       gig_dbg(level, "%s (%u bytes): %s", msg, (unsigned) len, outbuf);
-}
-EXPORT_SYMBOL_GPL(gigaset_dbg_buffer);
-
-static int setflags(struct cardstate *cs, unsigned flags, unsigned delay)
-{
-       int r;
-
-       r = cs->ops->set_modem_ctrl(cs, cs->control_state, flags);
-       cs->control_state = flags;
-       if (r < 0)
-               return r;
-
-       if (delay) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(delay * HZ / 1000);
-       }
-
-       return 0;
-}
-
-int gigaset_enterconfigmode(struct cardstate *cs)
-{
-       int i, r;
-
-       cs->control_state = TIOCM_RTS;
-
-       r = setflags(cs, TIOCM_DTR, 200);
-       if (r < 0)
-               goto error;
-       r = setflags(cs, 0, 200);
-       if (r < 0)
-               goto error;
-       for (i = 0; i < 5; ++i) {
-               r = setflags(cs, TIOCM_RTS, 100);
-               if (r < 0)
-                       goto error;
-               r = setflags(cs, 0, 100);
-               if (r < 0)
-                       goto error;
-       }
-       r = setflags(cs, TIOCM_RTS | TIOCM_DTR, 800);
-       if (r < 0)
-               goto error;
-
-       return 0;
-
-error:
-       dev_err(cs->dev, "error %d on setuartbits\n", -r);
-       cs->control_state = TIOCM_RTS | TIOCM_DTR;
-       cs->ops->set_modem_ctrl(cs, 0, TIOCM_RTS | TIOCM_DTR);
-
-       return -1;
-}
-
-static int test_timeout(struct at_state_t *at_state)
-{
-       if (!at_state->timer_expires)
-               return 0;
-
-       if (--at_state->timer_expires) {
-               gig_dbg(DEBUG_MCMD, "decreased timer of %p to %lu",
-                       at_state, at_state->timer_expires);
-               return 0;
-       }
-
-       gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
-                         at_state->timer_index, NULL);
-       return 1;
-}
-
-static void timer_tick(struct timer_list *t)
-{
-       struct cardstate *cs = from_timer(cs, t, timer);
-       unsigned long flags;
-       unsigned channel;
-       struct at_state_t *at_state;
-       int timeout = 0;
-
-       spin_lock_irqsave(&cs->lock, flags);
-
-       for (channel = 0; channel < cs->channels; ++channel)
-               if (test_timeout(&cs->bcs[channel].at_state))
-                       timeout = 1;
-
-       if (test_timeout(&cs->at_state))
-               timeout = 1;
-
-       list_for_each_entry(at_state, &cs->temp_at_states, list)
-               if (test_timeout(at_state))
-                       timeout = 1;
-
-       if (cs->running) {
-               mod_timer(&cs->timer, jiffies + msecs_to_jiffies(GIG_TICK));
-               if (timeout) {
-                       gig_dbg(DEBUG_EVENT, "scheduling timeout");
-                       tasklet_schedule(&cs->event_tasklet);
-               }
-       }
-
-       spin_unlock_irqrestore(&cs->lock, flags);
-}
-
-int gigaset_get_channel(struct bc_state *bcs)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&bcs->cs->lock, flags);
-       if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) {
-               gig_dbg(DEBUG_CHANNEL, "could not allocate channel %d",
-                       bcs->channel);
-               spin_unlock_irqrestore(&bcs->cs->lock, flags);
-               return -EBUSY;
-       }
-       ++bcs->use_count;
-       bcs->busy = 1;
-       gig_dbg(DEBUG_CHANNEL, "allocated channel %d", bcs->channel);
-       spin_unlock_irqrestore(&bcs->cs->lock, flags);
-       return 0;
-}
-
-struct bc_state *gigaset_get_free_channel(struct cardstate *cs)
-{
-       unsigned long flags;
-       int i;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (!try_module_get(cs->driver->owner)) {
-               gig_dbg(DEBUG_CHANNEL,
-                       "could not get module for allocating channel");
-               spin_unlock_irqrestore(&cs->lock, flags);
-               return NULL;
-       }
-       for (i = 0; i < cs->channels; ++i)
-               if (!cs->bcs[i].use_count) {
-                       ++cs->bcs[i].use_count;
-                       cs->bcs[i].busy = 1;
-                       spin_unlock_irqrestore(&cs->lock, flags);
-                       gig_dbg(DEBUG_CHANNEL, "allocated channel %d", i);
-                       return cs->bcs + i;
-               }
-       module_put(cs->driver->owner);
-       spin_unlock_irqrestore(&cs->lock, flags);
-       gig_dbg(DEBUG_CHANNEL, "no free channel");
-       return NULL;
-}
-
-void gigaset_free_channel(struct bc_state *bcs)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&bcs->cs->lock, flags);
-       if (!bcs->busy) {
-               gig_dbg(DEBUG_CHANNEL, "could not free channel %d",
-                       bcs->channel);
-               spin_unlock_irqrestore(&bcs->cs->lock, flags);
-               return;
-       }
-       --bcs->use_count;
-       bcs->busy = 0;
-       module_put(bcs->cs->driver->owner);
-       gig_dbg(DEBUG_CHANNEL, "freed channel %d", bcs->channel);
-       spin_unlock_irqrestore(&bcs->cs->lock, flags);
-}
-
-int gigaset_get_channels(struct cardstate *cs)
-{
-       unsigned long flags;
-       int i;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       for (i = 0; i < cs->channels; ++i)
-               if (cs->bcs[i].use_count) {
-                       spin_unlock_irqrestore(&cs->lock, flags);
-                       gig_dbg(DEBUG_CHANNEL,
-                               "could not allocate all channels");
-                       return -EBUSY;
-               }
-       for (i = 0; i < cs->channels; ++i)
-               ++cs->bcs[i].use_count;
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       gig_dbg(DEBUG_CHANNEL, "allocated all channels");
-
-       return 0;
-}
-
-void gigaset_free_channels(struct cardstate *cs)
-{
-       unsigned long flags;
-       int i;
-
-       gig_dbg(DEBUG_CHANNEL, "unblocking all channels");
-       spin_lock_irqsave(&cs->lock, flags);
-       for (i = 0; i < cs->channels; ++i)
-               --cs->bcs[i].use_count;
-       spin_unlock_irqrestore(&cs->lock, flags);
-}
-
-void gigaset_block_channels(struct cardstate *cs)
-{
-       unsigned long flags;
-       int i;
-
-       gig_dbg(DEBUG_CHANNEL, "blocking all channels");
-       spin_lock_irqsave(&cs->lock, flags);
-       for (i = 0; i < cs->channels; ++i)
-               ++cs->bcs[i].use_count;
-       spin_unlock_irqrestore(&cs->lock, flags);
-}
-
-static void clear_events(struct cardstate *cs)
-{
-       struct event_t *ev;
-       unsigned head, tail;
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->ev_lock, flags);
-
-       head = cs->ev_head;
-       tail = cs->ev_tail;
-
-       while (tail != head) {
-               ev = cs->events + head;
-               kfree(ev->ptr);
-               head = (head + 1) % MAX_EVENTS;
-       }
-
-       cs->ev_head = tail;
-
-       spin_unlock_irqrestore(&cs->ev_lock, flags);
-}
-
-/**
- * gigaset_add_event() - add event to device event queue
- * @cs:                device descriptor structure.
- * @at_state:  connection state structure.
- * @type:      event type.
- * @ptr:       pointer parameter for event.
- * @parameter: integer parameter for event.
- * @arg:       pointer parameter for event.
- *
- * Allocate an event queue entry from the device's event queue, and set it up
- * with the parameters given.
- *
- * Return value: added event
- */
-struct event_t *gigaset_add_event(struct cardstate *cs,
-                                 struct at_state_t *at_state, int type,
-                                 void *ptr, int parameter, void *arg)
-{
-       unsigned long flags;
-       unsigned next, tail;
-       struct event_t *event = NULL;
-
-       gig_dbg(DEBUG_EVENT, "queueing event %d", type);
-
-       spin_lock_irqsave(&cs->ev_lock, flags);
-
-       tail = cs->ev_tail;
-       next = (tail + 1) % MAX_EVENTS;
-       if (unlikely(next == cs->ev_head))
-               dev_err(cs->dev, "event queue full\n");
-       else {
-               event = cs->events + tail;
-               event->type = type;
-               event->at_state = at_state;
-               event->cid = -1;
-               event->ptr = ptr;
-               event->arg = arg;
-               event->parameter = parameter;
-               cs->ev_tail = next;
-       }
-
-       spin_unlock_irqrestore(&cs->ev_lock, flags);
-
-       return event;
-}
-EXPORT_SYMBOL_GPL(gigaset_add_event);
-
-static void clear_at_state(struct at_state_t *at_state)
-{
-       int i;
-
-       for (i = 0; i < STR_NUM; ++i) {
-               kfree(at_state->str_var[i]);
-               at_state->str_var[i] = NULL;
-       }
-}
-
-static void dealloc_temp_at_states(struct cardstate *cs)
-{
-       struct at_state_t *cur, *next;
-
-       list_for_each_entry_safe(cur, next, &cs->temp_at_states, list) {
-               list_del(&cur->list);
-               clear_at_state(cur);
-               kfree(cur);
-       }
-}
-
-static void gigaset_freebcs(struct bc_state *bcs)
-{
-       int i;
-
-       gig_dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel);
-       bcs->cs->ops->freebcshw(bcs);
-
-       gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel);
-       clear_at_state(&bcs->at_state);
-       gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel);
-       dev_kfree_skb(bcs->rx_skb);
-       bcs->rx_skb = NULL;
-
-       for (i = 0; i < AT_NUM; ++i) {
-               kfree(bcs->commands[i]);
-               bcs->commands[i] = NULL;
-       }
-}
-
-static struct cardstate *alloc_cs(struct gigaset_driver *drv)
-{
-       unsigned long flags;
-       unsigned i;
-       struct cardstate *cs;
-       struct cardstate *ret = NULL;
-
-       spin_lock_irqsave(&drv->lock, flags);
-       if (drv->blocked)
-               goto exit;
-       for (i = 0; i < drv->minors; ++i) {
-               cs = drv->cs + i;
-               if (!(cs->flags & VALID_MINOR)) {
-                       cs->flags = VALID_MINOR;
-                       ret = cs;
-                       break;
-               }
-       }
-exit:
-       spin_unlock_irqrestore(&drv->lock, flags);
-       return ret;
-}
-
-static void free_cs(struct cardstate *cs)
-{
-       cs->flags = 0;
-}
-
-static void make_valid(struct cardstate *cs, unsigned mask)
-{
-       unsigned long flags;
-       struct gigaset_driver *drv = cs->driver;
-       spin_lock_irqsave(&drv->lock, flags);
-       cs->flags |= mask;
-       spin_unlock_irqrestore(&drv->lock, flags);
-}
-
-static void make_invalid(struct cardstate *cs, unsigned mask)
-{
-       unsigned long flags;
-       struct gigaset_driver *drv = cs->driver;
-       spin_lock_irqsave(&drv->lock, flags);
-       cs->flags &= ~mask;
-       spin_unlock_irqrestore(&drv->lock, flags);
-}
-
-/**
- * gigaset_freecs() - free all associated ressources of a device
- * @cs:                device descriptor structure.
- *
- * Stops all tasklets and timers, unregisters the device from all
- * subsystems it was registered to, deallocates the device structure
- * @cs and all structures referenced from it.
- * Operations on the device should be stopped before calling this.
- */
-void gigaset_freecs(struct cardstate *cs)
-{
-       int i;
-       unsigned long flags;
-
-       if (!cs)
-               return;
-
-       mutex_lock(&cs->mutex);
-
-       spin_lock_irqsave(&cs->lock, flags);
-       cs->running = 0;
-       spin_unlock_irqrestore(&cs->lock, flags); /* event handler and timer are
-                                                    not rescheduled below */
-
-       tasklet_kill(&cs->event_tasklet);
-       del_timer_sync(&cs->timer);
-
-       switch (cs->cs_init) {
-       default:
-               /* clear B channel structures */
-               for (i = 0; i < cs->channels; ++i) {
-                       gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
-                       gigaset_freebcs(cs->bcs + i);
-               }
-
-               /* clear device sysfs */
-               gigaset_free_dev_sysfs(cs);
-
-               gigaset_if_free(cs);
-
-               gig_dbg(DEBUG_INIT, "clearing hw");
-               cs->ops->freecshw(cs);
-
-               /* fall through */
-       case 2: /* error in initcshw */
-               /* Deregister from LL */
-               make_invalid(cs, VALID_ID);
-               gigaset_isdn_unregdev(cs);
-
-               /* fall through */
-       case 1: /* error when registering to LL */
-               gig_dbg(DEBUG_INIT, "clearing at_state");
-               clear_at_state(&cs->at_state);
-               dealloc_temp_at_states(cs);
-               clear_events(cs);
-               tty_port_destroy(&cs->port);
-
-               /* fall through */
-       case 0: /* error in basic setup */
-               gig_dbg(DEBUG_INIT, "freeing inbuf");
-               kfree(cs->inbuf);
-               kfree(cs->bcs);
-       }
-
-       mutex_unlock(&cs->mutex);
-       free_cs(cs);
-}
-EXPORT_SYMBOL_GPL(gigaset_freecs);
-
-void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
-                    struct cardstate *cs, int cid)
-{
-       int i;
-
-       INIT_LIST_HEAD(&at_state->list);
-       at_state->waiting = 0;
-       at_state->getstring = 0;
-       at_state->pending_commands = 0;
-       at_state->timer_expires = 0;
-       at_state->timer_active = 0;
-       at_state->timer_index = 0;
-       at_state->seq_index = 0;
-       at_state->ConState = 0;
-       for (i = 0; i < STR_NUM; ++i)
-               at_state->str_var[i] = NULL;
-       at_state->int_var[VAR_ZDLE] = 0;
-       at_state->int_var[VAR_ZCTP] = -1;
-       at_state->int_var[VAR_ZSAU] = ZSAU_NULL;
-       at_state->cs = cs;
-       at_state->bcs = bcs;
-       at_state->cid = cid;
-       if (!cid)
-               at_state->replystruct = cs->tabnocid;
-       else
-               at_state->replystruct = cs->tabcid;
-}
-
-
-static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct cardstate *cs)
-/* inbuf->read must be allocated before! */
-{
-       inbuf->head = 0;
-       inbuf->tail = 0;
-       inbuf->cs = cs;
-       inbuf->inputstate = INS_command;
-}
-
-/**
- * gigaset_fill_inbuf() - append received data to input buffer
- * @inbuf:     buffer structure.
- * @src:       received data.
- * @numbytes:  number of bytes received.
- *
- * Return value: !=0 if some data was appended
- */
-int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
-                      unsigned numbytes)
-{
-       unsigned n, head, tail, bytesleft;
-
-       gig_dbg(DEBUG_INTR, "received %u bytes", numbytes);
-
-       if (!numbytes)
-               return 0;
-
-       bytesleft = numbytes;
-       tail = inbuf->tail;
-       head = inbuf->head;
-       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
-
-       while (bytesleft) {
-               if (head > tail)
-                       n = head - 1 - tail;
-               else if (head == 0)
-                       n = (RBUFSIZE - 1) - tail;
-               else
-                       n = RBUFSIZE - tail;
-               if (!n) {
-                       dev_err(inbuf->cs->dev,
-                               "buffer overflow (%u bytes lost)\n",
-                               bytesleft);
-                       break;
-               }
-               if (n > bytesleft)
-                       n = bytesleft;
-               memcpy(inbuf->data + tail, src, n);
-               bytesleft -= n;
-               tail = (tail + n) % RBUFSIZE;
-               src += n;
-       }
-       gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
-       inbuf->tail = tail;
-       return numbytes != bytesleft;
-}
-EXPORT_SYMBOL_GPL(gigaset_fill_inbuf);
-
-/* Initialize the b-channel structure */
-static int gigaset_initbcs(struct bc_state *bcs, struct cardstate *cs,
-                          int channel)
-{
-       int i;
-
-       bcs->tx_skb = NULL;
-
-       skb_queue_head_init(&bcs->squeue);
-
-       bcs->corrupted = 0;
-       bcs->trans_down = 0;
-       bcs->trans_up = 0;
-
-       gig_dbg(DEBUG_INIT, "setting up bcs[%d]->at_state", channel);
-       gigaset_at_init(&bcs->at_state, bcs, cs, -1);
-
-#ifdef CONFIG_GIGASET_DEBUG
-       bcs->emptycount = 0;
-#endif
-
-       bcs->rx_bufsize = 0;
-       bcs->rx_skb = NULL;
-       bcs->rx_fcs = PPP_INITFCS;
-       bcs->inputstate = 0;
-       bcs->channel = channel;
-       bcs->cs = cs;
-
-       bcs->chstate = 0;
-       bcs->use_count = 1;
-       bcs->busy = 0;
-       bcs->ignore = cs->ignoreframes;
-
-       for (i = 0; i < AT_NUM; ++i)
-               bcs->commands[i] = NULL;
-
-       spin_lock_init(&bcs->aplock);
-       bcs->ap = NULL;
-       bcs->apconnstate = 0;
-
-       gig_dbg(DEBUG_INIT, "  setting up bcs[%d]->hw", channel);
-       return cs->ops->initbcshw(bcs);
-}
-
-/**
- * gigaset_initcs() - initialize device structure
- * @drv:       hardware driver the device belongs to
- * @channels:  number of B channels supported by device
- * @onechannel:        !=0 if B channel data and AT commands share one
- *                 communication channel (M10x),
- *             ==0 if B channels have separate communication channels (base)
- * @ignoreframes:      number of frames to ignore after setting up B channel
- * @cidmode:   !=0: start in CallID mode
- * @modulename:        name of driver module for LL registration
- *
- * Allocate and initialize cardstate structure for Gigaset driver
- * Calls hardware dependent gigaset_initcshw() function
- * Calls B channel initialization function gigaset_initbcs() for each B channel
- *
- * Return value:
- *     pointer to cardstate structure
- */
-struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
-                                int onechannel, int ignoreframes,
-                                int cidmode, const char *modulename)
-{
-       struct cardstate *cs;
-       unsigned long flags;
-       int i;
-
-       gig_dbg(DEBUG_INIT, "allocating cs");
-       cs = alloc_cs(drv);
-       if (!cs) {
-               pr_err("maximum number of devices exceeded\n");
-               return NULL;
-       }
-
-       cs->cs_init = 0;
-       cs->channels = channels;
-       cs->onechannel = onechannel;
-       cs->ignoreframes = ignoreframes;
-       INIT_LIST_HEAD(&cs->temp_at_states);
-       cs->running = 0;
-       timer_setup(&cs->timer, timer_tick, 0);
-       spin_lock_init(&cs->ev_lock);
-       cs->ev_tail = 0;
-       cs->ev_head = 0;
-
-       tasklet_init(&cs->event_tasklet, gigaset_handle_event,
-                    (unsigned long) cs);
-       tty_port_init(&cs->port);
-       cs->commands_pending = 0;
-       cs->cur_at_seq = 0;
-       cs->gotfwver = -1;
-       cs->dev = NULL;
-       cs->tty_dev = NULL;
-       cs->cidmode = cidmode != 0;
-       cs->tabnocid = gigaset_tab_nocid;
-       cs->tabcid = gigaset_tab_cid;
-
-       init_waitqueue_head(&cs->waitqueue);
-       cs->waiting = 0;
-
-       cs->mode = M_UNKNOWN;
-       cs->mstate = MS_UNINITIALIZED;
-
-       cs->bcs = kmalloc_array(channels, sizeof(struct bc_state), GFP_KERNEL);
-       cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL);
-       if (!cs->bcs || !cs->inbuf) {
-               pr_err("out of memory\n");
-               goto error;
-       }
-       ++cs->cs_init;
-
-       gig_dbg(DEBUG_INIT, "setting up at_state");
-       spin_lock_init(&cs->lock);
-       gigaset_at_init(&cs->at_state, NULL, cs, 0);
-       cs->dle = 0;
-       cs->cbytes = 0;
-
-       gig_dbg(DEBUG_INIT, "setting up inbuf");
-       gigaset_inbuf_init(cs->inbuf, cs);
-
-       cs->connected = 0;
-       cs->isdn_up = 0;
-
-       gig_dbg(DEBUG_INIT, "setting up cmdbuf");
-       cs->cmdbuf = cs->lastcmdbuf = NULL;
-       spin_lock_init(&cs->cmdlock);
-       cs->curlen = 0;
-       cs->cmdbytes = 0;
-
-       gig_dbg(DEBUG_INIT, "setting up iif");
-       if (gigaset_isdn_regdev(cs, modulename) < 0) {
-               pr_err("error registering ISDN device\n");
-               goto error;
-       }
-
-       make_valid(cs, VALID_ID);
-       ++cs->cs_init;
-       gig_dbg(DEBUG_INIT, "setting up hw");
-       if (cs->ops->initcshw(cs) < 0)
-               goto error;
-
-       ++cs->cs_init;
-
-       /* set up character device */
-       gigaset_if_init(cs);
-
-       /* set up device sysfs */
-       gigaset_init_dev_sysfs(cs);
-
-       /* set up channel data structures */
-       for (i = 0; i < channels; ++i) {
-               gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i);
-               if (gigaset_initbcs(cs->bcs + i, cs, i) < 0) {
-                       pr_err("could not allocate channel %d data\n", i);
-                       goto error;
-               }
-       }
-
-       spin_lock_irqsave(&cs->lock, flags);
-       cs->running = 1;
-       spin_unlock_irqrestore(&cs->lock, flags);
-       cs->timer.expires = jiffies + msecs_to_jiffies(GIG_TICK);
-       add_timer(&cs->timer);
-
-       gig_dbg(DEBUG_INIT, "cs initialized");
-       return cs;
-
-error:
-       gig_dbg(DEBUG_INIT, "failed");
-       gigaset_freecs(cs);
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(gigaset_initcs);
-
-/* ReInitialize the b-channel structure on hangup */
-void gigaset_bcs_reinit(struct bc_state *bcs)
-{
-       struct sk_buff *skb;
-       struct cardstate *cs = bcs->cs;
-       unsigned long flags;
-
-       while ((skb = skb_dequeue(&bcs->squeue)) != NULL)
-               dev_kfree_skb(skb);
-
-       spin_lock_irqsave(&cs->lock, flags);
-       clear_at_state(&bcs->at_state);
-       bcs->at_state.ConState = 0;
-       bcs->at_state.timer_active = 0;
-       bcs->at_state.timer_expires = 0;
-       bcs->at_state.cid = -1;                 /* No CID defined */
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       bcs->inputstate = 0;
-
-#ifdef CONFIG_GIGASET_DEBUG
-       bcs->emptycount = 0;
-#endif
-
-       bcs->rx_fcs = PPP_INITFCS;
-       bcs->chstate = 0;
-
-       bcs->ignore = cs->ignoreframes;
-       dev_kfree_skb(bcs->rx_skb);
-       bcs->rx_skb = NULL;
-
-       cs->ops->reinitbcshw(bcs);
-}
-
-static void cleanup_cs(struct cardstate *cs)
-{
-       struct cmdbuf_t *cb, *tcb;
-       int i;
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->lock, flags);
-
-       cs->mode = M_UNKNOWN;
-       cs->mstate = MS_UNINITIALIZED;
-
-       clear_at_state(&cs->at_state);
-       dealloc_temp_at_states(cs);
-       gigaset_at_init(&cs->at_state, NULL, cs, 0);
-
-       cs->inbuf->inputstate = INS_command;
-       cs->inbuf->head = 0;
-       cs->inbuf->tail = 0;
-
-       cb = cs->cmdbuf;
-       while (cb) {
-               tcb = cb;
-               cb = cb->next;
-               kfree(tcb);
-       }
-       cs->cmdbuf = cs->lastcmdbuf = NULL;
-       cs->curlen = 0;
-       cs->cmdbytes = 0;
-       cs->gotfwver = -1;
-       cs->dle = 0;
-       cs->cur_at_seq = 0;
-       cs->commands_pending = 0;
-       cs->cbytes = 0;
-
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       for (i = 0; i < cs->channels; ++i) {
-               gigaset_freebcs(cs->bcs + i);
-               if (gigaset_initbcs(cs->bcs + i, cs, i) < 0)
-                       pr_err("could not allocate channel %d data\n", i);
-       }
-
-       if (cs->waiting) {
-               cs->cmd_result = -ENODEV;
-               cs->waiting = 0;
-               wake_up_interruptible(&cs->waitqueue);
-       }
-}
-
-
-/**
- * gigaset_start() - start device operations
- * @cs:                device descriptor structure.
- *
- * Prepares the device for use by setting up communication parameters,
- * scheduling an EV_START event to initiate device initialization, and
- * waiting for completion of the initialization.
- *
- * Return value:
- *     0 on success, error code < 0 on failure
- */
-int gigaset_start(struct cardstate *cs)
-{
-       unsigned long flags;
-
-       if (mutex_lock_interruptible(&cs->mutex))
-               return -EBUSY;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       cs->connected = 1;
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       if (cs->mstate != MS_LOCKED) {
-               cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
-               cs->ops->baud_rate(cs, B115200);
-               cs->ops->set_line_ctrl(cs, CS8);
-               cs->control_state = TIOCM_DTR | TIOCM_RTS;
-       }
-
-       cs->waiting = 1;
-
-       if (!gigaset_add_event(cs, &cs->at_state, EV_START, NULL, 0, NULL)) {
-               cs->waiting = 0;
-               goto error;
-       }
-       gigaset_schedule_event(cs);
-
-       wait_event(cs->waitqueue, !cs->waiting);
-
-       mutex_unlock(&cs->mutex);
-       return 0;
-
-error:
-       mutex_unlock(&cs->mutex);
-       return -ENOMEM;
-}
-EXPORT_SYMBOL_GPL(gigaset_start);
-
-/**
- * gigaset_shutdown() - shut down device operations
- * @cs:                device descriptor structure.
- *
- * Deactivates the device by scheduling an EV_SHUTDOWN event and
- * waiting for completion of the shutdown.
- *
- * Return value:
- *     0 - success, -ENODEV - error (no device associated)
- */
-int gigaset_shutdown(struct cardstate *cs)
-{
-       mutex_lock(&cs->mutex);
-
-       if (!(cs->flags & VALID_MINOR)) {
-               mutex_unlock(&cs->mutex);
-               return -ENODEV;
-       }
-
-       cs->waiting = 1;
-
-       if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL))
-               goto exit;
-       gigaset_schedule_event(cs);
-
-       wait_event(cs->waitqueue, !cs->waiting);
-
-       cleanup_cs(cs);
-
-exit:
-       mutex_unlock(&cs->mutex);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(gigaset_shutdown);
-
-/**
- * gigaset_stop() - stop device operations
- * @cs:                device descriptor structure.
- *
- * Stops operations on the device by scheduling an EV_STOP event and
- * waiting for completion of the shutdown.
- */
-void gigaset_stop(struct cardstate *cs)
-{
-       mutex_lock(&cs->mutex);
-
-       cs->waiting = 1;
-
-       if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL))
-               goto exit;
-       gigaset_schedule_event(cs);
-
-       wait_event(cs->waitqueue, !cs->waiting);
-
-       cleanup_cs(cs);
-
-exit:
-       mutex_unlock(&cs->mutex);
-}
-EXPORT_SYMBOL_GPL(gigaset_stop);
-
-static LIST_HEAD(drivers);
-static DEFINE_SPINLOCK(driver_lock);
-
-struct cardstate *gigaset_get_cs_by_id(int id)
-{
-       unsigned long flags;
-       struct cardstate *ret = NULL;
-       struct cardstate *cs;
-       struct gigaset_driver *drv;
-       unsigned i;
-
-       spin_lock_irqsave(&driver_lock, flags);
-       list_for_each_entry(drv, &drivers, list) {
-               spin_lock(&drv->lock);
-               for (i = 0; i < drv->minors; ++i) {
-                       cs = drv->cs + i;
-                       if ((cs->flags & VALID_ID) && cs->myid == id) {
-                               ret = cs;
-                               break;
-                       }
-               }
-               spin_unlock(&drv->lock);
-               if (ret)
-                       break;
-       }
-       spin_unlock_irqrestore(&driver_lock, flags);
-       return ret;
-}
-
-static struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
-{
-       unsigned long flags;
-       struct cardstate *ret = NULL;
-       struct gigaset_driver *drv;
-       unsigned index;
-
-       spin_lock_irqsave(&driver_lock, flags);
-       list_for_each_entry(drv, &drivers, list) {
-               if (minor < drv->minor || minor >= drv->minor + drv->minors)
-                       continue;
-               index = minor - drv->minor;
-               spin_lock(&drv->lock);
-               if (drv->cs[index].flags & VALID_MINOR)
-                       ret = drv->cs + index;
-               spin_unlock(&drv->lock);
-               if (ret)
-                       break;
-       }
-       spin_unlock_irqrestore(&driver_lock, flags);
-       return ret;
-}
-
-struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty)
-{
-       return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start);
-}
-
-/**
- * gigaset_freedriver() - free all associated ressources of a driver
- * @drv:       driver descriptor structure.
- *
- * Unregisters the driver from the system and deallocates the driver
- * structure @drv and all structures referenced from it.
- * All devices should be shut down before calling this.
- */
-void gigaset_freedriver(struct gigaset_driver *drv)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&driver_lock, flags);
-       list_del(&drv->list);
-       spin_unlock_irqrestore(&driver_lock, flags);
-
-       gigaset_if_freedriver(drv);
-
-       kfree(drv->cs);
-       kfree(drv);
-}
-EXPORT_SYMBOL_GPL(gigaset_freedriver);
-
-/**
- * gigaset_initdriver() - initialize driver structure
- * @minor:     First minor number
- * @minors:    Number of minors this driver can handle
- * @procname:  Name of the driver
- * @devname:   Name of the device files (prefix without minor number)
- *
- * Allocate and initialize gigaset_driver structure. Initialize interface.
- *
- * Return value:
- *     Pointer to the gigaset_driver structure on success, NULL on failure.
- */
-struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
-                                         const char *procname,
-                                         const char *devname,
-                                         const struct gigaset_ops *ops,
-                                         struct module *owner)
-{
-       struct gigaset_driver *drv;
-       unsigned long flags;
-       unsigned i;
-
-       drv = kmalloc(sizeof *drv, GFP_KERNEL);
-       if (!drv)
-               return NULL;
-
-       drv->have_tty = 0;
-       drv->minor = minor;
-       drv->minors = minors;
-       spin_lock_init(&drv->lock);
-       drv->blocked = 0;
-       drv->ops = ops;
-       drv->owner = owner;
-       INIT_LIST_HEAD(&drv->list);
-
-       drv->cs = kmalloc_array(minors, sizeof(*drv->cs), GFP_KERNEL);
-       if (!drv->cs)
-               goto error;
-
-       for (i = 0; i < minors; ++i) {
-               drv->cs[i].flags = 0;
-               drv->cs[i].driver = drv;
-               drv->cs[i].ops = drv->ops;
-               drv->cs[i].minor_index = i;
-               mutex_init(&drv->cs[i].mutex);
-       }
-
-       gigaset_if_initdriver(drv, procname, devname);
-
-       spin_lock_irqsave(&driver_lock, flags);
-       list_add(&drv->list, &drivers);
-       spin_unlock_irqrestore(&driver_lock, flags);
-
-       return drv;
-
-error:
-       kfree(drv);
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(gigaset_initdriver);
-
-/**
- * gigaset_blockdriver() - block driver
- * @drv:       driver descriptor structure.
- *
- * Prevents the driver from attaching new devices, in preparation for
- * deregistration.
- */
-void gigaset_blockdriver(struct gigaset_driver *drv)
-{
-       drv->blocked = 1;
-}
-EXPORT_SYMBOL_GPL(gigaset_blockdriver);
-
-static int __init gigaset_init_module(void)
-{
-       /* in accordance with the principle of least astonishment,
-        * setting the 'debug' parameter to 1 activates a sensible
-        * set of default debug levels
-        */
-       if (gigaset_debuglevel == 1)
-               gigaset_debuglevel = DEBUG_DEFAULT;
-
-       pr_info(DRIVER_DESC DRIVER_DESC_DEBUG "\n");
-       gigaset_isdn_regdrv();
-       return 0;
-}
-
-static void __exit gigaset_exit_module(void)
-{
-       gigaset_isdn_unregdrv();
-}
-
-module_init(gigaset_init_module);
-module_exit(gigaset_exit_module);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/isdn/gigaset/dummyll.c b/drivers/isdn/gigaset/dummyll.c
deleted file mode 100644 (file)
index 570c2d5..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Dummy LL interface for the Gigaset driver
- *
- * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
- *
- * =====================================================================
- *     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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include <linux/export.h>
-#include "gigaset.h"
-
-void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
-{
-}
-EXPORT_SYMBOL_GPL(gigaset_skb_sent);
-
-void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
-{
-}
-EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
-
-void gigaset_isdn_rcv_err(struct bc_state *bcs)
-{
-}
-EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
-
-int gigaset_isdn_icall(struct at_state_t *at_state)
-{
-       return ICALL_IGNORE;
-}
-
-void gigaset_isdn_connD(struct bc_state *bcs)
-{
-}
-
-void gigaset_isdn_hupD(struct bc_state *bcs)
-{
-}
-
-void gigaset_isdn_connB(struct bc_state *bcs)
-{
-}
-
-void gigaset_isdn_hupB(struct bc_state *bcs)
-{
-}
-
-void gigaset_isdn_start(struct cardstate *cs)
-{
-}
-
-void gigaset_isdn_stop(struct cardstate *cs)
-{
-}
-
-int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
-{
-       return 0;
-}
-
-void gigaset_isdn_unregdev(struct cardstate *cs)
-{
-}
-
-void gigaset_isdn_regdrv(void)
-{
-       pr_info("no ISDN subsystem interface\n");
-}
-
-void gigaset_isdn_unregdrv(void)
-{
-}
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
deleted file mode 100644 (file)
index 182826e..0000000
+++ /dev/null
@@ -1,1913 +0,0 @@
-/*
- * Stuff used by all variants of the driver
- *
- * Copyright (c) 2001 by Stefan Eilers,
- *                       Hansjoerg Lipp <hjlipp@web.de>,
- *                       Tilman Schmidt <tilman@imap.cc>.
- *
- * =====================================================================
- *     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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include <linux/export.h>
-#include "gigaset.h"
-
-/* ========================================================== */
-/* bit masks for pending commands */
-#define PC_DIAL                0x001
-#define PC_HUP         0x002
-#define PC_INIT                0x004
-#define PC_DLE0                0x008
-#define PC_DLE1                0x010
-#define PC_SHUTDOWN    0x020
-#define PC_ACCEPT      0x040
-#define PC_CID         0x080
-#define PC_NOCID       0x100
-#define PC_CIDMODE     0x200
-#define PC_UMMODE      0x400
-
-/* types of modem responses */
-#define RT_NOTHING     0
-#define RT_ZSAU                1
-#define RT_RING                2
-#define RT_NUMBER      3
-#define RT_STRING      4
-#define RT_ZCAU                6
-
-/* Possible ASCII responses */
-#define RSP_OK         0
-#define RSP_ERROR      1
-#define RSP_ZGCI       3
-#define RSP_RING       4
-#define RSP_ZVLS       5
-#define RSP_ZCAU       6
-
-/* responses with values to store in at_state */
-/* - numeric */
-#define RSP_VAR                100
-#define RSP_ZSAU       (RSP_VAR + VAR_ZSAU)
-#define RSP_ZDLE       (RSP_VAR + VAR_ZDLE)
-#define RSP_ZCTP       (RSP_VAR + VAR_ZCTP)
-/* - string */
-#define RSP_STR                (RSP_VAR + VAR_NUM)
-#define RSP_NMBR       (RSP_STR + STR_NMBR)
-#define RSP_ZCPN       (RSP_STR + STR_ZCPN)
-#define RSP_ZCON       (RSP_STR + STR_ZCON)
-#define RSP_ZBC                (RSP_STR + STR_ZBC)
-#define RSP_ZHLC       (RSP_STR + STR_ZHLC)
-
-#define RSP_WRONG_CID  -2      /* unknown cid in cmd */
-#define RSP_INVAL      -6      /* invalid response   */
-#define RSP_NODEV      -9      /* device not connected */
-
-#define RSP_NONE       -19
-#define RSP_STRING     -20
-#define RSP_NULL       -21
-#define RSP_INIT       -27
-#define RSP_ANY                -26
-#define RSP_LAST       -28
-
-/* actions for process_response */
-#define ACT_NOTHING            0
-#define ACT_SETDLE1            1
-#define ACT_SETDLE0            2
-#define ACT_FAILINIT           3
-#define ACT_HUPMODEM           4
-#define ACT_CONFIGMODE         5
-#define ACT_INIT               6
-#define ACT_DLE0               7
-#define ACT_DLE1               8
-#define ACT_FAILDLE0           9
-#define ACT_FAILDLE1           10
-#define ACT_RING               11
-#define ACT_CID                        12
-#define ACT_FAILCID            13
-#define ACT_SDOWN              14
-#define ACT_FAILSDOWN          15
-#define ACT_DEBUG              16
-#define ACT_WARN               17
-#define ACT_DIALING            18
-#define ACT_ABORTDIAL          19
-#define ACT_DISCONNECT         20
-#define ACT_CONNECT            21
-#define ACT_REMOTEREJECT       22
-#define ACT_CONNTIMEOUT                23
-#define ACT_REMOTEHUP          24
-#define ACT_ABORTHUP           25
-#define ACT_ICALL              26
-#define ACT_ACCEPTED           27
-#define ACT_ABORTACCEPT                28
-#define ACT_TIMEOUT            29
-#define ACT_GETSTRING          30
-#define ACT_SETVER             31
-#define ACT_FAILVER            32
-#define ACT_GOTVER             33
-#define ACT_TEST               34
-#define ACT_ERROR              35
-#define ACT_ABORTCID           36
-#define ACT_ZCAU               37
-#define ACT_NOTIFY_BC_DOWN     38
-#define ACT_NOTIFY_BC_UP       39
-#define ACT_DIAL               40
-#define ACT_ACCEPT             41
-#define ACT_HUP                        43
-#define ACT_IF_LOCK            44
-#define ACT_START              45
-#define ACT_STOP               46
-#define ACT_FAKEDLE0           47
-#define ACT_FAKEHUP            48
-#define ACT_FAKESDOWN          49
-#define ACT_SHUTDOWN           50
-#define ACT_PROC_CIDMODE       51
-#define ACT_UMODESET           52
-#define ACT_FAILUMODE          53
-#define ACT_CMODESET           54
-#define ACT_FAILCMODE          55
-#define ACT_IF_VER             56
-#define ACT_CMD                        100
-
-/* at command sequences */
-#define SEQ_NONE       0
-#define SEQ_INIT       100
-#define SEQ_DLE0       200
-#define SEQ_DLE1       250
-#define SEQ_CID                300
-#define SEQ_NOCID      350
-#define SEQ_HUP                400
-#define SEQ_DIAL       600
-#define SEQ_ACCEPT     720
-#define SEQ_SHUTDOWN   500
-#define SEQ_CIDMODE    10
-#define SEQ_UMMODE     11
-
-
-/* 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid),
- * 400: hup, 500: reset, 600: dial, 700: ring */
-struct reply_t gigaset_tab_nocid[] =
-{
-/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
- * action, command */
-
-/* initialize device, set cid mode if possible */
-       {RSP_INIT,       -1,  -1, SEQ_INIT,     100,  1, {ACT_TIMEOUT} },
-
-       {EV_TIMEOUT,    100, 100, -1,           101,  3, {0},   "Z\r"},
-       {RSP_OK,        101, 103, -1,           120,  5, {ACT_GETSTRING},
-                                                               "+GMR\r"},
-
-       {EV_TIMEOUT,    101, 101, -1,           102,  5, {0},   "Z\r"},
-       {RSP_ERROR,     101, 101, -1,           102,  5, {0},   "Z\r"},
-
-       {EV_TIMEOUT,    102, 102, -1,           108,  5, {ACT_SETDLE1},
-                                                               "^SDLE=0\r"},
-       {RSP_OK,        108, 108, -1,           104, -1},
-       {RSP_ZDLE,      104, 104,  0,           103,  5, {0},   "Z\r"},
-       {EV_TIMEOUT,    104, 104, -1,             0,  0, {ACT_FAILINIT} },
-       {RSP_ERROR,     108, 108, -1,             0,  0, {ACT_FAILINIT} },
-
-       {EV_TIMEOUT,    108, 108, -1,           105,  2, {ACT_SETDLE0,
-                                                         ACT_HUPMODEM,
-                                                         ACT_TIMEOUT} },
-       {EV_TIMEOUT,    105, 105, -1,           103,  5, {0},   "Z\r"},
-
-       {RSP_ERROR,     102, 102, -1,           107,  5, {0},   "^GETPRE\r"},
-       {RSP_OK,        107, 107, -1,             0,  0, {ACT_CONFIGMODE} },
-       {RSP_ERROR,     107, 107, -1,             0,  0, {ACT_FAILINIT} },
-       {EV_TIMEOUT,    107, 107, -1,             0,  0, {ACT_FAILINIT} },
-
-       {RSP_ERROR,     103, 103, -1,             0,  0, {ACT_FAILINIT} },
-       {EV_TIMEOUT,    103, 103, -1,             0,  0, {ACT_FAILINIT} },
-
-       {RSP_STRING,    120, 120, -1,           121, -1, {ACT_SETVER} },
-
-       {EV_TIMEOUT,    120, 121, -1,             0,  0, {ACT_FAILVER,
-                                                         ACT_INIT} },
-       {RSP_ERROR,     120, 121, -1,             0,  0, {ACT_FAILVER,
-                                                         ACT_INIT} },
-       {RSP_OK,        121, 121, -1,             0,  0, {ACT_GOTVER,
-                                                         ACT_INIT} },
-       {RSP_NONE,      121, 121, -1,           120,  0, {ACT_GETSTRING} },
-
-/* leave dle mode */
-       {RSP_INIT,        0,   0, SEQ_DLE0,     201,  5, {0},   "^SDLE=0\r"},
-       {RSP_OK,        201, 201, -1,           202, -1},
-       {RSP_ZDLE,      202, 202,  0,             0,  0, {ACT_DLE0} },
-       {RSP_NODEV,     200, 249, -1,             0,  0, {ACT_FAKEDLE0} },
-       {RSP_ERROR,     200, 249, -1,             0,  0, {ACT_FAILDLE0} },
-       {EV_TIMEOUT,    200, 249, -1,             0,  0, {ACT_FAILDLE0} },
-
-/* enter dle mode */
-       {RSP_INIT,        0,   0, SEQ_DLE1,     251,  5, {0},   "^SDLE=1\r"},
-       {RSP_OK,        251, 251, -1,           252, -1},
-       {RSP_ZDLE,      252, 252,  1,             0,  0, {ACT_DLE1} },
-       {RSP_ERROR,     250, 299, -1,             0,  0, {ACT_FAILDLE1} },
-       {EV_TIMEOUT,    250, 299, -1,             0,  0, {ACT_FAILDLE1} },
-
-/* incoming call */
-       {RSP_RING,       -1,  -1, -1,            -1, -1, {ACT_RING} },
-
-/* get cid */
-       {RSP_INIT,        0,   0, SEQ_CID,      301,  5, {0},   "^SGCI?\r"},
-       {RSP_OK,        301, 301, -1,           302, -1},
-       {RSP_ZGCI,      302, 302, -1,             0,  0, {ACT_CID} },
-       {RSP_ERROR,     301, 349, -1,             0,  0, {ACT_FAILCID} },
-       {EV_TIMEOUT,    301, 349, -1,             0,  0, {ACT_FAILCID} },
-
-/* enter cid mode */
-       {RSP_INIT,        0,   0, SEQ_CIDMODE,  150,  5, {0},   "^SGCI=1\r"},
-       {RSP_OK,        150, 150, -1,             0,  0, {ACT_CMODESET} },
-       {RSP_ERROR,     150, 150, -1,             0,  0, {ACT_FAILCMODE} },
-       {EV_TIMEOUT,    150, 150, -1,             0,  0, {ACT_FAILCMODE} },
-
-/* leave cid mode */
-       {RSP_INIT,        0,   0, SEQ_UMMODE,   160,  5, {0},   "Z\r"},
-       {RSP_OK,        160, 160, -1,             0,  0, {ACT_UMODESET} },
-       {RSP_ERROR,     160, 160, -1,             0,  0, {ACT_FAILUMODE} },
-       {EV_TIMEOUT,    160, 160, -1,             0,  0, {ACT_FAILUMODE} },
-
-/* abort getting cid */
-       {RSP_INIT,        0,   0, SEQ_NOCID,      0,  0, {ACT_ABORTCID} },
-
-/* reset */
-       {RSP_INIT,        0,   0, SEQ_SHUTDOWN, 504,  5, {0},   "Z\r"},
-       {RSP_OK,        504, 504, -1,             0,  0, {ACT_SDOWN} },
-       {RSP_ERROR,     501, 599, -1,             0,  0, {ACT_FAILSDOWN} },
-       {EV_TIMEOUT,    501, 599, -1,             0,  0, {ACT_FAILSDOWN} },
-       {RSP_NODEV,     501, 599, -1,             0,  0, {ACT_FAKESDOWN} },
-
-       {EV_PROC_CIDMODE, -1, -1, -1,            -1, -1, {ACT_PROC_CIDMODE} },
-       {EV_IF_LOCK,     -1,  -1, -1,            -1, -1, {ACT_IF_LOCK} },
-       {EV_IF_VER,      -1,  -1, -1,            -1, -1, {ACT_IF_VER} },
-       {EV_START,       -1,  -1, -1,            -1, -1, {ACT_START} },
-       {EV_STOP,        -1,  -1, -1,            -1, -1, {ACT_STOP} },
-       {EV_SHUTDOWN,    -1,  -1, -1,            -1, -1, {ACT_SHUTDOWN} },
-
-/* misc. */
-       {RSP_ERROR,      -1,  -1, -1,            -1, -1, {ACT_ERROR} },
-       {RSP_ZCAU,       -1,  -1, -1,            -1, -1, {ACT_ZCAU} },
-       {RSP_NONE,       -1,  -1, -1,            -1, -1, {ACT_DEBUG} },
-       {RSP_ANY,        -1,  -1, -1,            -1, -1, {ACT_WARN} },
-       {RSP_LAST}
-};
-
-/* 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring,
- * 400: hup, 750: accepted icall */
-struct reply_t gigaset_tab_cid[] =
-{
-/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
- * action, command */
-
-/* dial */
-       {EV_DIAL,        -1,  -1, -1,            -1, -1, {ACT_DIAL} },
-       {RSP_INIT,        0,   0, SEQ_DIAL,     601,  5, {ACT_CMD + AT_BC} },
-       {RSP_OK,        601, 601, -1,           603,  5, {ACT_CMD + AT_PROTO} },
-       {RSP_OK,        603, 603, -1,           604,  5, {ACT_CMD + AT_TYPE} },
-       {RSP_OK,        604, 604, -1,           605,  5, {ACT_CMD + AT_MSN} },
-       {RSP_NULL,      605, 605, -1,           606,  5, {ACT_CMD + AT_CLIP} },
-       {RSP_OK,        605, 605, -1,           606,  5, {ACT_CMD + AT_CLIP} },
-       {RSP_NULL,      606, 606, -1,           607,  5, {ACT_CMD + AT_ISO} },
-       {RSP_OK,        606, 606, -1,           607,  5, {ACT_CMD + AT_ISO} },
-       {RSP_OK,        607, 607, -1,           608,  5, {0},   "+VLS=17\r"},
-       {RSP_OK,        608, 608, -1,           609, -1},
-       {RSP_ZSAU,      609, 609, ZSAU_PROCEEDING, 610, 5, {ACT_CMD + AT_DIAL} },
-       {RSP_OK,        610, 610, -1,           650,  0, {ACT_DIALING} },
-
-       {RSP_ERROR,     601, 610, -1,             0,  0, {ACT_ABORTDIAL} },
-       {EV_TIMEOUT,    601, 610, -1,             0,  0, {ACT_ABORTDIAL} },
-
-/* optional dialing responses */
-       {EV_BC_OPEN,    650, 650, -1,           651, -1},
-       {RSP_ZVLS,      609, 651, 17,            -1, -1, {ACT_DEBUG} },
-       {RSP_ZCTP,      610, 651, -1,            -1, -1, {ACT_DEBUG} },
-       {RSP_ZCPN,      610, 651, -1,            -1, -1, {ACT_DEBUG} },
-       {RSP_ZSAU,      650, 651, ZSAU_CALL_DELIVERED, -1, -1, {ACT_DEBUG} },
-
-/* connect */
-       {RSP_ZSAU,      650, 650, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT} },
-       {RSP_ZSAU,      651, 651, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT,
-                                                         ACT_NOTIFY_BC_UP} },
-       {RSP_ZSAU,      750, 750, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT} },
-       {RSP_ZSAU,      751, 751, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT,
-                                                         ACT_NOTIFY_BC_UP} },
-       {EV_BC_OPEN,    800, 800, -1,           800, -1, {ACT_NOTIFY_BC_UP} },
-
-/* remote hangup */
-       {RSP_ZSAU,      650, 651, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT} },
-       {RSP_ZSAU,      750, 751, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },
-       {RSP_ZSAU,      800, 800, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },
-
-/* hangup */
-       {EV_HUP,         -1,  -1, -1,            -1, -1, {ACT_HUP} },
-       {RSP_INIT,       -1,  -1, SEQ_HUP,      401,  5, {0},   "+VLS=0\r"},
-       {RSP_OK,        401, 401, -1,           402,  5},
-       {RSP_ZVLS,      402, 402,  0,           403,  5},
-       {RSP_ZSAU,      403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} },
-       {RSP_ZSAU,      403, 403, ZSAU_NULL,      0,  0, {ACT_DISCONNECT} },
-       {RSP_NODEV,     401, 403, -1,             0,  0, {ACT_FAKEHUP} },
-       {RSP_ERROR,     401, 401, -1,             0,  0, {ACT_ABORTHUP} },
-       {EV_TIMEOUT,    401, 403, -1,             0,  0, {ACT_ABORTHUP} },
-
-       {EV_BC_CLOSED,    0,   0, -1,             0, -1, {ACT_NOTIFY_BC_DOWN} },
-
-/* ring */
-       {RSP_ZBC,       700, 700, -1,            -1, -1, {0} },
-       {RSP_ZHLC,      700, 700, -1,            -1, -1, {0} },
-       {RSP_NMBR,      700, 700, -1,            -1, -1, {0} },
-       {RSP_ZCPN,      700, 700, -1,            -1, -1, {0} },
-       {RSP_ZCTP,      700, 700, -1,            -1, -1, {0} },
-       {EV_TIMEOUT,    700, 700, -1,           720, 720, {ACT_ICALL} },
-       {EV_BC_CLOSED,  720, 720, -1,             0, -1, {ACT_NOTIFY_BC_DOWN} },
-
-/*accept icall*/
-       {EV_ACCEPT,      -1,  -1, -1,            -1, -1, {ACT_ACCEPT} },
-       {RSP_INIT,      720, 720, SEQ_ACCEPT,   721,  5, {ACT_CMD + AT_PROTO} },
-       {RSP_OK,        721, 721, -1,           722,  5, {ACT_CMD + AT_ISO} },
-       {RSP_OK,        722, 722, -1,           723,  5, {0},   "+VLS=17\r"},
-       {RSP_OK,        723, 723, -1,           724,  5, {0} },
-       {RSP_ZVLS,      724, 724, 17,           750, 50, {ACT_ACCEPTED} },
-       {RSP_ERROR,     721, 729, -1,             0,  0, {ACT_ABORTACCEPT} },
-       {EV_TIMEOUT,    721, 729, -1,             0,  0, {ACT_ABORTACCEPT} },
-       {RSP_ZSAU,      700, 729, ZSAU_NULL,      0,  0, {ACT_ABORTACCEPT} },
-       {RSP_ZSAU,      700, 729, ZSAU_ACTIVE,    0,  0, {ACT_ABORTACCEPT} },
-       {RSP_ZSAU,      700, 729, ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT} },
-
-       {EV_BC_OPEN,    750, 750, -1,           751, -1},
-       {EV_TIMEOUT,    750, 751, -1,             0,  0, {ACT_CONNTIMEOUT} },
-
-/* B channel closed (general case) */
-       {EV_BC_CLOSED,   -1,  -1, -1,            -1, -1, {ACT_NOTIFY_BC_DOWN} },
-
-/* misc. */
-       {RSP_ZCON,       -1,  -1, -1,            -1, -1, {ACT_DEBUG} },
-       {RSP_ZCAU,       -1,  -1, -1,            -1, -1, {ACT_ZCAU} },
-       {RSP_NONE,       -1,  -1, -1,            -1, -1, {ACT_DEBUG} },
-       {RSP_ANY,        -1,  -1, -1,            -1, -1, {ACT_WARN} },
-       {RSP_LAST}
-};
-
-
-static const struct resp_type_t {
-       char    *response;
-       int     resp_code;
-       int     type;
-}
-resp_type[] =
-{
-       {"OK",          RSP_OK,         RT_NOTHING},
-       {"ERROR",       RSP_ERROR,      RT_NOTHING},
-       {"ZSAU",        RSP_ZSAU,       RT_ZSAU},
-       {"ZCAU",        RSP_ZCAU,       RT_ZCAU},
-       {"RING",        RSP_RING,       RT_RING},
-       {"ZGCI",        RSP_ZGCI,       RT_NUMBER},
-       {"ZVLS",        RSP_ZVLS,       RT_NUMBER},
-       {"ZCTP",        RSP_ZCTP,       RT_NUMBER},
-       {"ZDLE",        RSP_ZDLE,       RT_NUMBER},
-       {"ZHLC",        RSP_ZHLC,       RT_STRING},
-       {"ZBC",         RSP_ZBC,        RT_STRING},
-       {"NMBR",        RSP_NMBR,       RT_STRING},
-       {"ZCPN",        RSP_ZCPN,       RT_STRING},
-       {"ZCON",        RSP_ZCON,       RT_STRING},
-       {NULL,          0,              0}
-};
-
-static const struct zsau_resp_t {
-       char    *str;
-       int     code;
-}
-zsau_resp[] =
-{
-       {"OUTGOING_CALL_PROCEEDING",    ZSAU_PROCEEDING},
-       {"CALL_DELIVERED",              ZSAU_CALL_DELIVERED},
-       {"ACTIVE",                      ZSAU_ACTIVE},
-       {"DISCONNECT_IND",              ZSAU_DISCONNECT_IND},
-       {"NULL",                        ZSAU_NULL},
-       {"DISCONNECT_REQ",              ZSAU_DISCONNECT_REQ},
-       {NULL,                          ZSAU_UNKNOWN}
-};
-
-/* check for and remove fixed string prefix
- * If s starts with prefix terminated by a non-alphanumeric character,
- * return pointer to the first character after that, otherwise return NULL.
- */
-static char *skip_prefix(char *s, const char *prefix)
-{
-       while (*prefix)
-               if (*s++ != *prefix++)
-                       return NULL;
-       if (isalnum(*s))
-               return NULL;
-       return s;
-}
-
-/* queue event with CID */
-static void add_cid_event(struct cardstate *cs, int cid, int type,
-                         void *ptr, int parameter)
-{
-       unsigned long flags;
-       unsigned next, tail;
-       struct event_t *event;
-
-       gig_dbg(DEBUG_EVENT, "queueing event %d for cid %d", type, cid);
-
-       spin_lock_irqsave(&cs->ev_lock, flags);
-
-       tail = cs->ev_tail;
-       next = (tail + 1) % MAX_EVENTS;
-       if (unlikely(next == cs->ev_head)) {
-               dev_err(cs->dev, "event queue full\n");
-               kfree(ptr);
-       } else {
-               event = cs->events + tail;
-               event->type = type;
-               event->cid = cid;
-               event->ptr = ptr;
-               event->arg = NULL;
-               event->parameter = parameter;
-               event->at_state = NULL;
-               cs->ev_tail = next;
-       }
-
-       spin_unlock_irqrestore(&cs->ev_lock, flags);
-}
-
-/**
- * gigaset_handle_modem_response() - process received modem response
- * @cs:                device descriptor structure.
- *
- * Called by asyncdata/isocdata if a block of data received from the
- * device must be processed as a modem command response. The data is
- * already in the cs structure.
- */
-void gigaset_handle_modem_response(struct cardstate *cs)
-{
-       char *eoc, *psep, *ptr;
-       const struct resp_type_t *rt;
-       const struct zsau_resp_t *zr;
-       int cid, parameter;
-       u8 type, value;
-
-       if (!cs->cbytes) {
-               /* ignore additional LFs/CRs (M10x config mode or cx100) */
-               gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[0]);
-               return;
-       }
-       cs->respdata[cs->cbytes] = 0;
-
-       if (cs->at_state.getstring) {
-               /* state machine wants next line verbatim */
-               cs->at_state.getstring = 0;
-               ptr = kstrdup(cs->respdata, GFP_ATOMIC);
-               gig_dbg(DEBUG_EVENT, "string==%s", ptr ? ptr : "NULL");
-               add_cid_event(cs, 0, RSP_STRING, ptr, 0);
-               return;
-       }
-
-       /* look up response type */
-       for (rt = resp_type; rt->response; ++rt) {
-               eoc = skip_prefix(cs->respdata, rt->response);
-               if (eoc)
-                       break;
-       }
-       if (!rt->response) {
-               add_cid_event(cs, 0, RSP_NONE, NULL, 0);
-               gig_dbg(DEBUG_EVENT, "unknown modem response: '%s'\n",
-                       cs->respdata);
-               return;
-       }
-
-       /* check for CID */
-       psep = strrchr(cs->respdata, ';');
-       if (psep &&
-           !kstrtoint(psep + 1, 10, &cid) &&
-           cid >= 1 && cid <= 65535) {
-               /* valid CID: chop it off */
-               *psep = 0;
-       } else {
-               /* no valid CID: leave unchanged */
-               cid = 0;
-       }
-
-       gig_dbg(DEBUG_EVENT, "CMD received: %s", cs->respdata);
-       if (cid)
-               gig_dbg(DEBUG_EVENT, "CID: %d", cid);
-
-       switch (rt->type) {
-       case RT_NOTHING:
-               /* check parameter separator */
-               if (*eoc)
-                       goto bad_param; /* extra parameter */
-
-               add_cid_event(cs, cid, rt->resp_code, NULL, 0);
-               break;
-
-       case RT_RING:
-               /* check parameter separator */
-               if (!*eoc)
-                       eoc = NULL;     /* no parameter */
-               else if (*eoc++ != ',')
-                       goto bad_param;
-
-               add_cid_event(cs, 0, rt->resp_code, NULL, cid);
-
-               /* process parameters as individual responses */
-               while (eoc) {
-                       /* look up parameter type */
-                       psep = NULL;
-                       for (rt = resp_type; rt->response; ++rt) {
-                               psep = skip_prefix(eoc, rt->response);
-                               if (psep)
-                                       break;
-                       }
-
-                       /* all legal parameters are of type RT_STRING */
-                       if (!psep || rt->type != RT_STRING) {
-                               dev_warn(cs->dev,
-                                        "illegal RING parameter: '%s'\n",
-                                        eoc);
-                               return;
-                       }
-
-                       /* skip parameter value separator */
-                       if (*psep++ != '=')
-                               goto bad_param;
-
-                       /* look up end of parameter */
-                       eoc = strchr(psep, ',');
-                       if (eoc)
-                               *eoc++ = 0;
-
-                       /* retrieve parameter value */
-                       ptr = kstrdup(psep, GFP_ATOMIC);
-
-                       /* queue event */
-                       add_cid_event(cs, cid, rt->resp_code, ptr, 0);
-               }
-               break;
-
-       case RT_ZSAU:
-               /* check parameter separator */
-               if (!*eoc) {
-                       /* no parameter */
-                       add_cid_event(cs, cid, rt->resp_code, NULL, ZSAU_NONE);
-                       break;
-               }
-               if (*eoc++ != '=')
-                       goto bad_param;
-
-               /* look up parameter value */
-               for (zr = zsau_resp; zr->str; ++zr)
-                       if (!strcmp(eoc, zr->str))
-                               break;
-               if (!zr->str)
-                       goto bad_param;
-
-               add_cid_event(cs, cid, rt->resp_code, NULL, zr->code);
-               break;
-
-       case RT_STRING:
-               /* check parameter separator */
-               if (*eoc++ != '=')
-                       goto bad_param;
-
-               /* retrieve parameter value */
-               ptr = kstrdup(eoc, GFP_ATOMIC);
-
-               /* queue event */
-               add_cid_event(cs, cid, rt->resp_code, ptr, 0);
-               break;
-
-       case RT_ZCAU:
-               /* check parameter separators */
-               if (*eoc++ != '=')
-                       goto bad_param;
-               psep = strchr(eoc, ',');
-               if (!psep)
-                       goto bad_param;
-               *psep++ = 0;
-
-               /* decode parameter values */
-               if (kstrtou8(eoc, 16, &type) || kstrtou8(psep, 16, &value)) {
-                       *--psep = ',';
-                       goto bad_param;
-               }
-               parameter = (type << 8) | value;
-
-               add_cid_event(cs, cid, rt->resp_code, NULL, parameter);
-               break;
-
-       case RT_NUMBER:
-               /* check parameter separator */
-               if (*eoc++ != '=')
-                       goto bad_param;
-
-               /* decode parameter value */
-               if (kstrtoint(eoc, 10, &parameter))
-                       goto bad_param;
-
-               /* special case ZDLE: set flag before queueing event */
-               if (rt->resp_code == RSP_ZDLE)
-                       cs->dle = parameter;
-
-               add_cid_event(cs, cid, rt->resp_code, NULL, parameter);
-               break;
-
-bad_param:
-               /* parameter unexpected, incomplete or malformed */
-               dev_warn(cs->dev, "bad parameter in response '%s'\n",
-                        cs->respdata);
-               add_cid_event(cs, cid, rt->resp_code, NULL, -1);
-               break;
-
-       default:
-               dev_err(cs->dev, "%s: internal error on '%s'\n",
-                       __func__, cs->respdata);
-       }
-}
-EXPORT_SYMBOL_GPL(gigaset_handle_modem_response);
-
-/* disconnect_nobc
- * process closing of connection associated with given AT state structure
- * without B channel
- */
-static void disconnect_nobc(struct at_state_t **at_state_p,
-                           struct cardstate *cs)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       ++(*at_state_p)->seq_index;
-
-       /* revert to selected idle mode */
-       if (!cs->cidmode) {
-               cs->at_state.pending_commands |= PC_UMMODE;
-               gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
-               cs->commands_pending = 1;
-       }
-
-       /* check for and deallocate temporary AT state */
-       if (!list_empty(&(*at_state_p)->list)) {
-               list_del(&(*at_state_p)->list);
-               kfree(*at_state_p);
-               *at_state_p = NULL;
-       }
-
-       spin_unlock_irqrestore(&cs->lock, flags);
-}
-
-/* disconnect_bc
- * process closing of connection associated with given AT state structure
- * and B channel
- */
-static void disconnect_bc(struct at_state_t *at_state,
-                         struct cardstate *cs, struct bc_state *bcs)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       ++at_state->seq_index;
-
-       /* revert to selected idle mode */
-       if (!cs->cidmode) {
-               cs->at_state.pending_commands |= PC_UMMODE;
-               gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
-               cs->commands_pending = 1;
-       }
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       /* invoke hardware specific handler */
-       cs->ops->close_bchannel(bcs);
-
-       /* notify LL */
-       if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
-               bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
-               gigaset_isdn_hupD(bcs);
-       }
-}
-
-/* get_free_channel
- * get a free AT state structure: either one of those associated with the
- * B channels of the Gigaset device, or if none of those is available,
- * a newly allocated one with bcs=NULL
- * The structure should be freed by calling disconnect_nobc() after use.
- */
-static inline struct at_state_t *get_free_channel(struct cardstate *cs,
-                                                 int cid)
-/* cids: >0: siemens-cid
- *        0: without cid
- *       -1: no cid assigned yet
- */
-{
-       unsigned long flags;
-       int i;
-       struct at_state_t *ret;
-
-       for (i = 0; i < cs->channels; ++i)
-               if (gigaset_get_channel(cs->bcs + i) >= 0) {
-                       ret = &cs->bcs[i].at_state;
-                       ret->cid = cid;
-                       return ret;
-               }
-
-       spin_lock_irqsave(&cs->lock, flags);
-       ret = kmalloc(sizeof(struct at_state_t), GFP_ATOMIC);
-       if (ret) {
-               gigaset_at_init(ret, NULL, cs, cid);
-               list_add(&ret->list, &cs->temp_at_states);
-       }
-       spin_unlock_irqrestore(&cs->lock, flags);
-       return ret;
-}
-
-static void init_failed(struct cardstate *cs, int mode)
-{
-       int i;
-       struct at_state_t *at_state;
-
-       cs->at_state.pending_commands &= ~PC_INIT;
-       cs->mode = mode;
-       cs->mstate = MS_UNINITIALIZED;
-       gigaset_free_channels(cs);
-       for (i = 0; i < cs->channels; ++i) {
-               at_state = &cs->bcs[i].at_state;
-               if (at_state->pending_commands & PC_CID) {
-                       at_state->pending_commands &= ~PC_CID;
-                       at_state->pending_commands |= PC_NOCID;
-                       cs->commands_pending = 1;
-               }
-       }
-}
-
-static void schedule_init(struct cardstate *cs, int state)
-{
-       if (cs->at_state.pending_commands & PC_INIT) {
-               gig_dbg(DEBUG_EVENT, "not scheduling PC_INIT again");
-               return;
-       }
-       cs->mstate = state;
-       cs->mode = M_UNKNOWN;
-       gigaset_block_channels(cs);
-       cs->at_state.pending_commands |= PC_INIT;
-       gig_dbg(DEBUG_EVENT, "Scheduling PC_INIT");
-       cs->commands_pending = 1;
-}
-
-/* send an AT command
- * adding the "AT" prefix, cid and DLE encapsulation as appropriate
- */
-static void send_command(struct cardstate *cs, const char *cmd,
-                        struct at_state_t *at_state)
-{
-       int cid = at_state->cid;
-       struct cmdbuf_t *cb;
-       size_t buflen;
-
-       buflen = strlen(cmd) + 12; /* DLE ( A T 1 2 3 4 5 <cmd> DLE ) \0 */
-       cb = kmalloc(sizeof(struct cmdbuf_t) + buflen, GFP_ATOMIC);
-       if (!cb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return;
-       }
-       if (cid > 0 && cid <= 65535)
-               cb->len = snprintf(cb->buf, buflen,
-                                  cs->dle ? "\020(AT%d%s\020)" : "AT%d%s",
-                                  cid, cmd);
-       else
-               cb->len = snprintf(cb->buf, buflen,
-                                  cs->dle ? "\020(AT%s\020)" : "AT%s",
-                                  cmd);
-       cb->offset = 0;
-       cb->next = NULL;
-       cb->wake_tasklet = NULL;
-       cs->ops->write_cmd(cs, cb);
-}
-
-static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid)
-{
-       struct at_state_t *at_state;
-       int i;
-       unsigned long flags;
-
-       if (cid == 0)
-               return &cs->at_state;
-
-       for (i = 0; i < cs->channels; ++i)
-               if (cid == cs->bcs[i].at_state.cid)
-                       return &cs->bcs[i].at_state;
-
-       spin_lock_irqsave(&cs->lock, flags);
-
-       list_for_each_entry(at_state, &cs->temp_at_states, list)
-               if (cid == at_state->cid) {
-                       spin_unlock_irqrestore(&cs->lock, flags);
-                       return at_state;
-               }
-
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       return NULL;
-}
-
-static void bchannel_down(struct bc_state *bcs)
-{
-       if (bcs->chstate & CHS_B_UP) {
-               bcs->chstate &= ~CHS_B_UP;
-               gigaset_isdn_hupB(bcs);
-       }
-
-       if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
-               bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
-               gigaset_isdn_hupD(bcs);
-       }
-
-       gigaset_free_channel(bcs);
-
-       gigaset_bcs_reinit(bcs);
-}
-
-static void bchannel_up(struct bc_state *bcs)
-{
-       if (bcs->chstate & CHS_B_UP) {
-               dev_notice(bcs->cs->dev, "%s: B channel already up\n",
-                          __func__);
-               return;
-       }
-
-       bcs->chstate |= CHS_B_UP;
-       gigaset_isdn_connB(bcs);
-}
-
-static void start_dial(struct at_state_t *at_state, void *data,
-                      unsigned seq_index)
-{
-       struct bc_state *bcs = at_state->bcs;
-       struct cardstate *cs = at_state->cs;
-       char **commands = data;
-       unsigned long flags;
-       int i;
-
-       bcs->chstate |= CHS_NOTIFY_LL;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (at_state->seq_index != seq_index) {
-               spin_unlock_irqrestore(&cs->lock, flags);
-               goto error;
-       }
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       for (i = 0; i < AT_NUM; ++i) {
-               kfree(bcs->commands[i]);
-               bcs->commands[i] = commands[i];
-       }
-
-       at_state->pending_commands |= PC_CID;
-       gig_dbg(DEBUG_EVENT, "Scheduling PC_CID");
-       cs->commands_pending = 1;
-       return;
-
-error:
-       for (i = 0; i < AT_NUM; ++i) {
-               kfree(commands[i]);
-               commands[i] = NULL;
-       }
-       at_state->pending_commands |= PC_NOCID;
-       gig_dbg(DEBUG_EVENT, "Scheduling PC_NOCID");
-       cs->commands_pending = 1;
-       return;
-}
-
-static void start_accept(struct at_state_t *at_state)
-{
-       struct cardstate *cs = at_state->cs;
-       struct bc_state *bcs = at_state->bcs;
-       int i;
-
-       for (i = 0; i < AT_NUM; ++i) {
-               kfree(bcs->commands[i]);
-               bcs->commands[i] = NULL;
-       }
-
-       bcs->commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
-       bcs->commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
-       if (!bcs->commands[AT_PROTO] || !bcs->commands[AT_ISO]) {
-               dev_err(at_state->cs->dev, "out of memory\n");
-               /* error reset */
-               at_state->pending_commands |= PC_HUP;
-               gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP");
-               cs->commands_pending = 1;
-               return;
-       }
-
-       snprintf(bcs->commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
-       snprintf(bcs->commands[AT_ISO], 9, "^SISO=%u\r", bcs->channel + 1);
-
-       at_state->pending_commands |= PC_ACCEPT;
-       gig_dbg(DEBUG_EVENT, "Scheduling PC_ACCEPT");
-       cs->commands_pending = 1;
-}
-
-static void do_start(struct cardstate *cs)
-{
-       gigaset_free_channels(cs);
-
-       if (cs->mstate != MS_LOCKED)
-               schedule_init(cs, MS_INIT);
-
-       cs->isdn_up = 1;
-       gigaset_isdn_start(cs);
-
-       cs->waiting = 0;
-       wake_up(&cs->waitqueue);
-}
-
-static void finish_shutdown(struct cardstate *cs)
-{
-       if (cs->mstate != MS_LOCKED) {
-               cs->mstate = MS_UNINITIALIZED;
-               cs->mode = M_UNKNOWN;
-       }
-
-       /* Tell the LL that the device is not available .. */
-       if (cs->isdn_up) {
-               cs->isdn_up = 0;
-               gigaset_isdn_stop(cs);
-       }
-
-       /* The rest is done by cleanup_cs() in process context. */
-
-       cs->cmd_result = -ENODEV;
-       cs->waiting = 0;
-       wake_up(&cs->waitqueue);
-}
-
-static void do_shutdown(struct cardstate *cs)
-{
-       gigaset_block_channels(cs);
-
-       if (cs->mstate == MS_READY) {
-               cs->mstate = MS_SHUTDOWN;
-               cs->at_state.pending_commands |= PC_SHUTDOWN;
-               gig_dbg(DEBUG_EVENT, "Scheduling PC_SHUTDOWN");
-               cs->commands_pending = 1;
-       } else
-               finish_shutdown(cs);
-}
-
-static void do_stop(struct cardstate *cs)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       cs->connected = 0;
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       do_shutdown(cs);
-}
-
-/* Entering cid mode or getting a cid failed:
- * try to initialize the device and try again.
- *
- * channel >= 0: getting cid for the channel failed
- * channel < 0:  entering cid mode failed
- *
- * returns 0 on success, <0 on failure
- */
-static int reinit_and_retry(struct cardstate *cs, int channel)
-{
-       int i;
-
-       if (--cs->retry_count <= 0)
-               return -EFAULT;
-
-       for (i = 0; i < cs->channels; ++i)
-               if (cs->bcs[i].at_state.cid > 0)
-                       return -EBUSY;
-
-       if (channel < 0)
-               dev_warn(cs->dev,
-                        "Could not enter cid mode. Reinit device and try again.\n");
-       else {
-               dev_warn(cs->dev,
-                        "Could not get a call id. Reinit device and try again.\n");
-               cs->bcs[channel].at_state.pending_commands |= PC_CID;
-       }
-       schedule_init(cs, MS_INIT);
-       return 0;
-}
-
-static int at_state_invalid(struct cardstate *cs,
-                           struct at_state_t *test_ptr)
-{
-       unsigned long flags;
-       unsigned channel;
-       struct at_state_t *at_state;
-       int retval = 0;
-
-       spin_lock_irqsave(&cs->lock, flags);
-
-       if (test_ptr == &cs->at_state)
-               goto exit;
-
-       list_for_each_entry(at_state, &cs->temp_at_states, list)
-               if (at_state == test_ptr)
-                       goto exit;
-
-       for (channel = 0; channel < cs->channels; ++channel)
-               if (&cs->bcs[channel].at_state == test_ptr)
-                       goto exit;
-
-       retval = 1;
-exit:
-       spin_unlock_irqrestore(&cs->lock, flags);
-       return retval;
-}
-
-static void handle_icall(struct cardstate *cs, struct bc_state *bcs,
-                        struct at_state_t *at_state)
-{
-       int retval;
-
-       retval = gigaset_isdn_icall(at_state);
-       switch (retval) {
-       case ICALL_ACCEPT:
-               break;
-       default:
-               dev_err(cs->dev, "internal error: disposition=%d\n", retval);
-               /* fall through */
-       case ICALL_IGNORE:
-       case ICALL_REJECT:
-               /* hang up actively
-                * Device doc says that would reject the call.
-                * In fact it doesn't.
-                */
-               at_state->pending_commands |= PC_HUP;
-               cs->commands_pending = 1;
-               break;
-       }
-}
-
-static int do_lock(struct cardstate *cs)
-{
-       int mode;
-       int i;
-
-       switch (cs->mstate) {
-       case MS_UNINITIALIZED:
-       case MS_READY:
-               if (cs->cur_at_seq || !list_empty(&cs->temp_at_states) ||
-                   cs->at_state.pending_commands)
-                       return -EBUSY;
-
-               for (i = 0; i < cs->channels; ++i)
-                       if (cs->bcs[i].at_state.pending_commands)
-                               return -EBUSY;
-
-               if (gigaset_get_channels(cs) < 0)
-                       return -EBUSY;
-
-               break;
-       case MS_LOCKED:
-               break;
-       default:
-               return -EBUSY;
-       }
-
-       mode = cs->mode;
-       cs->mstate = MS_LOCKED;
-       cs->mode = M_UNKNOWN;
-
-       return mode;
-}
-
-static int do_unlock(struct cardstate *cs)
-{
-       if (cs->mstate != MS_LOCKED)
-               return -EINVAL;
-
-       cs->mstate = MS_UNINITIALIZED;
-       cs->mode = M_UNKNOWN;
-       gigaset_free_channels(cs);
-       if (cs->connected)
-               schedule_init(cs, MS_INIT);
-
-       return 0;
-}
-
-static void do_action(int action, struct cardstate *cs,
-                     struct bc_state *bcs,
-                     struct at_state_t **p_at_state, char **pp_command,
-                     int *p_genresp, int *p_resp_code,
-                     struct event_t *ev)
-{
-       struct at_state_t *at_state = *p_at_state;
-       struct bc_state *bcs2;
-       unsigned long flags;
-
-       int channel;
-
-       unsigned char *s, *e;
-       int i;
-       unsigned long val;
-
-       switch (action) {
-       case ACT_NOTHING:
-               break;
-       case ACT_TIMEOUT:
-               at_state->waiting = 1;
-               break;
-       case ACT_INIT:
-               cs->at_state.pending_commands &= ~PC_INIT;
-               cs->cur_at_seq = SEQ_NONE;
-               cs->mode = M_UNIMODEM;
-               spin_lock_irqsave(&cs->lock, flags);
-               if (!cs->cidmode) {
-                       spin_unlock_irqrestore(&cs->lock, flags);
-                       gigaset_free_channels(cs);
-                       cs->mstate = MS_READY;
-                       break;
-               }
-               spin_unlock_irqrestore(&cs->lock, flags);
-               cs->at_state.pending_commands |= PC_CIDMODE;
-               gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
-               cs->commands_pending = 1;
-               break;
-       case ACT_FAILINIT:
-               dev_warn(cs->dev, "Could not initialize the device.\n");
-               cs->dle = 0;
-               init_failed(cs, M_UNKNOWN);
-               cs->cur_at_seq = SEQ_NONE;
-               break;
-       case ACT_CONFIGMODE:
-               init_failed(cs, M_CONFIG);
-               cs->cur_at_seq = SEQ_NONE;
-               break;
-       case ACT_SETDLE1:
-               cs->dle = 1;
-               /* cs->inbuf[0].inputstate |= INS_command | INS_DLE_command; */
-               cs->inbuf[0].inputstate &=
-                       ~(INS_command | INS_DLE_command);
-               break;
-       case ACT_SETDLE0:
-               cs->dle = 0;
-               cs->inbuf[0].inputstate =
-                       (cs->inbuf[0].inputstate & ~INS_DLE_command)
-                       | INS_command;
-               break;
-       case ACT_CMODESET:
-               if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) {
-                       gigaset_free_channels(cs);
-                       cs->mstate = MS_READY;
-               }
-               cs->mode = M_CID;
-               cs->cur_at_seq = SEQ_NONE;
-               break;
-       case ACT_UMODESET:
-               cs->mode = M_UNIMODEM;
-               cs->cur_at_seq = SEQ_NONE;
-               break;
-       case ACT_FAILCMODE:
-               cs->cur_at_seq = SEQ_NONE;
-               if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) {
-                       init_failed(cs, M_UNKNOWN);
-                       break;
-               }
-               if (reinit_and_retry(cs, -1) < 0)
-                       schedule_init(cs, MS_RECOVER);
-               break;
-       case ACT_FAILUMODE:
-               cs->cur_at_seq = SEQ_NONE;
-               schedule_init(cs, MS_RECOVER);
-               break;
-       case ACT_HUPMODEM:
-               /* send "+++" (hangup in unimodem mode) */
-               if (cs->connected) {
-                       struct cmdbuf_t *cb;
-
-                       cb = kmalloc(sizeof(struct cmdbuf_t) + 3, GFP_ATOMIC);
-                       if (!cb) {
-                               dev_err(cs->dev, "%s: out of memory\n",
-                                       __func__);
-                               return;
-                       }
-                       memcpy(cb->buf, "+++", 3);
-                       cb->len = 3;
-                       cb->offset = 0;
-                       cb->next = NULL;
-                       cb->wake_tasklet = NULL;
-                       cs->ops->write_cmd(cs, cb);
-               }
-               break;
-       case ACT_RING:
-               /* get fresh AT state structure for new CID */
-               at_state = get_free_channel(cs, ev->parameter);
-               if (!at_state) {
-                       dev_warn(cs->dev,
-                                "RING ignored: could not allocate channel structure\n");
-                       break;
-               }
-
-               /* initialize AT state structure
-                * note that bcs may be NULL if no B channel is free
-                */
-               at_state->ConState = 700;
-               for (i = 0; i < STR_NUM; ++i) {
-                       kfree(at_state->str_var[i]);
-                       at_state->str_var[i] = NULL;
-               }
-               at_state->int_var[VAR_ZCTP] = -1;
-
-               spin_lock_irqsave(&cs->lock, flags);
-               at_state->timer_expires = RING_TIMEOUT;
-               at_state->timer_active = 1;
-               spin_unlock_irqrestore(&cs->lock, flags);
-               break;
-       case ACT_ICALL:
-               handle_icall(cs, bcs, at_state);
-               break;
-       case ACT_FAILSDOWN:
-               dev_warn(cs->dev, "Could not shut down the device.\n");
-               /* fall through */
-       case ACT_FAKESDOWN:
-       case ACT_SDOWN:
-               cs->cur_at_seq = SEQ_NONE;
-               finish_shutdown(cs);
-               break;
-       case ACT_CONNECT:
-               if (cs->onechannel) {
-                       at_state->pending_commands |= PC_DLE1;
-                       cs->commands_pending = 1;
-                       break;
-               }
-               bcs->chstate |= CHS_D_UP;
-               gigaset_isdn_connD(bcs);
-               cs->ops->init_bchannel(bcs);
-               break;
-       case ACT_DLE1:
-               cs->cur_at_seq = SEQ_NONE;
-               bcs = cs->bcs + cs->curchannel;
-
-               bcs->chstate |= CHS_D_UP;
-               gigaset_isdn_connD(bcs);
-               cs->ops->init_bchannel(bcs);
-               break;
-       case ACT_FAKEHUP:
-               at_state->int_var[VAR_ZSAU] = ZSAU_NULL;
-               /* fall through */
-       case ACT_DISCONNECT:
-               cs->cur_at_seq = SEQ_NONE;
-               at_state->cid = -1;
-               if (!bcs) {
-                       disconnect_nobc(p_at_state, cs);
-               } else if (cs->onechannel && cs->dle) {
-                       /* Check for other open channels not needed:
-                        * DLE only used for M10x with one B channel.
-                        */
-                       at_state->pending_commands |= PC_DLE0;
-                       cs->commands_pending = 1;
-               } else {
-                       disconnect_bc(at_state, cs, bcs);
-               }
-               break;
-       case ACT_FAKEDLE0:
-               at_state->int_var[VAR_ZDLE] = 0;
-               cs->dle = 0;
-               /* fall through */
-       case ACT_DLE0:
-               cs->cur_at_seq = SEQ_NONE;
-               bcs2 = cs->bcs + cs->curchannel;
-               disconnect_bc(&bcs2->at_state, cs, bcs2);
-               break;
-       case ACT_ABORTHUP:
-               cs->cur_at_seq = SEQ_NONE;
-               dev_warn(cs->dev, "Could not hang up.\n");
-               at_state->cid = -1;
-               if (!bcs)
-                       disconnect_nobc(p_at_state, cs);
-               else if (cs->onechannel)
-                       at_state->pending_commands |= PC_DLE0;
-               else
-                       disconnect_bc(at_state, cs, bcs);
-               schedule_init(cs, MS_RECOVER);
-               break;
-       case ACT_FAILDLE0:
-               cs->cur_at_seq = SEQ_NONE;
-               dev_warn(cs->dev, "Error leaving DLE mode.\n");
-               cs->dle = 0;
-               bcs2 = cs->bcs + cs->curchannel;
-               disconnect_bc(&bcs2->at_state, cs, bcs2);
-               schedule_init(cs, MS_RECOVER);
-               break;
-       case ACT_FAILDLE1:
-               cs->cur_at_seq = SEQ_NONE;
-               dev_warn(cs->dev,
-                        "Could not enter DLE mode. Trying to hang up.\n");
-               channel = cs->curchannel;
-               cs->bcs[channel].at_state.pending_commands |= PC_HUP;
-               cs->commands_pending = 1;
-               break;
-
-       case ACT_CID: /* got cid; start dialing */
-               cs->cur_at_seq = SEQ_NONE;
-               channel = cs->curchannel;
-               if (ev->parameter > 0 && ev->parameter <= 65535) {
-                       cs->bcs[channel].at_state.cid = ev->parameter;
-                       cs->bcs[channel].at_state.pending_commands |=
-                               PC_DIAL;
-                       cs->commands_pending = 1;
-                       break;
-               }
-               /* fall through - bad cid */
-       case ACT_FAILCID:
-               cs->cur_at_seq = SEQ_NONE;
-               channel = cs->curchannel;
-               if (reinit_and_retry(cs, channel) < 0) {
-                       dev_warn(cs->dev,
-                                "Could not get a call ID. Cannot dial.\n");
-                       bcs2 = cs->bcs + channel;
-                       disconnect_bc(&bcs2->at_state, cs, bcs2);
-               }
-               break;
-       case ACT_ABORTCID:
-               cs->cur_at_seq = SEQ_NONE;
-               bcs2 = cs->bcs + cs->curchannel;
-               disconnect_bc(&bcs2->at_state, cs, bcs2);
-               break;
-
-       case ACT_DIALING:
-       case ACT_ACCEPTED:
-               cs->cur_at_seq = SEQ_NONE;
-               break;
-
-       case ACT_ABORTACCEPT:   /* hangup/error/timeout during ICALL procssng */
-               if (bcs)
-                       disconnect_bc(at_state, cs, bcs);
-               else
-                       disconnect_nobc(p_at_state, cs);
-               break;
-
-       case ACT_ABORTDIAL:     /* error/timeout during dial preparation */
-               cs->cur_at_seq = SEQ_NONE;
-               at_state->pending_commands |= PC_HUP;
-               cs->commands_pending = 1;
-               break;
-
-       case ACT_REMOTEREJECT:  /* DISCONNECT_IND after dialling */
-       case ACT_CONNTIMEOUT:   /* timeout waiting for ZSAU=ACTIVE */
-       case ACT_REMOTEHUP:     /* DISCONNECT_IND with established connection */
-               at_state->pending_commands |= PC_HUP;
-               cs->commands_pending = 1;
-               break;
-       case ACT_GETSTRING: /* warning: RING, ZDLE, ...
-                              are not handled properly anymore */
-               at_state->getstring = 1;
-               break;
-       case ACT_SETVER:
-               if (!ev->ptr) {
-                       *p_genresp = 1;
-                       *p_resp_code = RSP_ERROR;
-                       break;
-               }
-               s = ev->ptr;
-
-               if (!strcmp(s, "OK")) {
-                       /* OK without version string: assume old response */
-                       *p_genresp = 1;
-                       *p_resp_code = RSP_NONE;
-                       break;
-               }
-
-               for (i = 0; i < 4; ++i) {
-                       val = simple_strtoul(s, (char **) &e, 10);
-                       if (val > INT_MAX || e == s)
-                               break;
-                       if (i == 3) {
-                               if (*e)
-                                       break;
-                       } else if (*e != '.')
-                               break;
-                       else
-                               s = e + 1;
-                       cs->fwver[i] = val;
-               }
-               if (i != 4) {
-                       *p_genresp = 1;
-                       *p_resp_code = RSP_ERROR;
-                       break;
-               }
-               cs->gotfwver = 0;
-               break;
-       case ACT_GOTVER:
-               if (cs->gotfwver == 0) {
-                       cs->gotfwver = 1;
-                       gig_dbg(DEBUG_EVENT,
-                               "firmware version %02d.%03d.%02d.%02d",
-                               cs->fwver[0], cs->fwver[1],
-                               cs->fwver[2], cs->fwver[3]);
-                       break;
-               }
-               /* fall through */
-       case ACT_FAILVER:
-               cs->gotfwver = -1;
-               dev_err(cs->dev, "could not read firmware version.\n");
-               break;
-       case ACT_ERROR:
-               gig_dbg(DEBUG_ANY, "%s: ERROR response in ConState %d",
-                       __func__, at_state->ConState);
-               cs->cur_at_seq = SEQ_NONE;
-               break;
-       case ACT_DEBUG:
-               gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d",
-                       __func__, ev->type, at_state->ConState);
-               break;
-       case ACT_WARN:
-               dev_warn(cs->dev, "%s: resp_code %d in ConState %d!\n",
-                        __func__, ev->type, at_state->ConState);
-               break;
-       case ACT_ZCAU:
-               dev_warn(cs->dev, "cause code %04x in connection state %d.\n",
-                        ev->parameter, at_state->ConState);
-               break;
-
-       /* events from the LL */
-
-       case ACT_DIAL:
-               if (!ev->ptr) {
-                       *p_genresp = 1;
-                       *p_resp_code = RSP_ERROR;
-                       break;
-               }
-               start_dial(at_state, ev->ptr, ev->parameter);
-               break;
-       case ACT_ACCEPT:
-               start_accept(at_state);
-               break;
-       case ACT_HUP:
-               at_state->pending_commands |= PC_HUP;
-               gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP");
-               cs->commands_pending = 1;
-               break;
-
-       /* hotplug events */
-
-       case ACT_STOP:
-               do_stop(cs);
-               break;
-       case ACT_START:
-               do_start(cs);
-               break;
-
-       /* events from the interface */
-
-       case ACT_IF_LOCK:
-               cs->cmd_result = ev->parameter ? do_lock(cs) : do_unlock(cs);
-               cs->waiting = 0;
-               wake_up(&cs->waitqueue);
-               break;
-       case ACT_IF_VER:
-               if (ev->parameter != 0)
-                       cs->cmd_result = -EINVAL;
-               else if (cs->gotfwver != 1) {
-                       cs->cmd_result = -ENOENT;
-               } else {
-                       memcpy(ev->arg, cs->fwver, sizeof cs->fwver);
-                       cs->cmd_result = 0;
-               }
-               cs->waiting = 0;
-               wake_up(&cs->waitqueue);
-               break;
-
-       /* events from the proc file system */
-
-       case ACT_PROC_CIDMODE:
-               spin_lock_irqsave(&cs->lock, flags);
-               if (ev->parameter != cs->cidmode) {
-                       cs->cidmode = ev->parameter;
-                       if (ev->parameter) {
-                               cs->at_state.pending_commands |= PC_CIDMODE;
-                               gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
-                       } else {
-                               cs->at_state.pending_commands |= PC_UMMODE;
-                               gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
-                       }
-                       cs->commands_pending = 1;
-               }
-               spin_unlock_irqrestore(&cs->lock, flags);
-               cs->waiting = 0;
-               wake_up(&cs->waitqueue);
-               break;
-
-       /* events from the hardware drivers */
-
-       case ACT_NOTIFY_BC_DOWN:
-               bchannel_down(bcs);
-               break;
-       case ACT_NOTIFY_BC_UP:
-               bchannel_up(bcs);
-               break;
-       case ACT_SHUTDOWN:
-               do_shutdown(cs);
-               break;
-
-
-       default:
-               if (action >= ACT_CMD && action < ACT_CMD + AT_NUM) {
-                       *pp_command = at_state->bcs->commands[action - ACT_CMD];
-                       if (!*pp_command) {
-                               *p_genresp = 1;
-                               *p_resp_code = RSP_NULL;
-                       }
-               } else
-                       dev_err(cs->dev, "%s: action==%d!\n", __func__, action);
-       }
-}
-
-/* State machine to do the calling and hangup procedure */
-static void process_event(struct cardstate *cs, struct event_t *ev)
-{
-       struct bc_state *bcs;
-       char *p_command = NULL;
-       struct reply_t *rep;
-       int rcode;
-       int genresp = 0;
-       int resp_code = RSP_ERROR;
-       struct at_state_t *at_state;
-       int index;
-       int curact;
-       unsigned long flags;
-
-       if (ev->cid >= 0) {
-               at_state = at_state_from_cid(cs, ev->cid);
-               if (!at_state) {
-                       gig_dbg(DEBUG_EVENT, "event %d for invalid cid %d",
-                               ev->type, ev->cid);
-                       gigaset_add_event(cs, &cs->at_state, RSP_WRONG_CID,
-                                         NULL, 0, NULL);
-                       return;
-               }
-       } else {
-               at_state = ev->at_state;
-               if (at_state_invalid(cs, at_state)) {
-                       gig_dbg(DEBUG_EVENT, "event for invalid at_state %p",
-                               at_state);
-                       return;
-               }
-       }
-
-       gig_dbg(DEBUG_EVENT, "connection state %d, event %d",
-               at_state->ConState, ev->type);
-
-       bcs = at_state->bcs;
-
-       /* Setting the pointer to the dial array */
-       rep = at_state->replystruct;
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (ev->type == EV_TIMEOUT) {
-               if (ev->parameter != at_state->timer_index
-                   || !at_state->timer_active) {
-                       ev->type = RSP_NONE; /* old timeout */
-                       gig_dbg(DEBUG_EVENT, "old timeout");
-               } else {
-                       if (at_state->waiting)
-                               gig_dbg(DEBUG_EVENT, "stopped waiting");
-                       else
-                               gig_dbg(DEBUG_EVENT, "timeout occurred");
-               }
-       }
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       /* if the response belongs to a variable in at_state->int_var[VAR_XXXX]
-          or at_state->str_var[STR_XXXX], set it */
-       if (ev->type >= RSP_VAR && ev->type < RSP_VAR + VAR_NUM) {
-               index = ev->type - RSP_VAR;
-               at_state->int_var[index] = ev->parameter;
-       } else if (ev->type >= RSP_STR && ev->type < RSP_STR + STR_NUM) {
-               index = ev->type - RSP_STR;
-               kfree(at_state->str_var[index]);
-               at_state->str_var[index] = ev->ptr;
-               ev->ptr = NULL; /* prevent process_events() from
-                                  deallocating ptr */
-       }
-
-       if (ev->type == EV_TIMEOUT || ev->type == RSP_STRING)
-               at_state->getstring = 0;
-
-       /* Search row in dial array which matches modem response and current
-          constate */
-       for (;; rep++) {
-               rcode = rep->resp_code;
-               if (rcode == RSP_LAST) {
-                       /* found nothing...*/
-                       dev_warn(cs->dev, "%s: rcode=RSP_LAST: "
-                                "resp_code %d in ConState %d!\n",
-                                __func__, ev->type, at_state->ConState);
-                       return;
-               }
-               if ((rcode == RSP_ANY || rcode == ev->type)
-                   && ((int) at_state->ConState >= rep->min_ConState)
-                   && (rep->max_ConState < 0
-                       || (int) at_state->ConState <= rep->max_ConState)
-                   && (rep->parameter < 0 || rep->parameter == ev->parameter))
-                       break;
-       }
-
-       p_command = rep->command;
-
-       at_state->waiting = 0;
-       for (curact = 0; curact < MAXACT; ++curact) {
-               /* The row tells us what we should do  ..
-                */
-               do_action(rep->action[curact], cs, bcs, &at_state, &p_command,
-                         &genresp, &resp_code, ev);
-               if (!at_state)
-                       /* at_state destroyed by disconnect */
-                       return;
-       }
-
-       /* Jump to the next con-state regarding the array */
-       if (rep->new_ConState >= 0)
-               at_state->ConState = rep->new_ConState;
-
-       if (genresp) {
-               spin_lock_irqsave(&cs->lock, flags);
-               at_state->timer_expires = 0;
-               at_state->timer_active = 0;
-               spin_unlock_irqrestore(&cs->lock, flags);
-               gigaset_add_event(cs, at_state, resp_code, NULL, 0, NULL);
-       } else {
-               /* Send command to modem if not NULL... */
-               if (p_command) {
-                       if (cs->connected)
-                               send_command(cs, p_command, at_state);
-                       else
-                               gigaset_add_event(cs, at_state, RSP_NODEV,
-                                                 NULL, 0, NULL);
-               }
-
-               spin_lock_irqsave(&cs->lock, flags);
-               if (!rep->timeout) {
-                       at_state->timer_expires = 0;
-                       at_state->timer_active = 0;
-               } else if (rep->timeout > 0) { /* new timeout */
-                       at_state->timer_expires = rep->timeout * 10;
-                       at_state->timer_active = 1;
-                       ++at_state->timer_index;
-               }
-               spin_unlock_irqrestore(&cs->lock, flags);
-       }
-}
-
-static void schedule_sequence(struct cardstate *cs,
-                             struct at_state_t *at_state, int sequence)
-{
-       cs->cur_at_seq = sequence;
-       gigaset_add_event(cs, at_state, RSP_INIT, NULL, sequence, NULL);
-}
-
-static void process_command_flags(struct cardstate *cs)
-{
-       struct at_state_t *at_state = NULL;
-       struct bc_state *bcs;
-       int i;
-       int sequence;
-       unsigned long flags;
-
-       cs->commands_pending = 0;
-
-       if (cs->cur_at_seq) {
-               gig_dbg(DEBUG_EVENT, "not searching scheduled commands: busy");
-               return;
-       }
-
-       gig_dbg(DEBUG_EVENT, "searching scheduled commands");
-
-       sequence = SEQ_NONE;
-
-       /* clear pending_commands and hangup channels on shutdown */
-       if (cs->at_state.pending_commands & PC_SHUTDOWN) {
-               cs->at_state.pending_commands &= ~PC_CIDMODE;
-               for (i = 0; i < cs->channels; ++i) {
-                       bcs = cs->bcs + i;
-                       at_state = &bcs->at_state;
-                       at_state->pending_commands &=
-                               ~(PC_DLE1 | PC_ACCEPT | PC_DIAL);
-                       if (at_state->cid > 0)
-                               at_state->pending_commands |= PC_HUP;
-                       if (at_state->pending_commands & PC_CID) {
-                               at_state->pending_commands |= PC_NOCID;
-                               at_state->pending_commands &= ~PC_CID;
-                       }
-               }
-       }
-
-       /* clear pending_commands and hangup channels on reset */
-       if (cs->at_state.pending_commands & PC_INIT) {
-               cs->at_state.pending_commands &= ~PC_CIDMODE;
-               for (i = 0; i < cs->channels; ++i) {
-                       bcs = cs->bcs + i;
-                       at_state = &bcs->at_state;
-                       at_state->pending_commands &=
-                               ~(PC_DLE1 | PC_ACCEPT | PC_DIAL);
-                       if (at_state->cid > 0)
-                               at_state->pending_commands |= PC_HUP;
-                       if (cs->mstate == MS_RECOVER) {
-                               if (at_state->pending_commands & PC_CID) {
-                                       at_state->pending_commands |= PC_NOCID;
-                                       at_state->pending_commands &= ~PC_CID;
-                               }
-                       }
-               }
-       }
-
-       /* only switch back to unimodem mode if no commands are pending and
-        * no channels are up */
-       spin_lock_irqsave(&cs->lock, flags);
-       if (cs->at_state.pending_commands == PC_UMMODE
-           && !cs->cidmode
-           && list_empty(&cs->temp_at_states)
-           && cs->mode == M_CID) {
-               sequence = SEQ_UMMODE;
-               at_state = &cs->at_state;
-               for (i = 0; i < cs->channels; ++i) {
-                       bcs = cs->bcs + i;
-                       if (bcs->at_state.pending_commands ||
-                           bcs->at_state.cid > 0) {
-                               sequence = SEQ_NONE;
-                               break;
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&cs->lock, flags);
-       cs->at_state.pending_commands &= ~PC_UMMODE;
-       if (sequence != SEQ_NONE) {
-               schedule_sequence(cs, at_state, sequence);
-               return;
-       }
-
-       for (i = 0; i < cs->channels; ++i) {
-               bcs = cs->bcs + i;
-               if (bcs->at_state.pending_commands & PC_HUP) {
-                       if (cs->dle) {
-                               cs->curchannel = bcs->channel;
-                               schedule_sequence(cs, &cs->at_state, SEQ_DLE0);
-                               return;
-                       }
-                       bcs->at_state.pending_commands &= ~PC_HUP;
-                       if (bcs->at_state.pending_commands & PC_CID) {
-                               /* not yet dialing: PC_NOCID is sufficient */
-                               bcs->at_state.pending_commands |= PC_NOCID;
-                               bcs->at_state.pending_commands &= ~PC_CID;
-                       } else {
-                               schedule_sequence(cs, &bcs->at_state, SEQ_HUP);
-                               return;
-                       }
-               }
-               if (bcs->at_state.pending_commands & PC_NOCID) {
-                       bcs->at_state.pending_commands &= ~PC_NOCID;
-                       cs->curchannel = bcs->channel;
-                       schedule_sequence(cs, &cs->at_state, SEQ_NOCID);
-                       return;
-               } else if (bcs->at_state.pending_commands & PC_DLE0) {
-                       bcs->at_state.pending_commands &= ~PC_DLE0;
-                       cs->curchannel = bcs->channel;
-                       schedule_sequence(cs, &cs->at_state, SEQ_DLE0);
-                       return;
-               }
-       }
-
-       list_for_each_entry(at_state, &cs->temp_at_states, list)
-               if (at_state->pending_commands & PC_HUP) {
-                       at_state->pending_commands &= ~PC_HUP;
-                       schedule_sequence(cs, at_state, SEQ_HUP);
-                       return;
-               }
-
-       if (cs->at_state.pending_commands & PC_INIT) {
-               cs->at_state.pending_commands &= ~PC_INIT;
-               cs->dle = 0;
-               cs->inbuf->inputstate = INS_command;
-               schedule_sequence(cs, &cs->at_state, SEQ_INIT);
-               return;
-       }
-       if (cs->at_state.pending_commands & PC_SHUTDOWN) {
-               cs->at_state.pending_commands &= ~PC_SHUTDOWN;
-               schedule_sequence(cs, &cs->at_state, SEQ_SHUTDOWN);
-               return;
-       }
-       if (cs->at_state.pending_commands & PC_CIDMODE) {
-               cs->at_state.pending_commands &= ~PC_CIDMODE;
-               if (cs->mode == M_UNIMODEM) {
-                       cs->retry_count = 1;
-                       schedule_sequence(cs, &cs->at_state, SEQ_CIDMODE);
-                       return;
-               }
-       }
-
-       for (i = 0; i < cs->channels; ++i) {
-               bcs = cs->bcs + i;
-               if (bcs->at_state.pending_commands & PC_DLE1) {
-                       bcs->at_state.pending_commands &= ~PC_DLE1;
-                       cs->curchannel = bcs->channel;
-                       schedule_sequence(cs, &cs->at_state, SEQ_DLE1);
-                       return;
-               }
-               if (bcs->at_state.pending_commands & PC_ACCEPT) {
-                       bcs->at_state.pending_commands &= ~PC_ACCEPT;
-                       schedule_sequence(cs, &bcs->at_state, SEQ_ACCEPT);
-                       return;
-               }
-               if (bcs->at_state.pending_commands & PC_DIAL) {
-                       bcs->at_state.pending_commands &= ~PC_DIAL;
-                       schedule_sequence(cs, &bcs->at_state, SEQ_DIAL);
-                       return;
-               }
-               if (bcs->at_state.pending_commands & PC_CID) {
-                       switch (cs->mode) {
-                       case M_UNIMODEM:
-                               cs->at_state.pending_commands |= PC_CIDMODE;
-                               gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
-                               cs->commands_pending = 1;
-                               return;
-                       case M_UNKNOWN:
-                               schedule_init(cs, MS_INIT);
-                               return;
-                       }
-                       bcs->at_state.pending_commands &= ~PC_CID;
-                       cs->curchannel = bcs->channel;
-                       cs->retry_count = 2;
-                       schedule_sequence(cs, &cs->at_state, SEQ_CID);
-                       return;
-               }
-       }
-}
-
-static void process_events(struct cardstate *cs)
-{
-       struct event_t *ev;
-       unsigned head, tail;
-       int i;
-       int check_flags = 0;
-       int was_busy;
-       unsigned long flags;
-
-       spin_lock_irqsave(&cs->ev_lock, flags);
-       head = cs->ev_head;
-
-       for (i = 0; i < 2 * MAX_EVENTS; ++i) {
-               tail = cs->ev_tail;
-               if (tail == head) {
-                       if (!check_flags && !cs->commands_pending)
-                               break;
-                       check_flags = 0;
-                       spin_unlock_irqrestore(&cs->ev_lock, flags);
-                       process_command_flags(cs);
-                       spin_lock_irqsave(&cs->ev_lock, flags);
-                       tail = cs->ev_tail;
-                       if (tail == head) {
-                               if (!cs->commands_pending)
-                                       break;
-                               continue;
-                       }
-               }
-
-               ev = cs->events + head;
-               was_busy = cs->cur_at_seq != SEQ_NONE;
-               spin_unlock_irqrestore(&cs->ev_lock, flags);
-               process_event(cs, ev);
-               spin_lock_irqsave(&cs->ev_lock, flags);
-               kfree(ev->ptr);
-               ev->ptr = NULL;
-               if (was_busy && cs->cur_at_seq == SEQ_NONE)
-                       check_flags = 1;
-
-               head = (head + 1) % MAX_EVENTS;
-               cs->ev_head = head;
-       }
-
-       spin_unlock_irqrestore(&cs->ev_lock, flags);
-
-       if (i == 2 * MAX_EVENTS) {
-               dev_err(cs->dev,
-                       "infinite loop in process_events; aborting.\n");
-       }
-}
-
-/* tasklet scheduled on any event received from the Gigaset device
- * parameter:
- *     data    ISDN controller state structure
- */
-void gigaset_handle_event(unsigned long data)
-{
-       struct cardstate *cs = (struct cardstate *) data;
-
-       /* handle incoming data on control/common channel */
-       if (cs->inbuf->head != cs->inbuf->tail) {
-               gig_dbg(DEBUG_INTR, "processing new data");
-               cs->ops->handle_input(cs->inbuf);
-       }
-
-       process_events(cs);
-}
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
deleted file mode 100644 (file)
index 166537e..0000000
+++ /dev/null
@@ -1,830 +0,0 @@
-/*
- * Siemens Gigaset 307x driver
- * Common header file for all connection variants
- *
- * Written by Stefan Eilers
- *        and Hansjoerg Lipp <hjlipp@web.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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#ifndef GIGASET_H
-#define GIGASET_H
-
-/* define global prefix for pr_ macros in linux/kernel.h */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/compiler.h>
-#include <linux/types.h>
-#include <linux/ctype.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/ppp_defs.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/list.h>
-#include <linux/atomic.h>
-
-#define GIG_VERSION {0, 5, 0, 0}
-#define GIG_COMPAT  {0, 4, 0, 0}
-
-#define MAX_REC_PARAMS 10      /* Max. number of params in response string */
-#define MAX_RESP_SIZE 511      /* Max. size of a response string */
-
-#define MAX_EVENTS 64          /* size of event queue */
-
-#define RBUFSIZE 8192
-
-#define GIG_TICK 100           /* in milliseconds */
-
-/* timeout values (unit: 1 sec) */
-#define INIT_TIMEOUT 1
-
-/* timeout values (unit: 0.1 sec) */
-#define RING_TIMEOUT 3         /* for additional parameters to RING */
-#define BAS_TIMEOUT 20         /* for response to Base USB ops */
-#define ATRDY_TIMEOUT 3                /* for HD_READY_SEND_ATDATA */
-
-#define BAS_RETRY 3            /* max. retries for base USB ops */
-
-#define MAXACT 3
-
-extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */
-
-/* debug flags, combine by adding/bitwise OR */
-enum debuglevel {
-       DEBUG_INTR        = 0x00008, /* interrupt processing */
-       DEBUG_CMD         = 0x00020, /* sent/received LL commands */
-       DEBUG_STREAM      = 0x00040, /* application data stream I/O events */
-       DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */
-       DEBUG_LLDATA      = 0x00100, /* sent/received LL data */
-       DEBUG_EVENT       = 0x00200, /* event processing */
-       DEBUG_HDLC        = 0x00800, /* M10x HDLC processing */
-       DEBUG_CHANNEL     = 0x01000, /* channel allocation/deallocation */
-       DEBUG_TRANSCMD    = 0x02000, /* AT-COMMANDS+RESPONSES */
-       DEBUG_MCMD        = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */
-       DEBUG_INIT        = 0x08000, /* (de)allocation+initialization of data
-                                       structures */
-       DEBUG_SUSPEND     = 0x10000, /* suspend/resume processing */
-       DEBUG_OUTPUT      = 0x20000, /* output to device */
-       DEBUG_ISO         = 0x40000, /* isochronous transfers */
-       DEBUG_IF          = 0x80000, /* character device operations */
-       DEBUG_USBREQ      = 0x100000, /* USB communication (except payload
-                                        data) */
-       DEBUG_LOCKCMD     = 0x200000, /* AT commands and responses when
-                                        MS_LOCKED */
-
-       DEBUG_ANY         = 0x3fffff, /* print message if any of the others is
-                                        activated */
-};
-
-#ifdef CONFIG_GIGASET_DEBUG
-
-#define gig_dbg(level, format, arg...)                                 \
-       do {                                                            \
-               if (unlikely(((enum debuglevel)gigaset_debuglevel) & (level))) \
-                       printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \
-                              ## arg);                                 \
-       } while (0)
-#define DEBUG_DEFAULT (DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ)
-
-#else
-
-#define gig_dbg(level, format, arg...) do {} while (0)
-#define DEBUG_DEFAULT 0
-
-#endif
-
-void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
-                       size_t len, const unsigned char *buf);
-
-/* connection state */
-#define ZSAU_NONE                      0
-#define ZSAU_PROCEEDING                        1
-#define ZSAU_CALL_DELIVERED            2
-#define ZSAU_ACTIVE                    3
-#define ZSAU_DISCONNECT_IND            4
-#define ZSAU_NULL                      5
-#define ZSAU_DISCONNECT_REQ            6
-#define ZSAU_UNKNOWN                   -1
-
-/* USB control transfer requests */
-#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
-#define IN_VENDOR_REQ  (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
-
-/* interrupt pipe messages */
-#define HD_B1_FLOW_CONTROL             0x80
-#define HD_B2_FLOW_CONTROL             0x81
-#define HD_RECEIVEATDATA_ACK           (0x35)          /* 3070 */
-#define HD_READY_SEND_ATDATA           (0x36)          /* 3070 */
-#define HD_OPEN_ATCHANNEL_ACK          (0x37)          /* 3070 */
-#define HD_CLOSE_ATCHANNEL_ACK         (0x38)          /* 3070 */
-#define HD_DEVICE_INIT_OK              (0x11)          /* ISurf USB + 3070 */
-#define HD_OPEN_B1CHANNEL_ACK          (0x51)          /* ISurf USB + 3070 */
-#define HD_OPEN_B2CHANNEL_ACK          (0x52)          /* ISurf USB + 3070 */
-#define HD_CLOSE_B1CHANNEL_ACK         (0x53)          /* ISurf USB + 3070 */
-#define HD_CLOSE_B2CHANNEL_ACK         (0x54)          /* ISurf USB + 3070 */
-#define HD_SUSPEND_END                 (0x61)          /* ISurf USB */
-#define HD_RESET_INTERRUPT_PIPE_ACK    (0xFF)          /* ISurf USB + 3070 */
-
-/* control requests */
-#define        HD_OPEN_B1CHANNEL               (0x23)          /* ISurf USB + 3070 */
-#define        HD_CLOSE_B1CHANNEL              (0x24)          /* ISurf USB + 3070 */
-#define        HD_OPEN_B2CHANNEL               (0x25)          /* ISurf USB + 3070 */
-#define        HD_CLOSE_B2CHANNEL              (0x26)          /* ISurf USB + 3070 */
-#define HD_RESET_INTERRUPT_PIPE                (0x27)          /* ISurf USB + 3070 */
-#define        HD_DEVICE_INIT_ACK              (0x34)          /* ISurf USB + 3070 */
-#define        HD_WRITE_ATMESSAGE              (0x12)          /* 3070 */
-#define        HD_READ_ATMESSAGE               (0x13)          /* 3070 */
-#define        HD_OPEN_ATCHANNEL               (0x28)          /* 3070 */
-#define        HD_CLOSE_ATCHANNEL              (0x29)          /* 3070 */
-
-/* number of B channels supported by base driver */
-#define BAS_CHANNELS   2
-
-/* USB frames for isochronous transfer */
-#define BAS_FRAMETIME  1       /* number of milliseconds between frames */
-#define BAS_NUMFRAMES  8       /* number of frames per URB */
-#define BAS_MAXFRAME   16      /* allocated bytes per frame */
-#define BAS_NORMFRAME  8       /* send size without flow control */
-#define BAS_HIGHFRAME  10      /* "    "    with positive flow control */
-#define BAS_LOWFRAME   5       /* "    "    with negative flow control */
-#define BAS_CORRFRAMES 4       /* flow control multiplicator */
-
-#define BAS_INBUFSIZE  (BAS_MAXFRAME * BAS_NUMFRAMES)  /* size of isoc in buf
-                                                        * per URB */
-#define BAS_OUTBUFSIZE 4096            /* size of common isoc out buffer */
-#define BAS_OUTBUFPAD  BAS_MAXFRAME    /* size of pad area for isoc out buf */
-
-#define BAS_INURBS     3
-#define BAS_OUTURBS    3
-
-/* variable commands in struct bc_state */
-#define AT_ISO         0
-#define AT_DIAL                1
-#define AT_MSN         2
-#define AT_BC          3
-#define AT_PROTO       4
-#define AT_TYPE                5
-#define AT_CLIP                6
-/* total number */
-#define AT_NUM         7
-
-/* variables in struct at_state_t */
-/* - numeric */
-#define VAR_ZSAU       0
-#define VAR_ZDLE       1
-#define VAR_ZCTP       2
-/* total number */
-#define VAR_NUM                3
-/* - string */
-#define STR_NMBR       0
-#define STR_ZCPN       1
-#define STR_ZCON       2
-#define STR_ZBC                3
-#define STR_ZHLC       4
-/* total number */
-#define STR_NUM                5
-
-/* event types */
-#define EV_TIMEOUT     -105
-#define EV_IF_VER      -106
-#define EV_PROC_CIDMODE        -107
-#define EV_SHUTDOWN    -108
-#define EV_START       -110
-#define EV_STOP                -111
-#define EV_IF_LOCK     -112
-#define EV_ACCEPT      -114
-#define EV_DIAL                -115
-#define EV_HUP         -116
-#define EV_BC_OPEN     -117
-#define EV_BC_CLOSED   -118
-
-/* input state */
-#define INS_command    0x0001  /* receiving messages (not payload data) */
-#define INS_DLE_char   0x0002  /* DLE flag received (in DLE mode) */
-#define INS_byte_stuff 0x0004
-#define INS_have_data  0x0008
-#define INS_DLE_command        0x0020  /* DLE message start (<DLE> X) received */
-#define INS_flag_hunt  0x0040
-
-/* channel state */
-#define CHS_D_UP       0x01
-#define CHS_B_UP       0x02
-#define CHS_NOTIFY_LL  0x04
-
-#define ICALL_REJECT   0
-#define ICALL_ACCEPT   1
-#define ICALL_IGNORE   2
-
-/* device state */
-#define MS_UNINITIALIZED       0
-#define MS_INIT                        1
-#define MS_LOCKED              2
-#define MS_SHUTDOWN            3
-#define MS_RECOVER             4
-#define MS_READY               5
-
-/* mode */
-#define M_UNKNOWN      0
-#define M_CONFIG       1
-#define M_UNIMODEM     2
-#define M_CID          3
-
-/* start mode */
-#define SM_LOCKED      0
-#define SM_ISDN                1 /* default */
-
-/* layer 2 protocols (AT^SBPR=...) */
-#define L2_BITSYNC     0
-#define L2_HDLC                1
-#define L2_VOICE       2
-
-struct gigaset_ops;
-struct gigaset_driver;
-
-struct usb_cardstate;
-struct ser_cardstate;
-struct bas_cardstate;
-
-struct bc_state;
-struct usb_bc_state;
-struct ser_bc_state;
-struct bas_bc_state;
-
-struct reply_t {
-       int     resp_code;      /* RSP_XXXX */
-       int     min_ConState;   /* <0 => ignore */
-       int     max_ConState;   /* <0 => ignore */
-       int     parameter;      /* e.g. ZSAU_XXXX <0: ignore*/
-       int     new_ConState;   /* <0 => ignore */
-       int     timeout;        /* >0 => *HZ; <=0 => TOUT_XXXX*/
-       int     action[MAXACT]; /* ACT_XXXX */
-       char    *command;       /* NULL==none */
-};
-
-extern struct reply_t gigaset_tab_cid[];
-extern struct reply_t gigaset_tab_nocid[];
-
-struct inbuf_t {
-       struct cardstate        *cs;
-       int                     inputstate;
-       int                     head, tail;
-       unsigned char           data[RBUFSIZE];
-};
-
-/* isochronous write buffer structure
- * circular buffer with pad area for extraction of complete USB frames
- * - data[read..nextread-1] is valid data already submitted to the USB subsystem
- * - data[nextread..write-1] is valid data yet to be sent
- * - data[write] is the next byte to write to
- *   - in byte-oriented L2 procotols, it is completely free
- *   - in bit-oriented L2 procotols, it may contain a partial byte of valid data
- * - data[write+1..read-1] is free
- * - wbits is the number of valid data bits in data[write], starting at the LSB
- * - writesem is the semaphore for writing to the buffer:
- *   if writesem <= 0, data[write..read-1] is currently being written to
- * - idle contains the byte value to repeat when the end of valid data is
- *   reached; if nextread==write (buffer contains no data to send), either the
- *   BAS_OUTBUFPAD bytes immediately before data[write] (if
- *   write>=BAS_OUTBUFPAD) or those of the pad area (if write<BAS_OUTBUFPAD)
- *   are also filled with that value
- */
-struct isowbuf_t {
-       int             read;
-       int             nextread;
-       int             write;
-       atomic_t        writesem;
-       int             wbits;
-       unsigned char   data[BAS_OUTBUFSIZE + BAS_OUTBUFPAD];
-       unsigned char   idle;
-};
-
-/* isochronous write URB context structure
- * data to be stored along with the URB and retrieved when it is returned
- * as completed by the USB subsystem
- * - urb: pointer to the URB itself
- * - bcs: pointer to the B Channel control structure
- * - limit: end of write buffer area covered by this URB
- * - status: URB completion status
- */
-struct isow_urbctx_t {
-       struct urb *urb;
-       struct bc_state *bcs;
-       int limit;
-       int status;
-};
-
-/* AT state structure
- * data associated with the state of an ISDN connection, whether or not
- * it is currently assigned a B channel
- */
-struct at_state_t {
-       struct list_head        list;
-       int                     waiting;
-       int                     getstring;
-       unsigned                timer_index;
-       unsigned long           timer_expires;
-       int                     timer_active;
-       unsigned int            ConState;       /* State of connection */
-       struct reply_t          *replystruct;
-       int                     cid;
-       int                     int_var[VAR_NUM];       /* see VAR_XXXX */
-       char                    *str_var[STR_NUM];      /* see STR_XXXX */
-       unsigned                pending_commands;       /* see PC_XXXX */
-       unsigned                seq_index;
-
-       struct cardstate        *cs;
-       struct bc_state         *bcs;
-};
-
-struct event_t {
-       int type;
-       void *ptr, *arg;
-       int parameter;
-       int cid;
-       struct at_state_t *at_state;
-};
-
-/* This buffer holds all information about the used B-Channel */
-struct bc_state {
-       struct sk_buff *tx_skb;         /* Current transfer buffer to modem */
-       struct sk_buff_head squeue;     /* B-Channel send Queue */
-
-       /* Variables for debugging .. */
-       int corrupted;                  /* Counter for corrupted packages */
-       int trans_down;                 /* Counter of packages (downstream) */
-       int trans_up;                   /* Counter of packages (upstream) */
-
-       struct at_state_t at_state;
-
-       /* receive buffer */
-       unsigned rx_bufsize;            /* max size accepted by application */
-       struct sk_buff *rx_skb;
-       __u16 rx_fcs;
-       int inputstate;                 /* see INS_XXXX */
-
-       int channel;
-
-       struct cardstate *cs;
-
-       unsigned chstate;               /* bitmap (CHS_*) */
-       int ignore;
-       unsigned proto2;                /* layer 2 protocol (L2_*) */
-       char *commands[AT_NUM];         /* see AT_XXXX */
-
-#ifdef CONFIG_GIGASET_DEBUG
-       int emptycount;
-#endif
-       int busy;
-       int use_count;
-
-       /* private data of hardware drivers */
-       union {
-               struct ser_bc_state *ser;       /* serial hardware driver */
-               struct usb_bc_state *usb;       /* usb hardware driver (m105) */
-               struct bas_bc_state *bas;       /* usb hardware driver (base) */
-       } hw;
-
-       void *ap;                       /* associated LL application */
-       int apconnstate;                /* LL application connection state */
-       spinlock_t aplock;
-};
-
-struct cardstate {
-       struct gigaset_driver *driver;
-       unsigned minor_index;
-       struct device *dev;
-       struct device *tty_dev;
-       unsigned flags;
-
-       const struct gigaset_ops *ops;
-
-       /* Stuff to handle communication */
-       wait_queue_head_t waitqueue;
-       int waiting;
-       int mode;                       /* see M_XXXX */
-       int mstate;                     /* Modem state: see MS_XXXX */
-                                       /* only changed by the event layer */
-       int cmd_result;
-
-       int channels;
-       struct bc_state *bcs;           /* Array of struct bc_state */
-
-       int onechannel;                 /* data and commands transmitted in one
-                                          stream (M10x) */
-
-       spinlock_t lock;
-       struct at_state_t at_state;     /* at_state_t for cid == 0 */
-       struct list_head temp_at_states;/* list of temporary "struct
-                                          at_state_t"s without B channel */
-
-       struct inbuf_t *inbuf;
-
-       struct cmdbuf_t *cmdbuf, *lastcmdbuf;
-       spinlock_t cmdlock;
-       unsigned curlen, cmdbytes;
-
-       struct tty_port port;
-       struct tasklet_struct if_wake_tasklet;
-       unsigned control_state;
-
-       unsigned fwver[4];
-       int gotfwver;
-
-       unsigned running;               /* !=0 if events are handled */
-       unsigned connected;             /* !=0 if hardware is connected */
-       unsigned isdn_up;               /* !=0 after gigaset_isdn_start() */
-
-       unsigned cidmode;
-
-       int myid;                       /* id for communication with LL */
-       void *iif;                      /* LL interface structure */
-       unsigned short hw_hdr_len;      /* headroom needed in data skbs */
-
-       struct reply_t *tabnocid;
-       struct reply_t *tabcid;
-       int cs_init;
-       int ignoreframes;               /* frames to ignore after setting up the
-                                          B channel */
-       struct mutex mutex;             /* locks this structure:
-                                        *   connected is not changed,
-                                        *   hardware_up is not changed,
-                                        *   MState is not changed to or from
-                                        *   MS_LOCKED */
-
-       struct timer_list timer;
-       int retry_count;
-       int dle;                        /* !=0 if DLE mode is active
-                                          (ZDLE=1 received -- M10x only) */
-       int cur_at_seq;                 /* sequence of AT commands being
-                                          processed */
-       int curchannel;                 /* channel those commands are meant
-                                          for */
-       int commands_pending;           /* flag(s) in xxx.commands_pending have
-                                          been set */
-       struct tasklet_struct
-               event_tasklet;          /* tasklet for serializing AT commands.
-                                        * Scheduled
-                                        *   -> for modem reponses (and
-                                        *      incoming data for M10x)
-                                        *   -> on timeout
-                                        *   -> after setting bits in
-                                        *      xxx.at_state.pending_command
-                                        *      (e.g. command from LL) */
-       struct tasklet_struct
-               write_tasklet;          /* tasklet for serial output
-                                        * (not used in base driver) */
-
-       /* event queue */
-       struct event_t events[MAX_EVENTS];
-       unsigned ev_tail, ev_head;
-       spinlock_t ev_lock;
-
-       /* current modem response */
-       unsigned char respdata[MAX_RESP_SIZE + 1];
-       unsigned cbytes;
-
-       /* private data of hardware drivers */
-       union {
-               struct usb_cardstate *usb; /* USB hardware driver (m105) */
-               struct ser_cardstate *ser; /* serial hardware driver */
-               struct bas_cardstate *bas; /* USB hardware driver (base) */
-       } hw;
-};
-
-struct gigaset_driver {
-       struct list_head list;
-       spinlock_t lock;                /* locks minor tables and blocked */
-       struct tty_driver *tty;
-       unsigned have_tty;
-       unsigned minor;
-       unsigned minors;
-       struct cardstate *cs;
-       int blocked;
-
-       const struct gigaset_ops *ops;
-       struct module *owner;
-};
-
-struct cmdbuf_t {
-       struct cmdbuf_t *next, *prev;
-       int len, offset;
-       struct tasklet_struct *wake_tasklet;
-       unsigned char buf[0];
-};
-
-struct bas_bc_state {
-       /* isochronous output state */
-       int             running;
-       atomic_t        corrbytes;
-       spinlock_t      isooutlock;
-       struct isow_urbctx_t    isoouturbs[BAS_OUTURBS];
-       struct isow_urbctx_t    *isooutdone, *isooutfree, *isooutovfl;
-       struct isowbuf_t        *isooutbuf;
-       unsigned numsub;                /* submitted URB counter
-                                          (for diagnostic messages only) */
-       struct tasklet_struct   sent_tasklet;
-
-       /* isochronous input state */
-       spinlock_t isoinlock;
-       struct urb *isoinurbs[BAS_INURBS];
-       unsigned char isoinbuf[BAS_INBUFSIZE * BAS_INURBS];
-       struct urb *isoindone;          /* completed isoc read URB */
-       int isoinstatus;                /* status of completed URB */
-       int loststatus;                 /* status of dropped URB */
-       unsigned isoinlost;             /* number of bytes lost */
-       /* state of bit unstuffing algorithm
-          (in addition to BC_state.inputstate) */
-       unsigned seqlen;                /* number of '1' bits not yet
-                                          unstuffed */
-       unsigned inbyte, inbits;        /* collected bits for next byte */
-       /* statistics */
-       unsigned goodbytes;             /* bytes correctly received */
-       unsigned alignerrs;             /* frames with incomplete byte at end */
-       unsigned fcserrs;               /* FCS errors */
-       unsigned frameerrs;             /* framing errors */
-       unsigned giants;                /* long frames */
-       unsigned runts;                 /* short frames */
-       unsigned aborts;                /* HDLC aborts */
-       unsigned shared0s;              /* '0' bits shared between flags */
-       unsigned stolen0s;              /* '0' stuff bits also serving as
-                                          leading flag bits */
-       struct tasklet_struct rcvd_tasklet;
-};
-
-struct gigaset_ops {
-       /* Called from ev-layer.c/interface.c for sending AT commands to the
-          device */
-       int (*write_cmd)(struct cardstate *cs, struct cmdbuf_t *cb);
-
-       /* Called from interface.c for additional device control */
-       int (*write_room)(struct cardstate *cs);
-       int (*chars_in_buffer)(struct cardstate *cs);
-       int (*brkchars)(struct cardstate *cs, const unsigned char buf[6]);
-
-       /* Called from ev-layer.c after setting up connection
-        * Should call gigaset_bchannel_up(), when finished. */
-       int (*init_bchannel)(struct bc_state *bcs);
-
-       /* Called from ev-layer.c after hanging up
-        * Should call gigaset_bchannel_down(), when finished. */
-       int (*close_bchannel)(struct bc_state *bcs);
-
-       /* Called by gigaset_initcs() for setting up bcs->hw.xxx */
-       int (*initbcshw)(struct bc_state *bcs);
-
-       /* Called by gigaset_freecs() for freeing bcs->hw.xxx */
-       void (*freebcshw)(struct bc_state *bcs);
-
-       /* Called by gigaset_bchannel_down() for resetting bcs->hw.xxx */
-       void (*reinitbcshw)(struct bc_state *bcs);
-
-       /* Called by gigaset_initcs() for setting up cs->hw.xxx */
-       int (*initcshw)(struct cardstate *cs);
-
-       /* Called by gigaset_freecs() for freeing cs->hw.xxx */
-       void (*freecshw)(struct cardstate *cs);
-
-       /* Called from common.c/interface.c for additional serial port
-          control */
-       int (*set_modem_ctrl)(struct cardstate *cs, unsigned old_state,
-                             unsigned new_state);
-       int (*baud_rate)(struct cardstate *cs, unsigned cflag);
-       int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag);
-
-       /* Called from LL interface to put an skb into the send-queue.
-        * After sending is completed, gigaset_skb_sent() must be called
-        * with the skb's link layer header preserved. */
-       int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb);
-
-       /* Called from ev-layer.c to process a block of data
-        * received through the common/control channel. */
-       void (*handle_input)(struct inbuf_t *inbuf);
-
-};
-
-/* = Common structures and definitions =======================================
- */
-
-/* Parser states for DLE-Event:
- * <DLE-EVENT>: <DLE_FLAG> "X" <EVENT> <DLE_FLAG> "."
- * <DLE_FLAG>:  0x10
- * <EVENT>:     ((a-z)* | (A-Z)* | (0-10)*)+
- */
-#define DLE_FLAG       0x10
-
-/* ===========================================================================
- *  Functions implemented in asyncdata.c
- */
-
-/* Called from LL interface to put an skb into the send queue. */
-int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb);
-
-/* Called from ev-layer.c to process a block of data
- * received through the common/control channel. */
-void gigaset_m10x_input(struct inbuf_t *inbuf);
-
-/* ===========================================================================
- *  Functions implemented in isocdata.c
- */
-
-/* Called from LL interface to put an skb into the send queue. */
-int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb);
-
-/* Called from ev-layer.c to process a block of data
- * received through the common/control channel. */
-void gigaset_isoc_input(struct inbuf_t *inbuf);
-
-/* Called from bas-gigaset.c to process a block of data
- * received through the isochronous channel */
-void gigaset_isoc_receive(unsigned char *src, unsigned count,
-                         struct bc_state *bcs);
-
-/* Called from bas-gigaset.c to put a block of data
- * into the isochronous output buffer */
-int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len);
-
-/* Called from bas-gigaset.c to initialize the isochronous output buffer */
-void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle);
-
-/* Called from bas-gigaset.c to retrieve a block of bytes for sending */
-int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size);
-
-/* ===========================================================================
- *  Functions implemented in LL interface
- */
-
-/* Called from common.c for setting up/shutting down with the ISDN subsystem */
-void gigaset_isdn_regdrv(void);
-void gigaset_isdn_unregdrv(void);
-int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid);
-void gigaset_isdn_unregdev(struct cardstate *cs);
-
-/* Called from hardware module to indicate completion of an skb */
-void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
-void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb);
-void gigaset_isdn_rcv_err(struct bc_state *bcs);
-
-/* Called from common.c/ev-layer.c to indicate events relevant to the LL */
-void gigaset_isdn_start(struct cardstate *cs);
-void gigaset_isdn_stop(struct cardstate *cs);
-int gigaset_isdn_icall(struct at_state_t *at_state);
-void gigaset_isdn_connD(struct bc_state *bcs);
-void gigaset_isdn_hupD(struct bc_state *bcs);
-void gigaset_isdn_connB(struct bc_state *bcs);
-void gigaset_isdn_hupB(struct bc_state *bcs);
-
-/* ===========================================================================
- *  Functions implemented in ev-layer.c
- */
-
-/* tasklet called from common.c to process queued events */
-void gigaset_handle_event(unsigned long data);
-
-/* called from isocdata.c / asyncdata.c
- * when a complete modem response line has been received */
-void gigaset_handle_modem_response(struct cardstate *cs);
-
-/* ===========================================================================
- *  Functions implemented in proc.c
- */
-
-/* initialize sysfs for device */
-void gigaset_init_dev_sysfs(struct cardstate *cs);
-void gigaset_free_dev_sysfs(struct cardstate *cs);
-
-/* ===========================================================================
- *  Functions implemented in common.c/gigaset.h
- */
-
-void gigaset_bcs_reinit(struct bc_state *bcs);
-void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
-                    struct cardstate *cs, int cid);
-int gigaset_get_channel(struct bc_state *bcs);
-struct bc_state *gigaset_get_free_channel(struct cardstate *cs);
-void gigaset_free_channel(struct bc_state *bcs);
-int gigaset_get_channels(struct cardstate *cs);
-void gigaset_free_channels(struct cardstate *cs);
-void gigaset_block_channels(struct cardstate *cs);
-
-/* Allocate and initialize driver structure. */
-struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
-                                         const char *procname,
-                                         const char *devname,
-                                         const struct gigaset_ops *ops,
-                                         struct module *owner);
-
-/* Deallocate driver structure. */
-void gigaset_freedriver(struct gigaset_driver *drv);
-
-struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty);
-struct cardstate *gigaset_get_cs_by_id(int id);
-void gigaset_blockdriver(struct gigaset_driver *drv);
-
-/* Allocate and initialize card state. Calls hardware dependent
-   gigaset_init[b]cs(). */
-struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
-                                int onechannel, int ignoreframes,
-                                int cidmode, const char *modulename);
-
-/* Free card state. Calls hardware dependent gigaset_free[b]cs(). */
-void gigaset_freecs(struct cardstate *cs);
-
-/* Tell common.c that hardware and driver are ready. */
-int gigaset_start(struct cardstate *cs);
-
-/* Tell common.c that the device is not present any more. */
-void gigaset_stop(struct cardstate *cs);
-
-/* Tell common.c that the driver is being unloaded. */
-int gigaset_shutdown(struct cardstate *cs);
-
-/* Append event to the queue.
- * Returns NULL on failure or a pointer to the event on success.
- * ptr must be kmalloc()ed (and not be freed by the caller).
- */
-struct event_t *gigaset_add_event(struct cardstate *cs,
-                                 struct at_state_t *at_state, int type,
-                                 void *ptr, int parameter, void *arg);
-
-/* Called on CONFIG1 command from frontend. */
-int gigaset_enterconfigmode(struct cardstate *cs);
-
-/* cs->lock must not be locked */
-static inline void gigaset_schedule_event(struct cardstate *cs)
-{
-       unsigned long flags;
-       spin_lock_irqsave(&cs->lock, flags);
-       if (cs->running)
-               tasklet_schedule(&cs->event_tasklet);
-       spin_unlock_irqrestore(&cs->lock, flags);
-}
-
-/* Tell common.c that B channel has been closed. */
-/* cs->lock must not be locked */
-static inline void gigaset_bchannel_down(struct bc_state *bcs)
-{
-       gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_CLOSED, NULL, 0, NULL);
-       gigaset_schedule_event(bcs->cs);
-}
-
-/* Tell common.c that B channel has been opened. */
-/* cs->lock must not be locked */
-static inline void gigaset_bchannel_up(struct bc_state *bcs)
-{
-       gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_OPEN, NULL, 0, NULL);
-       gigaset_schedule_event(bcs->cs);
-}
-
-/* set up next receive skb for data mode */
-static inline struct sk_buff *gigaset_new_rx_skb(struct bc_state *bcs)
-{
-       struct cardstate *cs = bcs->cs;
-       unsigned short hw_hdr_len = cs->hw_hdr_len;
-
-       if (bcs->ignore) {
-               bcs->rx_skb = NULL;
-       } else {
-               bcs->rx_skb = dev_alloc_skb(bcs->rx_bufsize + hw_hdr_len);
-               if (bcs->rx_skb == NULL)
-                       dev_warn(cs->dev, "could not allocate skb\n");
-               else
-                       skb_reserve(bcs->rx_skb, hw_hdr_len);
-       }
-       return bcs->rx_skb;
-}
-
-/* append received bytes to inbuf */
-int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
-                      unsigned numbytes);
-
-/* ===========================================================================
- *  Functions implemented in interface.c
- */
-
-/* initialize interface */
-void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
-                          const char *devname);
-/* release interface */
-void gigaset_if_freedriver(struct gigaset_driver *drv);
-/* add minor */
-void gigaset_if_init(struct cardstate *cs);
-/* remove minor */
-void gigaset_if_free(struct cardstate *cs);
-/* device received data */
-void gigaset_if_receive(struct cardstate *cs,
-                       unsigned char *buffer, size_t len);
-
-#endif
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
deleted file mode 100644 (file)
index d9a578a..0000000
+++ /dev/null
@@ -1,616 +0,0 @@
-/*
- * interface to user space for the gigaset driver
- *
- * Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.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 of
- *    the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/gigaset_dev.h>
-#include <linux/tty_flip.h>
-#include <linux/module.h>
-
-/*** our ioctls ***/
-
-static int if_lock(struct cardstate *cs, int *arg)
-{
-       int cmd = *arg;
-
-       gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
-
-       if (cmd > 1)
-               return -EINVAL;
-
-       if (cmd < 0) {
-               *arg = cs->mstate == MS_LOCKED;
-               return 0;
-       }
-
-       if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
-               cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
-               cs->ops->baud_rate(cs, B115200);
-               cs->ops->set_line_ctrl(cs, CS8);
-               cs->control_state = TIOCM_DTR | TIOCM_RTS;
-       }
-
-       cs->waiting = 1;
-       if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
-                              NULL, cmd, NULL)) {
-               cs->waiting = 0;
-               return -ENOMEM;
-       }
-       gigaset_schedule_event(cs);
-
-       wait_event(cs->waitqueue, !cs->waiting);
-
-       if (cs->cmd_result >= 0) {
-               *arg = cs->cmd_result;
-               return 0;
-       }
-
-       return cs->cmd_result;
-}
-
-static int if_version(struct cardstate *cs, unsigned arg[4])
-{
-       static const unsigned version[4] = GIG_VERSION;
-       static const unsigned compat[4] = GIG_COMPAT;
-       unsigned cmd = arg[0];
-
-       gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
-
-       switch (cmd) {
-       case GIGVER_DRIVER:
-               memcpy(arg, version, sizeof version);
-               return 0;
-       case GIGVER_COMPAT:
-               memcpy(arg, compat, sizeof compat);
-               return 0;
-       case GIGVER_FWBASE:
-               cs->waiting = 1;
-               if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
-                                      NULL, 0, arg)) {
-                       cs->waiting = 0;
-                       return -ENOMEM;
-               }
-               gigaset_schedule_event(cs);
-
-               wait_event(cs->waitqueue, !cs->waiting);
-
-               if (cs->cmd_result >= 0)
-                       return 0;
-
-               return cs->cmd_result;
-       default:
-               return -EINVAL;
-       }
-}
-
-static int if_config(struct cardstate *cs, int *arg)
-{
-       gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
-
-       if (*arg != 1)
-               return -EINVAL;
-
-       if (cs->mstate != MS_LOCKED)
-               return -EBUSY;
-
-       if (!cs->connected) {
-               pr_err("%s: not connected\n", __func__);
-               return -ENODEV;
-       }
-
-       *arg = 0;
-       return gigaset_enterconfigmode(cs);
-}
-
-/*** the terminal driver ***/
-
-static int if_open(struct tty_struct *tty, struct file *filp)
-{
-       struct cardstate *cs;
-
-       gig_dbg(DEBUG_IF, "%d+%d: %s()",
-               tty->driver->minor_start, tty->index, __func__);
-
-       cs = gigaset_get_cs_by_tty(tty);
-       if (!cs || !try_module_get(cs->driver->owner))
-               return -ENODEV;
-
-       if (mutex_lock_interruptible(&cs->mutex)) {
-               module_put(cs->driver->owner);
-               return -ERESTARTSYS;
-       }
-       tty->driver_data = cs;
-
-       ++cs->port.count;
-
-       if (cs->port.count == 1) {
-               tty_port_tty_set(&cs->port, tty);
-               cs->port.low_latency = 1;
-       }
-
-       mutex_unlock(&cs->mutex);
-       return 0;
-}
-
-static void if_close(struct tty_struct *tty, struct file *filp)
-{
-       struct cardstate *cs = tty->driver_data;
-
-       if (!cs) { /* happens if we didn't find cs in open */
-               gig_dbg(DEBUG_IF, "%s: no cardstate", __func__);
-               return;
-       }
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       mutex_lock(&cs->mutex);
-
-       if (!cs->connected)
-               gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
-       else if (!cs->port.count)
-               dev_warn(cs->dev, "%s: device not opened\n", __func__);
-       else if (!--cs->port.count)
-               tty_port_tty_set(&cs->port, NULL);
-
-       mutex_unlock(&cs->mutex);
-
-       module_put(cs->driver->owner);
-}
-
-static int if_ioctl(struct tty_struct *tty,
-                   unsigned int cmd, unsigned long arg)
-{
-       struct cardstate *cs = tty->driver_data;
-       int retval = -ENODEV;
-       int int_arg;
-       unsigned char buf[6];
-       unsigned version[4];
-
-       gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
-
-       if (mutex_lock_interruptible(&cs->mutex))
-               return -ERESTARTSYS;
-
-       if (!cs->connected) {
-               gig_dbg(DEBUG_IF, "not connected");
-               retval = -ENODEV;
-       } else {
-               retval = 0;
-               switch (cmd) {
-               case GIGASET_REDIR:
-                       retval = get_user(int_arg, (int __user *) arg);
-                       if (retval >= 0)
-                               retval = if_lock(cs, &int_arg);
-                       if (retval >= 0)
-                               retval = put_user(int_arg, (int __user *) arg);
-                       break;
-               case GIGASET_CONFIG:
-                       retval = get_user(int_arg, (int __user *) arg);
-                       if (retval >= 0)
-                               retval = if_config(cs, &int_arg);
-                       if (retval >= 0)
-                               retval = put_user(int_arg, (int __user *) arg);
-                       break;
-               case GIGASET_BRKCHARS:
-                       retval = copy_from_user(&buf,
-                                               (const unsigned char __user *) arg, 6)
-                               ? -EFAULT : 0;
-                       if (retval >= 0) {
-                               gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
-                                                  6, buf);
-                               retval = cs->ops->brkchars(cs, buf);
-                       }
-                       break;
-               case GIGASET_VERSION:
-                       retval = copy_from_user(version,
-                                               (unsigned __user *) arg, sizeof version)
-                               ? -EFAULT : 0;
-                       if (retval >= 0)
-                               retval = if_version(cs, version);
-                       if (retval >= 0)
-                               retval = copy_to_user((unsigned __user *) arg,
-                                                     version, sizeof version)
-                                       ? -EFAULT : 0;
-                       break;
-               default:
-                       gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
-                               __func__, cmd);
-                       retval = -ENOIOCTLCMD;
-               }
-       }
-
-       mutex_unlock(&cs->mutex);
-
-       return retval;
-}
-
-#ifdef CONFIG_COMPAT
-static long if_compat_ioctl(struct tty_struct *tty,
-                   unsigned int cmd, unsigned long arg)
-{
-       return if_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
-}
-#endif
-
-static int if_tiocmget(struct tty_struct *tty)
-{
-       struct cardstate *cs = tty->driver_data;
-       int retval;
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       if (mutex_lock_interruptible(&cs->mutex))
-               return -ERESTARTSYS;
-
-       retval = cs->control_state & (TIOCM_RTS | TIOCM_DTR);
-
-       mutex_unlock(&cs->mutex);
-
-       return retval;
-}
-
-static int if_tiocmset(struct tty_struct *tty,
-                      unsigned int set, unsigned int clear)
-{
-       struct cardstate *cs = tty->driver_data;
-       int retval;
-       unsigned mc;
-
-       gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
-               cs->minor_index, __func__, set, clear);
-
-       if (mutex_lock_interruptible(&cs->mutex))
-               return -ERESTARTSYS;
-
-       if (!cs->connected) {
-               gig_dbg(DEBUG_IF, "not connected");
-               retval = -ENODEV;
-       } else {
-               mc = (cs->control_state | set) & ~clear & (TIOCM_RTS | TIOCM_DTR);
-               retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
-               cs->control_state = mc;
-       }
-
-       mutex_unlock(&cs->mutex);
-
-       return retval;
-}
-
-static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-       struct cardstate *cs = tty->driver_data;
-       struct cmdbuf_t *cb;
-       int retval;
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       if (mutex_lock_interruptible(&cs->mutex))
-               return -ERESTARTSYS;
-
-       if (!cs->connected) {
-               gig_dbg(DEBUG_IF, "not connected");
-               retval = -ENODEV;
-               goto done;
-       }
-       if (cs->mstate != MS_LOCKED) {
-               dev_warn(cs->dev, "can't write to unlocked device\n");
-               retval = -EBUSY;
-               goto done;
-       }
-       if (count <= 0) {
-               /* nothing to do */
-               retval = 0;
-               goto done;
-       }
-
-       cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
-       if (!cb) {
-               dev_err(cs->dev, "%s: out of memory\n", __func__);
-               retval = -ENOMEM;
-               goto done;
-       }
-
-       memcpy(cb->buf, buf, count);
-       cb->len = count;
-       cb->offset = 0;
-       cb->next = NULL;
-       cb->wake_tasklet = &cs->if_wake_tasklet;
-       retval = cs->ops->write_cmd(cs, cb);
-done:
-       mutex_unlock(&cs->mutex);
-       return retval;
-}
-
-static int if_write_room(struct tty_struct *tty)
-{
-       struct cardstate *cs = tty->driver_data;
-       int retval;
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       if (mutex_lock_interruptible(&cs->mutex))
-               return -ERESTARTSYS;
-
-       if (!cs->connected) {
-               gig_dbg(DEBUG_IF, "not connected");
-               retval = -ENODEV;
-       } else if (cs->mstate != MS_LOCKED) {
-               dev_warn(cs->dev, "can't write to unlocked device\n");
-               retval = -EBUSY;
-       } else
-               retval = cs->ops->write_room(cs);
-
-       mutex_unlock(&cs->mutex);
-
-       return retval;
-}
-
-static int if_chars_in_buffer(struct tty_struct *tty)
-{
-       struct cardstate *cs = tty->driver_data;
-       int retval = 0;
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       mutex_lock(&cs->mutex);
-
-       if (!cs->connected)
-               gig_dbg(DEBUG_IF, "not connected");
-       else if (cs->mstate != MS_LOCKED)
-               dev_warn(cs->dev, "can't write to unlocked device\n");
-       else
-               retval = cs->ops->chars_in_buffer(cs);
-
-       mutex_unlock(&cs->mutex);
-
-       return retval;
-}
-
-static void if_throttle(struct tty_struct *tty)
-{
-       struct cardstate *cs = tty->driver_data;
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       mutex_lock(&cs->mutex);
-
-       if (!cs->connected)
-               gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
-       else
-               gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
-
-       mutex_unlock(&cs->mutex);
-}
-
-static void if_unthrottle(struct tty_struct *tty)
-{
-       struct cardstate *cs = tty->driver_data;
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       mutex_lock(&cs->mutex);
-
-       if (!cs->connected)
-               gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
-       else
-               gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
-
-       mutex_unlock(&cs->mutex);
-}
-
-static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
-{
-       struct cardstate *cs = tty->driver_data;
-       unsigned int iflag;
-       unsigned int cflag;
-       unsigned int old_cflag;
-       unsigned int control_state, new_state;
-
-       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
-
-       mutex_lock(&cs->mutex);
-
-       if (!cs->connected) {
-               gig_dbg(DEBUG_IF, "not connected");
-               goto out;
-       }
-
-       iflag = tty->termios.c_iflag;
-       cflag = tty->termios.c_cflag;
-       old_cflag = old ? old->c_cflag : cflag;
-       gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
-               cs->minor_index, iflag, cflag, old_cflag);
-
-       /* get a local copy of the current port settings */
-       control_state = cs->control_state;
-
-       /*
-        * Update baud rate.
-        * Do not attempt to cache old rates and skip settings,
-        * disconnects screw such tricks up completely.
-        * Premature optimization is the root of all evil.
-        */
-
-       /* reassert DTR and (maybe) RTS on transition from B0 */
-       if ((old_cflag & CBAUD) == B0) {
-               new_state = control_state | TIOCM_DTR;
-               /* don't set RTS if using hardware flow control */
-               if (!(old_cflag & CRTSCTS))
-                       new_state |= TIOCM_RTS;
-               gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
-                       cs->minor_index,
-                       (new_state & TIOCM_RTS) ? " only" : "/RTS");
-               cs->ops->set_modem_ctrl(cs, control_state, new_state);
-               control_state = new_state;
-       }
-
-       cs->ops->baud_rate(cs, cflag & CBAUD);
-
-       if ((cflag & CBAUD) == B0) {
-               /* Drop RTS and DTR */
-               gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
-               new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
-               cs->ops->set_modem_ctrl(cs, control_state, new_state);
-               control_state = new_state;
-       }
-
-       /*
-        * Update line control register (LCR)
-        */
-
-       cs->ops->set_line_ctrl(cs, cflag);
-
-       /* save off the modified port settings */
-       cs->control_state = control_state;
-
-out:
-       mutex_unlock(&cs->mutex);
-}
-
-static const struct tty_operations if_ops = {
-       .open =                 if_open,
-       .close =                if_close,
-       .ioctl =                if_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl =         if_compat_ioctl,
-#endif
-       .write =                if_write,
-       .write_room =           if_write_room,
-       .chars_in_buffer =      if_chars_in_buffer,
-       .set_termios =          if_set_termios,
-       .throttle =             if_throttle,
-       .unthrottle =           if_unthrottle,
-       .tiocmget =             if_tiocmget,
-       .tiocmset =             if_tiocmset,
-};
-
-
-/* wakeup tasklet for the write operation */
-static void if_wake(unsigned long data)
-{
-       struct cardstate *cs = (struct cardstate *)data;
-
-       tty_port_tty_wakeup(&cs->port);
-}
-
-/*** interface to common ***/
-
-void gigaset_if_init(struct cardstate *cs)
-{
-       struct gigaset_driver *drv;
-
-       drv = cs->driver;
-       if (!drv->have_tty)
-               return;
-
-       tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
-
-       mutex_lock(&cs->mutex);
-       cs->tty_dev = tty_port_register_device(&cs->port, drv->tty,
-                       cs->minor_index, NULL);
-
-       if (!IS_ERR(cs->tty_dev))
-               dev_set_drvdata(cs->tty_dev, cs);
-       else {
-               pr_warning("could not register device to the tty subsystem\n");
-               cs->tty_dev = NULL;
-       }
-       mutex_unlock(&cs->mutex);
-}
-
-void gigaset_if_free(struct cardstate *cs)
-{
-       struct gigaset_driver *drv;
-
-       drv = cs->driver;
-       if (!drv->have_tty)
-               return;
-
-       tasklet_disable(&cs->if_wake_tasklet);
-       tasklet_kill(&cs->if_wake_tasklet);
-       cs->tty_dev = NULL;
-       tty_unregister_device(drv->tty, cs->minor_index);
-}
-
-/**
- * gigaset_if_receive() - pass a received block of data to the tty device
- * @cs:                device descriptor structure.
- * @buffer:    received data.
- * @len:       number of bytes received.
- *
- * Called by asyncdata/isocdata if a block of data received from the
- * device must be sent to userspace through the ttyG* device.
- */
-void gigaset_if_receive(struct cardstate *cs,
-                       unsigned char *buffer, size_t len)
-{
-       tty_insert_flip_string(&cs->port, buffer, len);
-       tty_flip_buffer_push(&cs->port);
-}
-EXPORT_SYMBOL_GPL(gigaset_if_receive);
-
-/* gigaset_if_initdriver
- * Initialize tty interface.
- * parameters:
- *     drv             Driver
- *     procname        Name of the driver (e.g. for /proc/tty/drivers)
- *     devname         Name of the device files (prefix without minor number)
- */
-void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
-                          const char *devname)
-{
-       int ret;
-       struct tty_driver *tty;
-
-       drv->have_tty = 0;
-
-       drv->tty = tty = alloc_tty_driver(drv->minors);
-       if (tty == NULL)
-               goto enomem;
-
-       tty->type =             TTY_DRIVER_TYPE_SERIAL;
-       tty->subtype =          SERIAL_TYPE_NORMAL;
-       tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
-
-       tty->driver_name =      procname;
-       tty->name =             devname;
-       tty->minor_start =      drv->minor;
-
-       tty->init_termios          = tty_std_termios;
-       tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       tty_set_operations(tty, &if_ops);
-
-       ret = tty_register_driver(tty);
-       if (ret < 0) {
-               pr_err("error %d registering tty driver\n", ret);
-               goto error;
-       }
-       gig_dbg(DEBUG_IF, "tty driver initialized");
-       drv->have_tty = 1;
-       return;
-
-enomem:
-       pr_err("out of memory\n");
-error:
-       if (drv->tty)
-               put_tty_driver(drv->tty);
-}
-
-void gigaset_if_freedriver(struct gigaset_driver *drv)
-{
-       if (!drv->have_tty)
-               return;
-
-       drv->have_tty = 0;
-       tty_unregister_driver(drv->tty);
-       put_tty_driver(drv->tty);
-}
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
deleted file mode 100644 (file)
index f9264ba..0000000
+++ /dev/null
@@ -1,1009 +0,0 @@
-/*
- * Common data handling layer for bas_gigaset
- *
- * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
- *                       Hansjoerg Lipp <hjlipp@web.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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/crc-ccitt.h>
-#include <linux/bitrev.h>
-
-/* access methods for isowbuf_t */
-/* ============================ */
-
-/* initialize buffer structure
- */
-void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle)
-{
-       iwb->read = 0;
-       iwb->nextread = 0;
-       iwb->write = 0;
-       atomic_set(&iwb->writesem, 1);
-       iwb->wbits = 0;
-       iwb->idle = idle;
-       memset(iwb->data + BAS_OUTBUFSIZE, idle, BAS_OUTBUFPAD);
-}
-
-/* compute number of bytes which can be appended to buffer
- * so that there is still room to append a maximum frame of flags
- */
-static inline int isowbuf_freebytes(struct isowbuf_t *iwb)
-{
-       int read, write, freebytes;
-
-       read = iwb->read;
-       write = iwb->write;
-       freebytes = read - write;
-       if (freebytes > 0) {
-               /* no wraparound: need padding space within regular area */
-               return freebytes - BAS_OUTBUFPAD;
-       } else if (read < BAS_OUTBUFPAD) {
-               /* wraparound: can use space up to end of regular area */
-               return BAS_OUTBUFSIZE - write;
-       } else {
-               /* following the wraparound yields more space */
-               return freebytes + BAS_OUTBUFSIZE - BAS_OUTBUFPAD;
-       }
-}
-
-/* start writing
- * acquire the write semaphore
- * return 0 if acquired, <0 if busy
- */
-static inline int isowbuf_startwrite(struct isowbuf_t *iwb)
-{
-       if (!atomic_dec_and_test(&iwb->writesem)) {
-               atomic_inc(&iwb->writesem);
-               gig_dbg(DEBUG_ISO, "%s: couldn't acquire iso write semaphore",
-                       __func__);
-               return -EBUSY;
-       }
-       gig_dbg(DEBUG_ISO,
-               "%s: acquired iso write semaphore, data[write]=%02x, nbits=%d",
-               __func__, iwb->data[iwb->write], iwb->wbits);
-       return 0;
-}
-
-/* finish writing
- * release the write semaphore
- * returns the current write position
- */
-static inline int isowbuf_donewrite(struct isowbuf_t *iwb)
-{
-       int write = iwb->write;
-       atomic_inc(&iwb->writesem);
-       return write;
-}
-
-/* append bits to buffer without any checks
- * - data contains bits to append, starting at LSB
- * - nbits is number of bits to append (0..24)
- * must be called with the write semaphore held
- * If more than nbits bits are set in data, the extraneous bits are set in the
- * buffer too, but the write position is only advanced by nbits.
- */
-static inline void isowbuf_putbits(struct isowbuf_t *iwb, u32 data, int nbits)
-{
-       int write = iwb->write;
-       data <<= iwb->wbits;
-       data |= iwb->data[write];
-       nbits += iwb->wbits;
-       while (nbits >= 8) {
-               iwb->data[write++] = data & 0xff;
-               write %= BAS_OUTBUFSIZE;
-               data >>= 8;
-               nbits -= 8;
-       }
-       iwb->wbits = nbits;
-       iwb->data[write] = data & 0xff;
-       iwb->write = write;
-}
-
-/* put final flag on HDLC bitstream
- * also sets the idle fill byte to the correspondingly shifted flag pattern
- * must be called with the write semaphore held
- */
-static inline void isowbuf_putflag(struct isowbuf_t *iwb)
-{
-       int write;
-
-       /* add two flags, thus reliably covering one byte */
-       isowbuf_putbits(iwb, 0x7e7e, 8);
-       /* recover the idle flag byte */
-       write = iwb->write;
-       iwb->idle = iwb->data[write];
-       gig_dbg(DEBUG_ISO, "idle fill byte %02x", iwb->idle);
-       /* mask extraneous bits in buffer */
-       iwb->data[write] &= (1 << iwb->wbits) - 1;
-}
-
-/* retrieve a block of bytes for sending
- * The requested number of bytes is provided as a contiguous block.
- * If necessary, the frame is filled to the requested number of bytes
- * with the idle value.
- * returns offset to frame, < 0 on busy or error
- */
-int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size)
-{
-       int read, write, limit, src, dst;
-       unsigned char pbyte;
-
-       read = iwb->nextread;
-       write = iwb->write;
-       if (likely(read == write)) {
-               /* return idle frame */
-               return read < BAS_OUTBUFPAD ?
-                       BAS_OUTBUFSIZE : read - BAS_OUTBUFPAD;
-       }
-
-       limit = read + size;
-       gig_dbg(DEBUG_STREAM, "%s: read=%d write=%d limit=%d",
-               __func__, read, write, limit);
-#ifdef CONFIG_GIGASET_DEBUG
-       if (unlikely(size < 0 || size > BAS_OUTBUFPAD)) {
-               pr_err("invalid size %d\n", size);
-               return -EINVAL;
-       }
-#endif
-
-       if (read < write) {
-               /* no wraparound in valid data */
-               if (limit >= write) {
-                       /* append idle frame */
-                       if (isowbuf_startwrite(iwb) < 0)
-                               return -EBUSY;
-                       /* write position could have changed */
-                       write = iwb->write;
-                       if (limit >= write) {
-                               pbyte = iwb->data[write]; /* save
-                                                            partial byte */
-                               limit = write + BAS_OUTBUFPAD;
-                               gig_dbg(DEBUG_STREAM,
-                                       "%s: filling %d->%d with %02x",
-                                       __func__, write, limit, iwb->idle);
-                               if (write + BAS_OUTBUFPAD < BAS_OUTBUFSIZE)
-                                       memset(iwb->data + write, iwb->idle,
-                                              BAS_OUTBUFPAD);
-                               else {
-                                       /* wraparound, fill entire pad area */
-                                       memset(iwb->data + write, iwb->idle,
-                                              BAS_OUTBUFSIZE + BAS_OUTBUFPAD
-                                              - write);
-                                       limit = 0;
-                               }
-                               gig_dbg(DEBUG_STREAM,
-                                       "%s: restoring %02x at %d",
-                                       __func__, pbyte, limit);
-                               iwb->data[limit] = pbyte; /* restore
-                                                            partial byte */
-                               iwb->write = limit;
-                       }
-                       isowbuf_donewrite(iwb);
-               }
-       } else {
-               /* valid data wraparound */
-               if (limit >= BAS_OUTBUFSIZE) {
-                       /* copy wrapped part into pad area */
-                       src = 0;
-                       dst = BAS_OUTBUFSIZE;
-                       while (dst < limit && src < write)
-                               iwb->data[dst++] = iwb->data[src++];
-                       if (dst <= limit) {
-                               /* fill pad area with idle byte */
-                               memset(iwb->data + dst, iwb->idle,
-                                      BAS_OUTBUFSIZE + BAS_OUTBUFPAD - dst);
-                       }
-                       limit = src;
-               }
-       }
-       iwb->nextread = limit;
-       return read;
-}
-
-/* dump_bytes
- * write hex bytes to syslog for debugging
- */
-static inline void dump_bytes(enum debuglevel level, const char *tag,
-                             unsigned char *bytes, int count)
-{
-#ifdef CONFIG_GIGASET_DEBUG
-       unsigned char c;
-       static char dbgline[3 * 32 + 1];
-       int i = 0;
-
-       if (!(gigaset_debuglevel & level))
-               return;
-
-       while (count-- > 0) {
-               if (i > sizeof(dbgline) - 4) {
-                       dbgline[i] = '\0';
-                       gig_dbg(level, "%s:%s", tag, dbgline);
-                       i = 0;
-               }
-               c = *bytes++;
-               dbgline[i] = (i && !(i % 12)) ? '-' : ' ';
-               i++;
-               dbgline[i++] = hex_asc_hi(c);
-               dbgline[i++] = hex_asc_lo(c);
-       }
-       dbgline[i] = '\0';
-       gig_dbg(level, "%s:%s", tag, dbgline);
-#endif
-}
-
-/*============================================================================*/
-
-/* bytewise HDLC bitstuffing via table lookup
- * lookup table: 5 subtables for 0..4 preceding consecutive '1' bits
- * index: 256*(number of preceding '1' bits) + (next byte to stuff)
- * value: bit  9.. 0 = result bits
- *        bit 12..10 = number of trailing '1' bits in result
- *        bit 14..13 = number of bits added by stuffing
- */
-static const u16 stufftab[5 * 256] = {
-/* previous 1s = 0: */
-       0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
-       0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x201f,
-       0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
-       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x205f,
-       0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
-       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x209f,
-       0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
-       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20df,
-       0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f,
-       0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x251f,
-       0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af,
-       0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x255f,
-       0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x08c7, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x08cf,
-       0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x08d7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x299f,
-       0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x0cef,
-       0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x2ddf,
-
-/* previous 1s = 1: */
-       0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x200f,
-       0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x202f,
-       0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x204f,
-       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x206f,
-       0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x208f,
-       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x20af,
-       0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x20cf,
-       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20ef,
-       0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x250f,
-       0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x252f,
-       0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x254f,
-       0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x256f,
-       0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x08c7, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x298f,
-       0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x08d7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x29af,
-       0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dcf,
-       0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x31ef,
-
-/* previous 1s = 2: */
-       0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x2007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x2017,
-       0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x2027, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x2037,
-       0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x2047, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x2057,
-       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x2067, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x2077,
-       0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x2087, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x2097,
-       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x20a7, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x20b7,
-       0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x20c7, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x20d7,
-       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x20e7, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20f7,
-       0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x2507, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x2517,
-       0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x2527, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x2537,
-       0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x2547, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x2557,
-       0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x2567, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x2577,
-       0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x2987, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x2997,
-       0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x29a7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x29b7,
-       0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dc7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dd7,
-       0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x31e7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x41f7,
-
-/* previous 1s = 3: */
-       0x0000, 0x0001, 0x0002, 0x2003, 0x0004, 0x0005, 0x0006, 0x200b, 0x0008, 0x0009, 0x000a, 0x2013, 0x000c, 0x000d, 0x000e, 0x201b,
-       0x0010, 0x0011, 0x0012, 0x2023, 0x0014, 0x0015, 0x0016, 0x202b, 0x0018, 0x0019, 0x001a, 0x2033, 0x001c, 0x001d, 0x001e, 0x203b,
-       0x0020, 0x0021, 0x0022, 0x2043, 0x0024, 0x0025, 0x0026, 0x204b, 0x0028, 0x0029, 0x002a, 0x2053, 0x002c, 0x002d, 0x002e, 0x205b,
-       0x0030, 0x0031, 0x0032, 0x2063, 0x0034, 0x0035, 0x0036, 0x206b, 0x0038, 0x0039, 0x003a, 0x2073, 0x003c, 0x003d, 0x203e, 0x207b,
-       0x0040, 0x0041, 0x0042, 0x2083, 0x0044, 0x0045, 0x0046, 0x208b, 0x0048, 0x0049, 0x004a, 0x2093, 0x004c, 0x004d, 0x004e, 0x209b,
-       0x0050, 0x0051, 0x0052, 0x20a3, 0x0054, 0x0055, 0x0056, 0x20ab, 0x0058, 0x0059, 0x005a, 0x20b3, 0x005c, 0x005d, 0x005e, 0x20bb,
-       0x0060, 0x0061, 0x0062, 0x20c3, 0x0064, 0x0065, 0x0066, 0x20cb, 0x0068, 0x0069, 0x006a, 0x20d3, 0x006c, 0x006d, 0x006e, 0x20db,
-       0x0070, 0x0071, 0x0072, 0x20e3, 0x0074, 0x0075, 0x0076, 0x20eb, 0x0078, 0x0079, 0x007a, 0x20f3, 0x207c, 0x207d, 0x20be, 0x40fb,
-       0x0480, 0x0481, 0x0482, 0x2503, 0x0484, 0x0485, 0x0486, 0x250b, 0x0488, 0x0489, 0x048a, 0x2513, 0x048c, 0x048d, 0x048e, 0x251b,
-       0x0490, 0x0491, 0x0492, 0x2523, 0x0494, 0x0495, 0x0496, 0x252b, 0x0498, 0x0499, 0x049a, 0x2533, 0x049c, 0x049d, 0x049e, 0x253b,
-       0x04a0, 0x04a1, 0x04a2, 0x2543, 0x04a4, 0x04a5, 0x04a6, 0x254b, 0x04a8, 0x04a9, 0x04aa, 0x2553, 0x04ac, 0x04ad, 0x04ae, 0x255b,
-       0x04b0, 0x04b1, 0x04b2, 0x2563, 0x04b4, 0x04b5, 0x04b6, 0x256b, 0x04b8, 0x04b9, 0x04ba, 0x2573, 0x04bc, 0x04bd, 0x253e, 0x257b,
-       0x08c0, 0x08c1, 0x08c2, 0x2983, 0x08c4, 0x08c5, 0x08c6, 0x298b, 0x08c8, 0x08c9, 0x08ca, 0x2993, 0x08cc, 0x08cd, 0x08ce, 0x299b,
-       0x08d0, 0x08d1, 0x08d2, 0x29a3, 0x08d4, 0x08d5, 0x08d6, 0x29ab, 0x08d8, 0x08d9, 0x08da, 0x29b3, 0x08dc, 0x08dd, 0x08de, 0x29bb,
-       0x0ce0, 0x0ce1, 0x0ce2, 0x2dc3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dcb, 0x0ce8, 0x0ce9, 0x0cea, 0x2dd3, 0x0cec, 0x0ced, 0x0cee, 0x2ddb,
-       0x10f0, 0x10f1, 0x10f2, 0x31e3, 0x10f4, 0x10f5, 0x10f6, 0x31eb, 0x20f8, 0x20f9, 0x20fa, 0x41f3, 0x257c, 0x257d, 0x29be, 0x46fb,
-
-/* previous 1s = 4: */
-       0x0000, 0x2001, 0x0002, 0x2005, 0x0004, 0x2009, 0x0006, 0x200d, 0x0008, 0x2011, 0x000a, 0x2015, 0x000c, 0x2019, 0x000e, 0x201d,
-       0x0010, 0x2021, 0x0012, 0x2025, 0x0014, 0x2029, 0x0016, 0x202d, 0x0018, 0x2031, 0x001a, 0x2035, 0x001c, 0x2039, 0x001e, 0x203d,
-       0x0020, 0x2041, 0x0022, 0x2045, 0x0024, 0x2049, 0x0026, 0x204d, 0x0028, 0x2051, 0x002a, 0x2055, 0x002c, 0x2059, 0x002e, 0x205d,
-       0x0030, 0x2061, 0x0032, 0x2065, 0x0034, 0x2069, 0x0036, 0x206d, 0x0038, 0x2071, 0x003a, 0x2075, 0x003c, 0x2079, 0x203e, 0x407d,
-       0x0040, 0x2081, 0x0042, 0x2085, 0x0044, 0x2089, 0x0046, 0x208d, 0x0048, 0x2091, 0x004a, 0x2095, 0x004c, 0x2099, 0x004e, 0x209d,
-       0x0050, 0x20a1, 0x0052, 0x20a5, 0x0054, 0x20a9, 0x0056, 0x20ad, 0x0058, 0x20b1, 0x005a, 0x20b5, 0x005c, 0x20b9, 0x005e, 0x20bd,
-       0x0060, 0x20c1, 0x0062, 0x20c5, 0x0064, 0x20c9, 0x0066, 0x20cd, 0x0068, 0x20d1, 0x006a, 0x20d5, 0x006c, 0x20d9, 0x006e, 0x20dd,
-       0x0070, 0x20e1, 0x0072, 0x20e5, 0x0074, 0x20e9, 0x0076, 0x20ed, 0x0078, 0x20f1, 0x007a, 0x20f5, 0x207c, 0x40f9, 0x20be, 0x417d,
-       0x0480, 0x2501, 0x0482, 0x2505, 0x0484, 0x2509, 0x0486, 0x250d, 0x0488, 0x2511, 0x048a, 0x2515, 0x048c, 0x2519, 0x048e, 0x251d,
-       0x0490, 0x2521, 0x0492, 0x2525, 0x0494, 0x2529, 0x0496, 0x252d, 0x0498, 0x2531, 0x049a, 0x2535, 0x049c, 0x2539, 0x049e, 0x253d,
-       0x04a0, 0x2541, 0x04a2, 0x2545, 0x04a4, 0x2549, 0x04a6, 0x254d, 0x04a8, 0x2551, 0x04aa, 0x2555, 0x04ac, 0x2559, 0x04ae, 0x255d,
-       0x04b0, 0x2561, 0x04b2, 0x2565, 0x04b4, 0x2569, 0x04b6, 0x256d, 0x04b8, 0x2571, 0x04ba, 0x2575, 0x04bc, 0x2579, 0x253e, 0x467d,
-       0x08c0, 0x2981, 0x08c2, 0x2985, 0x08c4, 0x2989, 0x08c6, 0x298d, 0x08c8, 0x2991, 0x08ca, 0x2995, 0x08cc, 0x2999, 0x08ce, 0x299d,
-       0x08d0, 0x29a1, 0x08d2, 0x29a5, 0x08d4, 0x29a9, 0x08d6, 0x29ad, 0x08d8, 0x29b1, 0x08da, 0x29b5, 0x08dc, 0x29b9, 0x08de, 0x29bd,
-       0x0ce0, 0x2dc1, 0x0ce2, 0x2dc5, 0x0ce4, 0x2dc9, 0x0ce6, 0x2dcd, 0x0ce8, 0x2dd1, 0x0cea, 0x2dd5, 0x0cec, 0x2dd9, 0x0cee, 0x2ddd,
-       0x10f0, 0x31e1, 0x10f2, 0x31e5, 0x10f4, 0x31e9, 0x10f6, 0x31ed, 0x20f8, 0x41f1, 0x20fa, 0x41f5, 0x257c, 0x46f9, 0x29be, 0x4b7d
-};
-
-/* hdlc_bitstuff_byte
- * perform HDLC bitstuffing for one input byte (8 bits, LSB first)
- * parameters:
- *     cin     input byte
- *     ones    number of trailing '1' bits in result before this step
- *     iwb     pointer to output buffer structure
- *             (write semaphore must be held)
- * return value:
- *     number of trailing '1' bits in result after this step
- */
-
-static inline int hdlc_bitstuff_byte(struct isowbuf_t *iwb, unsigned char cin,
-                                    int ones)
-{
-       u16 stuff;
-       int shiftinc, newones;
-
-       /* get stuffing information for input byte
-        * value: bit  9.. 0 = result bits
-        *        bit 12..10 = number of trailing '1' bits in result
-        *        bit 14..13 = number of bits added by stuffing
-        */
-       stuff = stufftab[256 * ones + cin];
-       shiftinc = (stuff >> 13) & 3;
-       newones = (stuff >> 10) & 7;
-       stuff &= 0x3ff;
-
-       /* append stuffed byte to output stream */
-       isowbuf_putbits(iwb, stuff, 8 + shiftinc);
-       return newones;
-}
-
-/* hdlc_buildframe
- * Perform HDLC framing with bitstuffing on a byte buffer
- * The input buffer is regarded as a sequence of bits, starting with the least
- * significant bit of the first byte and ending with the most significant bit
- * of the last byte. A 16 bit FCS is appended as defined by RFC 1662.
- * Whenever five consecutive '1' bits appear in the resulting bit sequence, a
- * '0' bit is inserted after them.
- * The resulting bit string and a closing flag pattern (PPP_FLAG, '01111110')
- * are appended to the output buffer starting at the given bit position, which
- * is assumed to already contain a leading flag.
- * The output buffer must have sufficient length; count + count/5 + 6 bytes
- * starting at *out are safe and are verified to be present.
- * parameters:
- *     in      input buffer
- *     count   number of bytes in input buffer
- *     iwb     pointer to output buffer structure
- *             (write semaphore must be held)
- * return value:
- *     position of end of packet in output buffer on success,
- *     -EAGAIN if write semaphore busy or buffer full
- */
-
-static inline int hdlc_buildframe(struct isowbuf_t *iwb,
-                                 unsigned char *in, int count)
-{
-       int ones;
-       u16 fcs;
-       int end;
-       unsigned char c;
-
-       if (isowbuf_freebytes(iwb) < count + count / 5 + 6 ||
-           isowbuf_startwrite(iwb) < 0) {
-               gig_dbg(DEBUG_ISO, "%s: %d bytes free -> -EAGAIN",
-                       __func__, isowbuf_freebytes(iwb));
-               return -EAGAIN;
-       }
-
-       dump_bytes(DEBUG_STREAM_DUMP, "snd data", in, count);
-
-       /* bitstuff and checksum input data */
-       fcs = PPP_INITFCS;
-       ones = 0;
-       while (count-- > 0) {
-               c = *in++;
-               ones = hdlc_bitstuff_byte(iwb, c, ones);
-               fcs = crc_ccitt_byte(fcs, c);
-       }
-
-       /* bitstuff and append FCS
-        * (complemented, least significant byte first) */
-       fcs ^= 0xffff;
-       ones = hdlc_bitstuff_byte(iwb, fcs & 0x00ff, ones);
-       ones = hdlc_bitstuff_byte(iwb, (fcs >> 8) & 0x00ff, ones);
-
-       /* put closing flag and repeat byte for flag idle */
-       isowbuf_putflag(iwb);
-       end = isowbuf_donewrite(iwb);
-       return end;
-}
-
-/* trans_buildframe
- * Append a block of 'transparent' data to the output buffer,
- * inverting the bytes.
- * The output buffer must have sufficient length; count bytes
- * starting at *out are safe and are verified to be present.
- * parameters:
- *     in      input buffer
- *     count   number of bytes in input buffer
- *     iwb     pointer to output buffer structure
- *             (write semaphore must be held)
- * return value:
- *     position of end of packet in output buffer on success,
- *     -EAGAIN if write semaphore busy or buffer full
- */
-
-static inline int trans_buildframe(struct isowbuf_t *iwb,
-                                  unsigned char *in, int count)
-{
-       int write;
-       unsigned char c;
-
-       if (unlikely(count <= 0))
-               return iwb->write;
-
-       if (isowbuf_freebytes(iwb) < count ||
-           isowbuf_startwrite(iwb) < 0) {
-               gig_dbg(DEBUG_ISO, "can't put %d bytes", count);
-               return -EAGAIN;
-       }
-
-       gig_dbg(DEBUG_STREAM, "put %d bytes", count);
-       dump_bytes(DEBUG_STREAM_DUMP, "snd data", in, count);
-
-       write = iwb->write;
-       do {
-               c = bitrev8(*in++);
-               iwb->data[write++] = c;
-               write %= BAS_OUTBUFSIZE;
-       } while (--count > 0);
-       iwb->write = write;
-       iwb->idle = c;
-
-       return isowbuf_donewrite(iwb);
-}
-
-int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len)
-{
-       int result;
-
-       switch (bcs->proto2) {
-       case L2_HDLC:
-               result = hdlc_buildframe(bcs->hw.bas->isooutbuf, in, len);
-               gig_dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d",
-                       __func__, len, result);
-               break;
-       default:                        /* assume transparent */
-               result = trans_buildframe(bcs->hw.bas->isooutbuf, in, len);
-               gig_dbg(DEBUG_ISO, "%s: %d bytes trans -> %d",
-                       __func__, len, result);
-       }
-       return result;
-}
-
-/* hdlc_putbyte
- * append byte c to current skb of B channel structure *bcs, updating fcs
- */
-static inline void hdlc_putbyte(unsigned char c, struct bc_state *bcs)
-{
-       bcs->rx_fcs = crc_ccitt_byte(bcs->rx_fcs, c);
-       if (bcs->rx_skb == NULL)
-               /* skipping */
-               return;
-       if (bcs->rx_skb->len >= bcs->rx_bufsize) {
-               dev_warn(bcs->cs->dev, "received oversized packet discarded\n");
-               bcs->hw.bas->giants++;
-               dev_kfree_skb_any(bcs->rx_skb);
-               bcs->rx_skb = NULL;
-               return;
-       }
-       __skb_put_u8(bcs->rx_skb, c);
-}
-
-/* hdlc_flush
- * drop partial HDLC data packet
- */
-static inline void hdlc_flush(struct bc_state *bcs)
-{
-       /* clear skb or allocate new if not skipping */
-       if (bcs->rx_skb != NULL)
-               skb_trim(bcs->rx_skb, 0);
-       else
-               gigaset_new_rx_skb(bcs);
-
-       /* reset packet state */
-       bcs->rx_fcs = PPP_INITFCS;
-}
-
-/* hdlc_done
- * process completed HDLC data packet
- */
-static inline void hdlc_done(struct bc_state *bcs)
-{
-       struct cardstate *cs = bcs->cs;
-       struct sk_buff *procskb;
-       unsigned int len;
-
-       if (unlikely(bcs->ignore)) {
-               bcs->ignore--;
-               hdlc_flush(bcs);
-               return;
-       }
-       procskb = bcs->rx_skb;
-       if (procskb == NULL) {
-               /* previous error */
-               gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__);
-               gigaset_isdn_rcv_err(bcs);
-       } else if (procskb->len < 2) {
-               dev_notice(cs->dev, "received short frame (%d octets)\n",
-                          procskb->len);
-               bcs->hw.bas->runts++;
-               dev_kfree_skb_any(procskb);
-               gigaset_isdn_rcv_err(bcs);
-       } else if (bcs->rx_fcs != PPP_GOODFCS) {
-               dev_notice(cs->dev, "frame check error\n");
-               bcs->hw.bas->fcserrs++;
-               dev_kfree_skb_any(procskb);
-               gigaset_isdn_rcv_err(bcs);
-       } else {
-               len = procskb->len;
-               __skb_trim(procskb, len -= 2);  /* subtract FCS */
-               gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", __func__, len);
-               dump_bytes(DEBUG_STREAM_DUMP,
-                          "rcv data", procskb->data, len);
-               bcs->hw.bas->goodbytes += len;
-               gigaset_skb_rcvd(bcs, procskb);
-       }
-       gigaset_new_rx_skb(bcs);
-       bcs->rx_fcs = PPP_INITFCS;
-}
-
-/* hdlc_frag
- * drop HDLC data packet with non-integral last byte
- */
-static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits)
-{
-       if (unlikely(bcs->ignore)) {
-               bcs->ignore--;
-               hdlc_flush(bcs);
-               return;
-       }
-
-       dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits);
-       bcs->hw.bas->alignerrs++;
-       gigaset_isdn_rcv_err(bcs);
-       __skb_trim(bcs->rx_skb, 0);
-       bcs->rx_fcs = PPP_INITFCS;
-}
-
-/* bit counts lookup table for HDLC bit unstuffing
- * index: input byte
- * value: bit 0..3 = number of consecutive '1' bits starting from LSB
- *        bit 4..6 = number of consecutive '1' bits starting from MSB
- *                  (replacing 8 by 7 to make it fit; the algorithm won't care)
- *        bit 7 set if there are 5 or more "interior" consecutive '1' bits
- */
-static const unsigned char bitcounts[256] = {
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05,
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x80, 0x06,
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05,
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
-       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x80, 0x81, 0x80, 0x07,
-       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x14,
-       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x15,
-       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x14,
-       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x90, 0x16,
-       0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x24,
-       0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x25,
-       0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30, 0x34,
-       0x40, 0x41, 0x40, 0x42, 0x40, 0x41, 0x40, 0x43, 0x50, 0x51, 0x50, 0x52, 0x60, 0x61, 0x70, 0x78
-};
-
-/* hdlc_unpack
- * perform HDLC frame processing (bit unstuffing, flag detection, FCS
- * calculation) on a sequence of received data bytes (8 bits each, LSB first)
- * pass on successfully received, complete frames as SKBs via gigaset_skb_rcvd
- * notify of errors via gigaset_isdn_rcv_err
- * tally frames, errors etc. in BC structure counters
- * parameters:
- *     src     received data
- *     count   number of received bytes
- *     bcs     receiving B channel structure
- */
-static inline void hdlc_unpack(unsigned char *src, unsigned count,
-                              struct bc_state *bcs)
-{
-       struct bas_bc_state *ubc = bcs->hw.bas;
-       int inputstate;
-       unsigned seqlen, inbyte, inbits;
-
-       /* load previous state:
-        * inputstate = set of flag bits:
-        * - INS_flag_hunt: no complete opening flag received since connection
-        *                  setup or last abort
-        * - INS_have_data: at least one complete data byte received since last
-        *                  flag
-        * seqlen = number of consecutive '1' bits in last 7 input stream bits
-        *          (0..7)
-        * inbyte = accumulated partial data byte (if !INS_flag_hunt)
-        * inbits = number of valid bits in inbyte, starting at LSB (0..6)
-        */
-       inputstate = bcs->inputstate;
-       seqlen = ubc->seqlen;
-       inbyte = ubc->inbyte;
-       inbits = ubc->inbits;
-
-       /* bit unstuffing a byte a time
-        * Take your time to understand this; it's straightforward but tedious.
-        * The "bitcounts" lookup table is used to speed up the counting of
-        * leading and trailing '1' bits.
-        */
-       while (count--) {
-               unsigned char c = *src++;
-               unsigned char tabentry = bitcounts[c];
-               unsigned lead1 = tabentry & 0x0f;
-               unsigned trail1 = (tabentry >> 4) & 0x0f;
-
-               seqlen += lead1;
-
-               if (unlikely(inputstate & INS_flag_hunt)) {
-                       if (c == PPP_FLAG) {
-                               /* flag-in-one */
-                               inputstate &= ~(INS_flag_hunt | INS_have_data);
-                               inbyte = 0;
-                               inbits = 0;
-                       } else if (seqlen == 6 && trail1 != 7) {
-                               /* flag completed & not followed by abort */
-                               inputstate &= ~(INS_flag_hunt | INS_have_data);
-                               inbyte = c >> (lead1 + 1);
-                               inbits = 7 - lead1;
-                               if (trail1 >= 8) {
-                                       /* interior stuffing:
-                                        * omitting the MSB handles most cases,
-                                        * correct the incorrectly handled
-                                        * cases individually */
-                                       inbits--;
-                                       switch (c) {
-                                       case 0xbe:
-                                               inbyte = 0x3f;
-                                               break;
-                                       }
-                               }
-                       }
-                       /* else: continue flag-hunting */
-               } else if (likely(seqlen < 5 && trail1 < 7)) {
-                       /* streamlined case: 8 data bits, no stuffing */
-                       inbyte |= c << inbits;
-                       hdlc_putbyte(inbyte & 0xff, bcs);
-                       inputstate |= INS_have_data;
-                       inbyte >>= 8;
-                       /* inbits unchanged */
-               } else if (likely(seqlen == 6 && inbits == 7 - lead1 &&
-                                 trail1 + 1 == inbits &&
-                                 !(inputstate & INS_have_data))) {
-                       /* streamlined case: flag idle - state unchanged */
-               } else if (unlikely(seqlen > 6)) {
-                       /* abort sequence */
-                       ubc->aborts++;
-                       hdlc_flush(bcs);
-                       inputstate |= INS_flag_hunt;
-               } else if (seqlen == 6) {
-                       /* closing flag, including (6 - lead1) '1's
-                        * and one '0' from inbits */
-                       if (inbits > 7 - lead1) {
-                               hdlc_frag(bcs, inbits + lead1 - 7);
-                               inputstate &= ~INS_have_data;
-                       } else {
-                               if (inbits < 7 - lead1)
-                                       ubc->stolen0s++;
-                               if (inputstate & INS_have_data) {
-                                       hdlc_done(bcs);
-                                       inputstate &= ~INS_have_data;
-                               }
-                       }
-
-                       if (c == PPP_FLAG) {
-                               /* complete flag, LSB overlaps preceding flag */
-                               ubc->shared0s++;
-                               inbits = 0;
-                               inbyte = 0;
-                       } else if (trail1 != 7) {
-                               /* remaining bits */
-                               inbyte = c >> (lead1 + 1);
-                               inbits = 7 - lead1;
-                               if (trail1 >= 8) {
-                                       /* interior stuffing:
-                                        * omitting the MSB handles most cases,
-                                        * correct the incorrectly handled
-                                        * cases individually */
-                                       inbits--;
-                                       switch (c) {
-                                       case 0xbe:
-                                               inbyte = 0x3f;
-                                               break;
-                                       }
-                               }
-                       } else {
-                               /* abort sequence follows,
-                                * skb already empty anyway */
-                               ubc->aborts++;
-                               inputstate |= INS_flag_hunt;
-                       }
-               } else { /* (seqlen < 6) && (seqlen == 5 || trail1 >= 7) */
-
-                       if (c == PPP_FLAG) {
-                               /* complete flag */
-                               if (seqlen == 5)
-                                       ubc->stolen0s++;
-                               if (inbits) {
-                                       hdlc_frag(bcs, inbits);
-                                       inbits = 0;
-                                       inbyte = 0;
-                               } else if (inputstate & INS_have_data)
-                                       hdlc_done(bcs);
-                               inputstate &= ~INS_have_data;
-                       } else if (trail1 == 7) {
-                               /* abort sequence */
-                               ubc->aborts++;
-                               hdlc_flush(bcs);
-                               inputstate |= INS_flag_hunt;
-                       } else {
-                               /* stuffed data */
-                               if (trail1 < 7) { /* => seqlen == 5 */
-                                       /* stuff bit at position lead1,
-                                        * no interior stuffing */
-                                       unsigned char mask = (1 << lead1) - 1;
-                                       c = (c & mask) | ((c & ~mask) >> 1);
-                                       inbyte |= c << inbits;
-                                       inbits += 7;
-                               } else if (seqlen < 5) { /* trail1 >= 8 */
-                                       /* interior stuffing:
-                                        * omitting the MSB handles most cases,
-                                        * correct the incorrectly handled
-                                        * cases individually */
-                                       switch (c) {
-                                       case 0xbe:
-                                               c = 0x7e;
-                                               break;
-                                       }
-                                       inbyte |= c << inbits;
-                                       inbits += 7;
-                               } else { /* seqlen == 5 && trail1 >= 8 */
-
-                                       /* stuff bit at lead1 *and* interior
-                                        * stuffing -- unstuff individually */
-                                       switch (c) {
-                                       case 0x7d:
-                                               c = 0x3f;
-                                               break;
-                                       case 0xbe:
-                                               c = 0x3f;
-                                               break;
-                                       case 0x3e:
-                                               c = 0x1f;
-                                               break;
-                                       case 0x7c:
-                                               c = 0x3e;
-                                               break;
-                                       }
-                                       inbyte |= c << inbits;
-                                       inbits += 6;
-                               }
-                               if (inbits >= 8) {
-                                       inbits -= 8;
-                                       hdlc_putbyte(inbyte & 0xff, bcs);
-                                       inputstate |= INS_have_data;
-                                       inbyte >>= 8;
-                               }
-                       }
-               }
-               seqlen = trail1 & 7;
-       }
-
-       /* save new state */
-       bcs->inputstate = inputstate;
-       ubc->seqlen = seqlen;
-       ubc->inbyte = inbyte;
-       ubc->inbits = inbits;
-}
-
-/* trans_receive
- * pass on received USB frame transparently as SKB via gigaset_skb_rcvd
- * invert bytes
- * tally frames, errors etc. in BC structure counters
- * parameters:
- *     src     received data
- *     count   number of received bytes
- *     bcs     receiving B channel structure
- */
-static inline void trans_receive(unsigned char *src, unsigned count,
-                                struct bc_state *bcs)
-{
-       struct sk_buff *skb;
-       int dobytes;
-       unsigned char *dst;
-
-       if (unlikely(bcs->ignore)) {
-               bcs->ignore--;
-               return;
-       }
-       skb = bcs->rx_skb;
-       if (skb == NULL) {
-               skb = gigaset_new_rx_skb(bcs);
-               if (skb == NULL)
-                       return;
-       }
-       dobytes = bcs->rx_bufsize - skb->len;
-       while (count > 0) {
-               dst = skb_put(skb, count < dobytes ? count : dobytes);
-               while (count > 0 && dobytes > 0) {
-                       *dst++ = bitrev8(*src++);
-                       count--;
-                       dobytes--;
-               }
-               if (dobytes == 0) {
-                       dump_bytes(DEBUG_STREAM_DUMP,
-                                  "rcv data", skb->data, skb->len);
-                       bcs->hw.bas->goodbytes += skb->len;
-                       gigaset_skb_rcvd(bcs, skb);
-                       skb = gigaset_new_rx_skb(bcs);
-                       if (skb == NULL)
-                               return;
-                       dobytes = bcs->rx_bufsize;
-               }
-       }
-}
-
-void gigaset_isoc_receive(unsigned char *src, unsigned count,
-                         struct bc_state *bcs)
-{
-       switch (bcs->proto2) {
-       case L2_HDLC:
-               hdlc_unpack(src, count, bcs);
-               break;
-       default:                /* assume transparent */
-               trans_receive(src, count, bcs);
-       }
-}
-
-/* == data input =========================================================== */
-
-/* process a block of received bytes in command mode (mstate != MS_LOCKED)
- * Append received bytes to the command response buffer and forward them
- * line by line to the response handler.
- * Note: Received lines may be terminated by CR, LF, or CR LF, which will be
- * removed before passing the line to the response handler.
- */
-static void cmd_loop(unsigned char *src, int numbytes, struct inbuf_t *inbuf)
-{
-       struct cardstate *cs = inbuf->cs;
-       unsigned cbytes      = cs->cbytes;
-       unsigned char c;
-
-       while (numbytes--) {
-               c = *src++;
-               switch (c) {
-               case '\n':
-                       if (cbytes == 0 && cs->respdata[0] == '\r') {
-                               /* collapse LF with preceding CR */
-                               cs->respdata[0] = 0;
-                               break;
-                       }
-                       /* fall through */
-               case '\r':
-                       /* end of message line, pass to response handler */
-                       if (cbytes >= MAX_RESP_SIZE) {
-                               dev_warn(cs->dev, "response too large (%d)\n",
-                                        cbytes);
-                               cbytes = MAX_RESP_SIZE;
-                       }
-                       cs->cbytes = cbytes;
-                       gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
-                                          cbytes, cs->respdata);
-                       gigaset_handle_modem_response(cs);
-                       cbytes = 0;
-
-                       /* store EOL byte for CRLF collapsing */
-                       cs->respdata[0] = c;
-                       break;
-               default:
-                       /* append to line buffer if possible */
-                       if (cbytes < MAX_RESP_SIZE)
-                               cs->respdata[cbytes] = c;
-                       cbytes++;
-               }
-       }
-
-       /* save state */
-       cs->cbytes = cbytes;
-}
-
-
-/* process a block of data received through the control channel
- */
-void gigaset_isoc_input(struct inbuf_t *inbuf)
-{
-       struct cardstate *cs = inbuf->cs;
-       unsigned tail, head, numbytes;
-       unsigned char *src;
-
-       head = inbuf->head;
-       while (head != (tail = inbuf->tail)) {
-               gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
-               if (head > tail)
-                       tail = RBUFSIZE;
-               src = inbuf->data + head;
-               numbytes = tail - head;
-               gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
-
-               if (cs->mstate == MS_LOCKED) {
-                       gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
-                                          numbytes, src);
-                       gigaset_if_receive(inbuf->cs, src, numbytes);
-               } else {
-                       cmd_loop(src, numbytes, inbuf);
-               }
-
-               head += numbytes;
-               if (head == RBUFSIZE)
-                       head = 0;
-               gig_dbg(DEBUG_INTR, "setting head to %u", head);
-               inbuf->head = head;
-       }
-}
-
-
-/* == data output ========================================================== */
-
-/**
- * gigaset_isoc_send_skb() - queue an skb for sending
- * @bcs:       B channel descriptor structure.
- * @skb:       data to send.
- *
- * Called by LL to queue an skb for sending, and start transmission if
- * necessary.
- * Once the payload data has been transmitted completely, gigaset_skb_sent()
- * will be called with the skb's link layer header preserved.
- *
- * Return value:
- *     number of bytes accepted for sending (skb->len) if ok,
- *     error code < 0 (eg. -ENODEV) on error
- */
-int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb)
-{
-       int len = skb->len;
-       unsigned long flags;
-
-       spin_lock_irqsave(&bcs->cs->lock, flags);
-       if (!bcs->cs->connected) {
-               spin_unlock_irqrestore(&bcs->cs->lock, flags);
-               return -ENODEV;
-       }
-
-       skb_queue_tail(&bcs->squeue, skb);
-       gig_dbg(DEBUG_ISO, "%s: skb queued, qlen=%d",
-               __func__, skb_queue_len(&bcs->squeue));
-
-       /* tasklet submits URB if necessary */
-       tasklet_schedule(&bcs->hw.bas->sent_tasklet);
-       spin_unlock_irqrestore(&bcs->cs->lock, flags);
-
-       return len;     /* ok so far */
-}
diff --git a/drivers/isdn/gigaset/proc.c b/drivers/isdn/gigaset/proc.c
deleted file mode 100644 (file)
index e3f9d0f..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Stuff used by all variants of the driver
- *
- * Copyright (c) 2001 by Stefan Eilers,
- *                       Hansjoerg Lipp <hjlipp@web.de>,
- *                       Tilman Schmidt <tilman@imap.cc>.
- *
- * =====================================================================
- *     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 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-
-static ssize_t show_cidmode(struct device *dev,
-                           struct device_attribute *attr, char *buf)
-{
-       struct cardstate *cs = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%u\n", cs->cidmode);
-}
-
-static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
-{
-       struct cardstate *cs = dev_get_drvdata(dev);
-       long int value;
-       char *end;
-
-       value = simple_strtol(buf, &end, 0);
-       while (*end)
-               if (!isspace(*end++))
-                       return -EINVAL;
-       if (value < 0 || value > 1)
-               return -EINVAL;
-
-       if (mutex_lock_interruptible(&cs->mutex))
-               return -ERESTARTSYS;
-
-       cs->waiting = 1;
-       if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE,
-                              NULL, value, NULL)) {
-               cs->waiting = 0;
-               mutex_unlock(&cs->mutex);
-               return -ENOMEM;
-       }
-       gigaset_schedule_event(cs);
-
-       wait_event(cs->waitqueue, !cs->waiting);
-
-       mutex_unlock(&cs->mutex);
-
-       return count;
-}
-
-static DEVICE_ATTR(cidmode, S_IRUGO | S_IWUSR, show_cidmode, set_cidmode);
-
-/* free sysfs for device */
-void gigaset_free_dev_sysfs(struct cardstate *cs)
-{
-       if (!cs->tty_dev)
-               return;
-
-       gig_dbg(DEBUG_INIT, "removing sysfs entries");
-       device_remove_file(cs->tty_dev, &dev_attr_cidmode);
-}
-
-/* initialize sysfs for device */
-void gigaset_init_dev_sysfs(struct cardstate *cs)
-{
-       if (!cs->tty_dev)
-               return;
-
-       gig_dbg(DEBUG_INIT, "setting up sysfs");
-       if (device_create_file(cs->tty_dev, &dev_attr_cidmode))
-               pr_err("could not create sysfs attribute\n");
-}
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
deleted file mode 100644 (file)
index e1de8b1..0000000
+++ /dev/null
@@ -1,799 +0,0 @@
-/* This is the serial hardware link layer (HLL) for the Gigaset 307x isdn
- * DECT base (aka Sinus 45 isdn) using the RS232 DECT data module M101,
- * written as a line discipline.
- *
- * =====================================================================
- * 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 of
- * the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/completion.h>
-
-/* Version Information */
-#define DRIVER_AUTHOR "Tilman Schmidt"
-#define DRIVER_DESC "Serial Driver for Gigaset 307x using Siemens M101"
-
-#define GIGASET_MINORS     1
-#define GIGASET_MINOR      0
-#define GIGASET_MODULENAME "ser_gigaset"
-#define GIGASET_DEVNAME    "ttyGS"
-
-/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
-#define IF_WRITEBUF 264
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_LDISC(N_GIGASET_M101);
-
-static int startmode = SM_ISDN;
-module_param(startmode, int, S_IRUGO);
-MODULE_PARM_DESC(startmode, "initial operation mode");
-static int cidmode = 1;
-module_param(cidmode, int, S_IRUGO);
-MODULE_PARM_DESC(cidmode, "stay in CID mode when idle");
-
-static struct gigaset_driver *driver;
-
-struct ser_cardstate {
-       struct platform_device  dev;
-       struct tty_struct       *tty;
-       atomic_t                refcnt;
-       struct completion       dead_cmp;
-};
-
-static struct platform_driver device_driver = {
-       .driver = {
-               .name = GIGASET_MODULENAME,
-       },
-};
-
-static void flush_send_queue(struct cardstate *);
-
-/* transmit data from current open skb
- * result: number of bytes sent or error code < 0
- */
-static int write_modem(struct cardstate *cs)
-{
-       struct tty_struct *tty = cs->hw.ser->tty;
-       struct bc_state *bcs = &cs->bcs[0];     /* only one channel */
-       struct sk_buff *skb = bcs->tx_skb;
-       int sent = -EOPNOTSUPP;
-
-       WARN_ON(!tty || !tty->ops || !skb);
-
-       if (!skb->len) {
-               dev_kfree_skb_any(skb);
-               bcs->tx_skb = NULL;
-               return -EINVAL;
-       }
-
-       set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-       if (tty->ops->write)
-               sent = tty->ops->write(tty, skb->data, skb->len);
-       gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent);
-       if (sent < 0) {
-               /* error */
-               flush_send_queue(cs);
-               return sent;
-       }
-       skb_pull(skb, sent);
-       if (!skb->len) {
-               /* skb sent completely */
-               gigaset_skb_sent(bcs, skb);
-
-               gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
-                       (unsigned long) skb);
-               dev_kfree_skb_any(skb);
-               bcs->tx_skb = NULL;
-       }
-       return sent;
-}
-
-/*
- * transmit first queued command buffer
- * result: number of bytes sent or error code < 0
- */
-static int send_cb(struct cardstate *cs)
-{
-       struct tty_struct *tty = cs->hw.ser->tty;
-       struct cmdbuf_t *cb, *tcb;
-       unsigned long flags;
-       int sent = 0;
-
-       WARN_ON(!tty || !tty->ops);
-
-       cb = cs->cmdbuf;
-       if (!cb)
-               return 0;       /* nothing to do */
-
-       if (cb->len) {
-               set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-               sent = tty->ops->write(tty, cb->buf + cb->offset, cb->len);
-               if (sent < 0) {
-                       /* error */
-                       gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent);
-                       flush_send_queue(cs);
-                       return sent;
-               }
-               cb->offset += sent;
-               cb->len -= sent;
-               gig_dbg(DEBUG_OUTPUT, "send_cb: sent %d, left %u, queued %u",
-                       sent, cb->len, cs->cmdbytes);
-       }
-
-       while (cb && !cb->len) {
-               spin_lock_irqsave(&cs->cmdlock, flags);
-               cs->cmdbytes -= cs->curlen;
-               tcb = cb;
-               cs->cmdbuf = cb = cb->next;
-               if (cb) {
-                       cb->prev = NULL;
-                       cs->curlen = cb->len;
-               } else {
-                       cs->lastcmdbuf = NULL;
-                       cs->curlen = 0;
-               }
-               spin_unlock_irqrestore(&cs->cmdlock, flags);
-
-               if (tcb->wake_tasklet)
-                       tasklet_schedule(tcb->wake_tasklet);
-               kfree(tcb);
-       }
-       return sent;
-}
-
-/*
- * send queue tasklet
- * If there is already a skb opened, put data to the transfer buffer
- * by calling "write_modem".
- * Otherwise take a new skb out of the queue.
- */
-static void gigaset_modem_fill(unsigned long data)
-{
-       struct cardstate *cs = (struct cardstate *) data;
-       struct bc_state *bcs;
-       struct sk_buff *nextskb;
-       int sent = 0;
-
-       if (!cs) {
-               gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
-               return;
-       }
-       bcs = cs->bcs;
-       if (!bcs) {
-               gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
-               return;
-       }
-       if (!bcs->tx_skb) {
-               /* no skb is being sent; send command if any */
-               sent = send_cb(cs);
-               gig_dbg(DEBUG_OUTPUT, "%s: send_cb -> %d", __func__, sent);
-               if (sent)
-                       /* something sent or error */
-                       return;
-
-               /* no command to send; get skb */
-               nextskb = skb_dequeue(&bcs->squeue);
-               if (!nextskb)
-                       /* no skb either, nothing to do */
-                       return;
-               bcs->tx_skb = nextskb;
-
-               gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)",
-                       (unsigned long) bcs->tx_skb);
-       }
-
-       /* send skb */
-       gig_dbg(DEBUG_OUTPUT, "%s: tx_skb", __func__);
-       if (write_modem(cs) < 0)
-               gig_dbg(DEBUG_OUTPUT, "%s: write_modem failed", __func__);
-}
-
-/*
- * throw away all data queued for sending
- */
-static void flush_send_queue(struct cardstate *cs)
-{
-       struct sk_buff *skb;
-       struct cmdbuf_t *cb;
-       unsigned long flags;
-
-       /* command queue */
-       spin_lock_irqsave(&cs->cmdlock, flags);
-       while ((cb = cs->cmdbuf) != NULL) {
-               cs->cmdbuf = cb->next;
-               if (cb->wake_tasklet)
-                       tasklet_schedule(cb->wake_tasklet);
-               kfree(cb);
-       }
-       cs->cmdbuf = cs->lastcmdbuf = NULL;
-       cs->cmdbytes = cs->curlen = 0;
-       spin_unlock_irqrestore(&cs->cmdlock, flags);
-
-       /* data queue */
-       if (cs->bcs->tx_skb)
-               dev_kfree_skb_any(cs->bcs->tx_skb);
-       while ((skb = skb_dequeue(&cs->bcs->squeue)) != NULL)
-               dev_kfree_skb_any(skb);
-}
-
-
-/* Gigaset Driver Interface */
-/* ======================== */
-
-/*
- * queue an AT command string for transmission to the Gigaset device
- * parameters:
- *     cs              controller state structure
- *     buf             buffer containing the string to send
- *     len             number of characters to send
- *     wake_tasklet    tasklet to run when transmission is complete, or NULL
- * return value:
- *     number of bytes queued, or error code < 0
- */
-static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
-{
-       unsigned long flags;
-
-       gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
-                          DEBUG_TRANSCMD : DEBUG_LOCKCMD,
-                          "CMD Transmit", cb->len, cb->buf);
-
-       spin_lock_irqsave(&cs->cmdlock, flags);
-       cb->prev = cs->lastcmdbuf;
-       if (cs->lastcmdbuf)
-               cs->lastcmdbuf->next = cb;
-       else {
-               cs->cmdbuf = cb;
-               cs->curlen = cb->len;
-       }
-       cs->cmdbytes += cb->len;
-       cs->lastcmdbuf = cb;
-       spin_unlock_irqrestore(&cs->cmdlock, flags);
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (cs->connected)
-               tasklet_schedule(&cs->write_tasklet);
-       spin_unlock_irqrestore(&cs->lock, flags);
-       return cb->len;
-}
-
-/*
- * tty_driver.write_room interface routine
- * return number of characters the driver will accept to be written
- * parameter:
- *     controller state structure
- * return value:
- *     number of characters
- */
-static int gigaset_write_room(struct cardstate *cs)
-{
-       unsigned bytes;
-
-       bytes = cs->cmdbytes;
-       return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
-}
-
-/*
- * tty_driver.chars_in_buffer interface routine
- * return number of characters waiting to be sent
- * parameter:
- *     controller state structure
- * return value:
- *     number of characters
- */
-static int gigaset_chars_in_buffer(struct cardstate *cs)
-{
-       return cs->cmdbytes;
-}
-
-/*
- * implementation of ioctl(GIGASET_BRKCHARS)
- * parameter:
- *     controller state structure
- * return value:
- *     -EINVAL (unimplemented function)
- */
-static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
-{
-       /* not implemented */
-       return -EINVAL;
-}
-
-/*
- * Open B channel
- * Called by "do_action" in ev-layer.c
- */
-static int gigaset_init_bchannel(struct bc_state *bcs)
-{
-       /* nothing to do for M10x */
-       gigaset_bchannel_up(bcs);
-       return 0;
-}
-
-/*
- * Close B channel
- * Called by "do_action" in ev-layer.c
- */
-static int gigaset_close_bchannel(struct bc_state *bcs)
-{
-       /* nothing to do for M10x */
-       gigaset_bchannel_down(bcs);
-       return 0;
-}
-
-/*
- * Set up B channel structure
- * This is called by "gigaset_initcs" in common.c
- */
-static int gigaset_initbcshw(struct bc_state *bcs)
-{
-       /* unused */
-       bcs->hw.ser = NULL;
-       return 0;
-}
-
-/*
- * Free B channel structure
- * Called by "gigaset_freebcs" in common.c
- */
-static void gigaset_freebcshw(struct bc_state *bcs)
-{
-       /* unused */
-}
-
-/*
- * Reinitialize B channel structure
- * This is called by "bcs_reinit" in common.c
- */
-static void gigaset_reinitbcshw(struct bc_state *bcs)
-{
-       /* nothing to do for M10x */
-}
-
-/*
- * Free hardware specific device data
- * This will be called by "gigaset_freecs" in common.c
- */
-static void gigaset_freecshw(struct cardstate *cs)
-{
-       tasklet_kill(&cs->write_tasklet);
-       if (!cs->hw.ser)
-               return;
-       platform_device_unregister(&cs->hw.ser->dev);
-}
-
-static void gigaset_device_release(struct device *dev)
-{
-       kfree(container_of(dev, struct ser_cardstate, dev.dev));
-}
-
-/*
- * Set up hardware specific device data
- * This is called by "gigaset_initcs" in common.c
- */
-static int gigaset_initcshw(struct cardstate *cs)
-{
-       int rc;
-       struct ser_cardstate *scs;
-
-       scs = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL);
-       if (!scs) {
-               pr_err("out of memory\n");
-               return -ENOMEM;
-       }
-       cs->hw.ser = scs;
-
-       cs->hw.ser->dev.name = GIGASET_MODULENAME;
-       cs->hw.ser->dev.id = cs->minor_index;
-       cs->hw.ser->dev.dev.release = gigaset_device_release;
-       rc = platform_device_register(&cs->hw.ser->dev);
-       if (rc != 0) {
-               pr_err("error %d registering platform device\n", rc);
-               kfree(cs->hw.ser);
-               cs->hw.ser = NULL;
-               return rc;
-       }
-
-       tasklet_init(&cs->write_tasklet,
-                    gigaset_modem_fill, (unsigned long) cs);
-       return 0;
-}
-
-/*
- * set modem control lines
- * Parameters:
- *     card state structure
- *     modem control line state ([TIOCM_DTR]|[TIOCM_RTS])
- * Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c
- * and by "if_lock" and "if_termios" in interface.c
- */
-static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
-                                 unsigned new_state)
-{
-       struct tty_struct *tty = cs->hw.ser->tty;
-       unsigned int set, clear;
-
-       WARN_ON(!tty || !tty->ops);
-       /* tiocmset is an optional tty driver method */
-       if (!tty->ops->tiocmset)
-               return -EINVAL;
-       set = new_state & ~old_state;
-       clear = old_state & ~new_state;
-       if (!set && !clear)
-               return 0;
-       gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear);
-       return tty->ops->tiocmset(tty, set, clear);
-}
-
-static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
-{
-       return -EINVAL;
-}
-
-static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
-{
-       return -EINVAL;
-}
-
-static const struct gigaset_ops ops = {
-       .write_cmd = gigaset_write_cmd,
-       .write_room = gigaset_write_room,
-       .chars_in_buffer = gigaset_chars_in_buffer,
-       .brkchars = gigaset_brkchars,
-       .init_bchannel = gigaset_init_bchannel,
-       .close_bchannel = gigaset_close_bchannel,
-       .initbcshw = gigaset_initbcshw,
-       .freebcshw = gigaset_freebcshw,
-       .reinitbcshw = gigaset_reinitbcshw,
-       .initcshw = gigaset_initcshw,
-       .freecshw = gigaset_freecshw,
-       .set_modem_ctrl = gigaset_set_modem_ctrl,
-       .baud_rate = gigaset_baud_rate,
-       .set_line_ctrl = gigaset_set_line_ctrl,
-       .send_skb = gigaset_m10x_send_skb,      /* asyncdata.c */
-       .handle_input = gigaset_m10x_input,     /* asyncdata.c */
-};
-
-
-/* Line Discipline Interface */
-/* ========================= */
-
-/* helper functions for cardstate refcounting */
-static struct cardstate *cs_get(struct tty_struct *tty)
-{
-       struct cardstate *cs = tty->disc_data;
-
-       if (!cs || !cs->hw.ser) {
-               gig_dbg(DEBUG_ANY, "%s: no cardstate", __func__);
-               return NULL;
-       }
-       atomic_inc(&cs->hw.ser->refcnt);
-       return cs;
-}
-
-static void cs_put(struct cardstate *cs)
-{
-       if (atomic_dec_and_test(&cs->hw.ser->refcnt))
-               complete(&cs->hw.ser->dead_cmp);
-}
-
-/*
- * Called by the tty driver when the line discipline is pushed onto the tty.
- * Called in process context.
- */
-static int
-gigaset_tty_open(struct tty_struct *tty)
-{
-       struct cardstate *cs;
-       int rc;
-
-       gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101");
-
-       pr_info(DRIVER_DESC "\n");
-
-       if (!driver) {
-               pr_err("%s: no driver structure\n", __func__);
-               return -ENODEV;
-       }
-
-       /* allocate memory for our device state and initialize it */
-       cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
-       if (!cs) {
-               rc = -ENODEV;
-               goto error;
-       }
-
-       cs->dev = &cs->hw.ser->dev.dev;
-       cs->hw.ser->tty = tty;
-       atomic_set(&cs->hw.ser->refcnt, 1);
-       init_completion(&cs->hw.ser->dead_cmp);
-       tty->disc_data = cs;
-
-       /* Set the amount of data we're willing to receive per call
-        * from the hardware driver to half of the input buffer size
-        * to leave some reserve.
-        * Note: We don't do flow control towards the hardware driver.
-        * If more data is received than will fit into the input buffer,
-        * it will be dropped and an error will be logged. This should
-        * never happen as the device is slow and the buffer size ample.
-        */
-       tty->receive_room = RBUFSIZE/2;
-
-       /* OK.. Initialization of the datastructures and the HW is done.. Now
-        * startup system and notify the LL that we are ready to run
-        */
-       if (startmode == SM_LOCKED)
-               cs->mstate = MS_LOCKED;
-       rc = gigaset_start(cs);
-       if (rc < 0) {
-               tasklet_kill(&cs->write_tasklet);
-               goto error;
-       }
-
-       gig_dbg(DEBUG_INIT, "Startup of HLL done");
-       return 0;
-
-error:
-       gig_dbg(DEBUG_INIT, "Startup of HLL failed");
-       tty->disc_data = NULL;
-       gigaset_freecs(cs);
-       return rc;
-}
-
-/*
- * Called by the tty driver when the line discipline is removed.
- * Called from process context.
- */
-static void
-gigaset_tty_close(struct tty_struct *tty)
-{
-       struct cardstate *cs = tty->disc_data;
-
-       gig_dbg(DEBUG_INIT, "Stopping HLL for Gigaset M101");
-
-       if (!cs) {
-               gig_dbg(DEBUG_INIT, "%s: no cardstate", __func__);
-               return;
-       }
-
-       /* prevent other callers from entering ldisc methods */
-       tty->disc_data = NULL;
-
-       if (!cs->hw.ser)
-               pr_err("%s: no hw cardstate\n", __func__);
-       else {
-               /* wait for running methods to finish */
-               if (!atomic_dec_and_test(&cs->hw.ser->refcnt))
-                       wait_for_completion(&cs->hw.ser->dead_cmp);
-       }
-
-       /* stop operations */
-       gigaset_stop(cs);
-       tasklet_kill(&cs->write_tasklet);
-       flush_send_queue(cs);
-       cs->dev = NULL;
-       gigaset_freecs(cs);
-
-       gig_dbg(DEBUG_INIT, "Shutdown of HLL done");
-}
-
-/*
- * Called by the tty driver when the tty line is hung up.
- * Wait for I/O to driver to complete and unregister ISDN device.
- * This is already done by the close routine, so just call that.
- * Called from process context.
- */
-static int gigaset_tty_hangup(struct tty_struct *tty)
-{
-       gigaset_tty_close(tty);
-       return 0;
-}
-
-/*
- * Ioctl on the tty.
- * Called in process context only.
- * May be re-entered by multiple ioctl calling threads.
- */
-static int
-gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
-                 unsigned int cmd, unsigned long arg)
-{
-       struct cardstate *cs = cs_get(tty);
-       int rc, val;
-       int __user *p = (int __user *)arg;
-
-       if (!cs)
-               return -ENXIO;
-
-       switch (cmd) {
-
-       case FIONREAD:
-               /* unused, always return zero */
-               val = 0;
-               rc = put_user(val, p);
-               break;
-
-       case TCFLSH:
-               /* flush our buffers and the serial port's buffer */
-               switch (arg) {
-               case TCIFLUSH:
-                       /* no own input buffer to flush */
-                       break;
-               case TCIOFLUSH:
-               case TCOFLUSH:
-                       flush_send_queue(cs);
-                       break;
-               }
-               /* fall through */
-
-       default:
-               /* pass through to underlying serial device */
-               rc = n_tty_ioctl_helper(tty, file, cmd, arg);
-               break;
-       }
-       cs_put(cs);
-       return rc;
-}
-
-/*
- * Called by the tty driver when a block of data has been received.
- * Will not be re-entered while running but other ldisc functions
- * may be called in parallel.
- * Can be called from hard interrupt level as well as soft interrupt
- * level or mainline.
- * Parameters:
- *     tty     tty structure
- *     buf     buffer containing received characters
- *     cflags  buffer containing error flags for received characters (ignored)
- *     count   number of received characters
- */
-static void
-gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
-                   char *cflags, int count)
-{
-       struct cardstate *cs = cs_get(tty);
-       unsigned tail, head, n;
-       struct inbuf_t *inbuf;
-
-       if (!cs)
-               return;
-       inbuf = cs->inbuf;
-       if (!inbuf) {
-               dev_err(cs->dev, "%s: no inbuf\n", __func__);
-               cs_put(cs);
-               return;
-       }
-
-       tail = inbuf->tail;
-       head = inbuf->head;
-       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes",
-               head, tail, count);
-
-       if (head <= tail) {
-               /* possible buffer wraparound */
-               n = min_t(unsigned, count, RBUFSIZE - tail);
-               memcpy(inbuf->data + tail, buf, n);
-               tail = (tail + n) % RBUFSIZE;
-               buf += n;
-               count -= n;
-       }
-
-       if (count > 0) {
-               /* tail < head and some data left */
-               n = head - tail - 1;
-               if (count > n) {
-                       dev_err(cs->dev,
-                               "inbuf overflow, discarding %d bytes\n",
-                               count - n);
-                       count = n;
-               }
-               memcpy(inbuf->data + tail, buf, count);
-               tail += count;
-       }
-
-       gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
-       inbuf->tail = tail;
-
-       /* Everything was received .. Push data into handler */
-       gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
-       gigaset_schedule_event(cs);
-       cs_put(cs);
-}
-
-/*
- * Called by the tty driver when there's room for more data to send.
- */
-static void
-gigaset_tty_wakeup(struct tty_struct *tty)
-{
-       struct cardstate *cs = cs_get(tty);
-
-       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-       if (!cs)
-               return;
-       tasklet_schedule(&cs->write_tasklet);
-       cs_put(cs);
-}
-
-static struct tty_ldisc_ops gigaset_ldisc = {
-       .owner          = THIS_MODULE,
-       .magic          = TTY_LDISC_MAGIC,
-       .name           = "ser_gigaset",
-       .open           = gigaset_tty_open,
-       .close          = gigaset_tty_close,
-       .hangup         = gigaset_tty_hangup,
-       .ioctl          = gigaset_tty_ioctl,
-       .receive_buf    = gigaset_tty_receive,
-       .write_wakeup   = gigaset_tty_wakeup,
-};
-
-
-/* Initialization / Shutdown */
-/* ========================= */
-
-static int __init ser_gigaset_init(void)
-{
-       int rc;
-
-       gig_dbg(DEBUG_INIT, "%s", __func__);
-       rc = platform_driver_register(&device_driver);
-       if (rc != 0) {
-               pr_err("error %d registering platform driver\n", rc);
-               return rc;
-       }
-
-       /* allocate memory for our driver state and initialize it */
-       driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
-                                   GIGASET_MODULENAME, GIGASET_DEVNAME,
-                                   &ops, THIS_MODULE);
-       if (!driver) {
-               rc = -ENOMEM;
-               goto error;
-       }
-
-       rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
-       if (rc != 0) {
-               pr_err("error %d registering line discipline\n", rc);
-               goto error;
-       }
-
-       return 0;
-
-error:
-       if (driver) {
-               gigaset_freedriver(driver);
-               driver = NULL;
-       }
-       platform_driver_unregister(&device_driver);
-       return rc;
-}
-
-static void __exit ser_gigaset_exit(void)
-{
-       int rc;
-
-       gig_dbg(DEBUG_INIT, "%s", __func__);
-
-       if (driver) {
-               gigaset_freedriver(driver);
-               driver = NULL;
-       }
-
-       rc = tty_unregister_ldisc(N_GIGASET_M101);
-       if (rc != 0)
-               pr_err("error %d unregistering line discipline\n", rc);
-
-       platform_driver_unregister(&device_driver);
-}
-
-module_init(ser_gigaset_init);
-module_exit(ser_gigaset_exit);
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
deleted file mode 100644 (file)
index eade36d..0000000
+++ /dev/null
@@ -1,949 +0,0 @@
-/*
- * USB driver for Gigaset 307x directly or using M105 Data.
- *
- * Copyright (c) 2001 by Stefan Eilers
- *                   and Hansjoerg Lipp <hjlipp@web.de>.
- *
- * This driver was derived from the USB skeleton driver by
- * Greg Kroah-Hartman <greg@kroah.com>
- *
- * =====================================================================
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- * =====================================================================
- */
-
-#include "gigaset.h"
-#include <linux/usb.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-/* Version Information */
-#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
-#define DRIVER_DESC "USB Driver for Gigaset 307x using M105"
-
-/* Module parameters */
-
-static int startmode = SM_ISDN;
-static int cidmode = 1;
-
-module_param(startmode, int, S_IRUGO);
-module_param(cidmode, int, S_IRUGO);
-MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
-MODULE_PARM_DESC(cidmode, "Call-ID mode");
-
-#define GIGASET_MINORS     1
-#define GIGASET_MINOR      8
-#define GIGASET_MODULENAME "usb_gigaset"
-#define GIGASET_DEVNAME    "ttyGU"
-
-/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
-#define IF_WRITEBUF 264
-
-/* Values for the Gigaset M105 Data */
-#define USB_M105_VENDOR_ID     0x0681
-#define USB_M105_PRODUCT_ID    0x0009
-
-/* table of devices that work with this driver */
-static const struct usb_device_id gigaset_table[] = {
-       { USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) },
-       { }                                     /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, gigaset_table);
-
-/*
- * Control requests (empty fields: 00)
- *
- *       RT|RQ|VALUE|INDEX|LEN  |DATA
- * In:
- *       C1 08             01
- *            Get flags (1 byte). Bits: 0=dtr,1=rts,3-7:?
- *       C1 0F             ll ll
- *            Get device information/status (llll: 0x200 and 0x40 seen).
- *            Real size: I only saw MIN(llll,0x64).
- *            Contents: seems to be always the same...
- *              offset 0x00: Length of this structure (0x64) (len: 1,2,3 bytes)
- *              offset 0x3c: String (16 bit chars): "MCCI USB Serial V2.0"
- *              rest:        ?
- * Out:
- *       41 11
- *            Initialize/reset device ?
- *       41 00 xx 00
- *            ? (xx=00 or 01; 01 on start, 00 on close)
- *       41 07 vv mm
- *            Set/clear flags vv=value, mm=mask (see RQ 08)
- *       41 12 xx
- *            Used before the following configuration requests are issued
- *            (with xx=0x0f). I've seen other values<0xf, though.
- *       41 01 xx xx
- *            Set baud rate. xxxx=ceil(0x384000/rate)=trunc(0x383fff/rate)+1.
- *       41 03 ps bb
- *            Set byte size and parity. p:  0x20=even,0x10=odd,0x00=no parity
- *                                     [    0x30: m, 0x40: s           ]
- *                                     [s:  0: 1 stop bit; 1: 1.5; 2: 2]
- *                                      bb: bits/byte (seen 7 and 8)
- *       41 13 -- -- -- -- 10 00 ww 00 00 00 xx 00 00 00 yy 00 00 00 zz 00 00 00
- *            ??
- *            Initialization: 01, 40, 00, 00
- *            Open device:    00  40, 00, 00
- *            yy and zz seem to be equal, either 0x00 or 0x0a
- *            (ww,xx) pairs seen: (00,00), (00,40), (01,40), (09,80), (19,80)
- *       41 19 -- -- -- -- 06 00 00 00 00 xx 11 13
- *            Used after every "configuration sequence" (RQ 12, RQs 01/03/13).
- *            xx is usually 0x00 but was 0x7e before starting data transfer
- *            in unimodem mode. So, this might be an array of characters that
- *            need special treatment ("commit all bufferd data"?), 11=^Q, 13=^S.
- *
- * Unimodem mode: use "modprobe ppp_async flag_time=0" as the device _needs_ two
- * flags per packet.
- */
-
-/* functions called if a device of this driver is connected/disconnected */
-static int gigaset_probe(struct usb_interface *interface,
-                        const struct usb_device_id *id);
-static void gigaset_disconnect(struct usb_interface *interface);
-
-/* functions called before/after suspend */
-static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
-static int gigaset_resume(struct usb_interface *intf);
-static int gigaset_pre_reset(struct usb_interface *intf);
-
-static struct gigaset_driver *driver;
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver gigaset_usb_driver = {
-       .name =         GIGASET_MODULENAME,
-       .probe =        gigaset_probe,
-       .disconnect =   gigaset_disconnect,
-       .id_table =     gigaset_table,
-       .suspend =      gigaset_suspend,
-       .resume =       gigaset_resume,
-       .reset_resume = gigaset_resume,
-       .pre_reset =    gigaset_pre_reset,
-       .post_reset =   gigaset_resume,
-       .disable_hub_initiated_lpm = 1,
-};
-
-struct usb_cardstate {
-       struct usb_device       *udev;          /* usb device pointer */
-       struct usb_interface    *interface;     /* interface for this device */
-       int                     busy;           /* bulk output in progress */
-
-       /* Output buffer */
-       unsigned char           *bulk_out_buffer;
-       int                     bulk_out_size;
-       int                     bulk_out_epnum;
-       struct urb              *bulk_out_urb;
-
-       /* Input buffer */
-       unsigned char           *rcvbuf;
-       int                     rcvbuf_size;
-       struct urb              *read_urb;
-
-       char                    bchars[6];              /* for request 0x19 */
-};
-
-static inline unsigned tiocm_to_gigaset(unsigned state)
-{
-       return ((state & TIOCM_DTR) ? 1 : 0) | ((state & TIOCM_RTS) ? 2 : 0);
-}
-
-static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
-                                 unsigned new_state)
-{
-       struct usb_device *udev = cs->hw.usb->udev;
-       unsigned mask, val;
-       int r;
-
-       mask = tiocm_to_gigaset(old_state ^ new_state);
-       val = tiocm_to_gigaset(new_state);
-
-       gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask);
-       r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41,
-                           (val & 0xff) | ((mask & 0xff) << 8), 0,
-                           NULL, 0, 2000 /* timeout? */);
-       if (r < 0)
-               return r;
-       return 0;
-}
-
-/*
- * Set M105 configuration value
- * using undocumented device commands reverse engineered from USB traces
- * of the Siemens Windows driver
- */
-static int set_value(struct cardstate *cs, u8 req, u16 val)
-{
-       struct usb_device *udev = cs->hw.usb->udev;
-       int r, r2;
-
-       gig_dbg(DEBUG_USBREQ, "request %02x (%04x)",
-               (unsigned)req, (unsigned)val);
-       r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x12, 0x41,
-                           0xf /*?*/, 0, NULL, 0, 2000 /*?*/);
-       /* no idea what this does */
-       if (r < 0) {
-               dev_err(&udev->dev, "error %d on request 0x12\n", -r);
-               return r;
-       }
-
-       r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), req, 0x41,
-                           val, 0, NULL, 0, 2000 /*?*/);
-       if (r < 0)
-               dev_err(&udev->dev, "error %d on request 0x%02x\n",
-                       -r, (unsigned)req);
-
-       r2 = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
-                            0, 0, cs->hw.usb->bchars, 6, 2000 /*?*/);
-       if (r2 < 0)
-               dev_err(&udev->dev, "error %d on request 0x19\n", -r2);
-
-       return r < 0 ? r : (r2 < 0 ? r2 : 0);
-}
-
-/*
- * set the baud rate on the internal serial adapter
- * using the undocumented parameter setting command
- */
-static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
-{
-       u16 val;
-       u32 rate;
-
-       cflag &= CBAUD;
-
-       switch (cflag) {
-       case    B300: rate =     300; break;
-       case    B600: rate =     600; break;
-       case   B1200: rate =    1200; break;
-       case   B2400: rate =    2400; break;
-       case   B4800: rate =    4800; break;
-       case   B9600: rate =    9600; break;
-       case  B19200: rate =   19200; break;
-       case  B38400: rate =   38400; break;
-       case  B57600: rate =   57600; break;
-       case B115200: rate =  115200; break;
-       default:
-               rate =  9600;
-               dev_err(cs->dev, "unsupported baudrate request 0x%x,"
-                       " using default of B9600\n", cflag);
-       }
-
-       val = 0x383fff / rate + 1;
-
-       return set_value(cs, 1, val);
-}
-
-/*
- * set the line format on the internal serial adapter
- * using the undocumented parameter setting command
- */
-static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
-{
-       u16 val = 0;
-
-       /* set the parity */
-       if (cflag & PARENB)
-               val |= (cflag & PARODD) ? 0x10 : 0x20;
-
-       /* set the number of data bits */
-       switch (cflag & CSIZE) {
-       case CS5:
-               val |= 5 << 8; break;
-       case CS6:
-               val |= 6 << 8; break;
-       case CS7:
-               val |= 7 << 8; break;
-       case CS8:
-               val |= 8 << 8; break;
-       default:
-               dev_err(cs->dev, "CSIZE was not CS5-CS8, using default of 8\n");
-               val |= 8 << 8;
-               break;
-       }
-
-       /* set the number of stop bits */
-       if (cflag & CSTOPB) {
-               if ((cflag & CSIZE) == CS5)
-                       val |= 1; /* 1.5 stop bits */
-               else
-                       val |= 2; /* 2 stop bits */
-       }
-
-       return set_value(cs, 3, val);
-}
-
-
-/*============================================================================*/
-static int gigaset_init_bchannel(struct bc_state *bcs)
-{
-       /* nothing to do for M10x */
-       gigaset_bchannel_up(bcs);
-       return 0;
-}
-
-static int gigaset_close_bchannel(struct bc_state *bcs)
-{
-       /* nothing to do for M10x */
-       gigaset_bchannel_down(bcs);
-       return 0;
-}
-
-static int write_modem(struct cardstate *cs);
-static int send_cb(struct cardstate *cs);
-
-
-/* Write tasklet handler: Continue sending current skb, or send command, or
- * start sending an skb from the send queue.
- */
-static void gigaset_modem_fill(unsigned long data)
-{
-       struct cardstate *cs = (struct cardstate *) data;
-       struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
-
-       gig_dbg(DEBUG_OUTPUT, "modem_fill");
-
-       if (cs->hw.usb->busy) {
-               gig_dbg(DEBUG_OUTPUT, "modem_fill: busy");
-               return;
-       }
-
-again:
-       if (!bcs->tx_skb) {     /* no skb is being sent */
-               if (cs->cmdbuf) {       /* commands to send? */
-                       gig_dbg(DEBUG_OUTPUT, "modem_fill: cb");
-                       if (send_cb(cs) < 0) {
-                               gig_dbg(DEBUG_OUTPUT,
-                                       "modem_fill: send_cb failed");
-                               goto again; /* no callback will be called! */
-                       }
-                       return;
-               }
-
-               /* skbs to send? */
-               bcs->tx_skb = skb_dequeue(&bcs->squeue);
-               if (!bcs->tx_skb)
-                       return;
-
-               gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)!",
-                       (unsigned long) bcs->tx_skb);
-       }
-
-       gig_dbg(DEBUG_OUTPUT, "modem_fill: tx_skb");
-       if (write_modem(cs) < 0) {
-               gig_dbg(DEBUG_OUTPUT, "modem_fill: write_modem failed");
-               goto again;     /* no callback will be called! */
-       }
-}
-
-/*
- * Interrupt Input URB completion routine
- */
-static void gigaset_read_int_callback(struct urb *urb)
-{
-       struct cardstate *cs = urb->context;
-       struct inbuf_t *inbuf = cs->inbuf;
-       int status = urb->status;
-       int r;
-       unsigned numbytes;
-       unsigned char *src;
-       unsigned long flags;
-
-       if (!status) {
-               numbytes = urb->actual_length;
-
-               if (numbytes) {
-                       src = cs->hw.usb->rcvbuf;
-                       if (unlikely(*src))
-                               dev_warn(cs->dev,
-                                        "%s: There was no leading 0, but 0x%02x!\n",
-                                        __func__, (unsigned) *src);
-                       ++src; /* skip leading 0x00 */
-                       --numbytes;
-                       if (gigaset_fill_inbuf(inbuf, src, numbytes)) {
-                               gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
-                               gigaset_schedule_event(inbuf->cs);
-                       }
-               } else
-                       gig_dbg(DEBUG_INTR, "Received zero block length");
-       } else {
-               /* The urb might have been killed. */
-               gig_dbg(DEBUG_ANY, "%s - nonzero status received: %d",
-                       __func__, status);
-               if (status == -ENOENT || status == -ESHUTDOWN)
-                       /* killed or endpoint shutdown: don't resubmit */
-                       return;
-       }
-
-       /* resubmit URB */
-       spin_lock_irqsave(&cs->lock, flags);
-       if (!cs->connected) {
-               spin_unlock_irqrestore(&cs->lock, flags);
-               pr_err("%s: disconnected\n", __func__);
-               return;
-       }
-       r = usb_submit_urb(urb, GFP_ATOMIC);
-       spin_unlock_irqrestore(&cs->lock, flags);
-       if (r)
-               dev_err(cs->dev, "error %d resubmitting URB\n", -r);
-}
-
-
-/* This callback routine is called when data was transmitted to the device. */
-static void gigaset_write_bulk_callback(struct urb *urb)
-{
-       struct cardstate *cs = urb->context;
-       int status = urb->status;
-       unsigned long flags;
-
-       switch (status) {
-       case 0:                 /* normal completion */
-               break;
-       case -ENOENT:           /* killed */
-               gig_dbg(DEBUG_ANY, "%s: killed", __func__);
-               cs->hw.usb->busy = 0;
-               return;
-       default:
-               dev_err(cs->dev, "bulk transfer failed (status %d)\n",
-                       -status);
-               /* That's all we can do. Communication problems
-                  are handled by timeouts or network protocols. */
-       }
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (!cs->connected) {
-               pr_err("%s: disconnected\n", __func__);
-       } else {
-               cs->hw.usb->busy = 0;
-               tasklet_schedule(&cs->write_tasklet);
-       }
-       spin_unlock_irqrestore(&cs->lock, flags);
-}
-
-static int send_cb(struct cardstate *cs)
-{
-       struct cmdbuf_t *cb = cs->cmdbuf;
-       unsigned long flags;
-       int count;
-       int status = -ENOENT;
-       struct usb_cardstate *ucs = cs->hw.usb;
-
-       do {
-               if (!cb->len) {
-                       spin_lock_irqsave(&cs->cmdlock, flags);
-                       cs->cmdbytes -= cs->curlen;
-                       gig_dbg(DEBUG_OUTPUT, "send_cb: sent %u bytes, %u left",
-                               cs->curlen, cs->cmdbytes);
-                       cs->cmdbuf = cb->next;
-                       if (cs->cmdbuf) {
-                               cs->cmdbuf->prev = NULL;
-                               cs->curlen = cs->cmdbuf->len;
-                       } else {
-                               cs->lastcmdbuf = NULL;
-                               cs->curlen = 0;
-                       }
-                       spin_unlock_irqrestore(&cs->cmdlock, flags);
-
-                       if (cb->wake_tasklet)
-                               tasklet_schedule(cb->wake_tasklet);
-                       kfree(cb);
-
-                       cb = cs->cmdbuf;
-               }
-
-               if (cb) {
-                       count = min(cb->len, ucs->bulk_out_size);
-                       gig_dbg(DEBUG_OUTPUT, "send_cb: send %d bytes", count);
-
-                       usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
-                                         usb_sndbulkpipe(ucs->udev,
-                                                         ucs->bulk_out_epnum),
-                                         cb->buf + cb->offset, count,
-                                         gigaset_write_bulk_callback, cs);
-
-                       cb->offset += count;
-                       cb->len -= count;
-                       ucs->busy = 1;
-
-                       spin_lock_irqsave(&cs->lock, flags);
-                       status = cs->connected ?
-                               usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) :
-                               -ENODEV;
-                       spin_unlock_irqrestore(&cs->lock, flags);
-
-                       if (status) {
-                               ucs->busy = 0;
-                               dev_err(cs->dev,
-                                       "could not submit urb (error %d)\n",
-                                       -status);
-                               cb->len = 0; /* skip urb => remove cb+wakeup
-                                               in next loop cycle */
-                       }
-               }
-       } while (cb && status); /* next command on error */
-
-       return status;
-}
-
-/* Send command to device. */
-static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
-{
-       unsigned long flags;
-       int len;
-
-       gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
-                          DEBUG_TRANSCMD : DEBUG_LOCKCMD,
-                          "CMD Transmit", cb->len, cb->buf);
-
-       spin_lock_irqsave(&cs->cmdlock, flags);
-       cb->prev = cs->lastcmdbuf;
-       if (cs->lastcmdbuf)
-               cs->lastcmdbuf->next = cb;
-       else {
-               cs->cmdbuf = cb;
-               cs->curlen = cb->len;
-       }
-       cs->cmdbytes += cb->len;
-       cs->lastcmdbuf = cb;
-       spin_unlock_irqrestore(&cs->cmdlock, flags);
-
-       spin_lock_irqsave(&cs->lock, flags);
-       len = cb->len;
-       if (cs->connected)
-               tasklet_schedule(&cs->write_tasklet);
-       spin_unlock_irqrestore(&cs->lock, flags);
-       return len;
-}
-
-static int gigaset_write_room(struct cardstate *cs)
-{
-       unsigned bytes;
-
-       bytes = cs->cmdbytes;
-       return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
-}
-
-static int gigaset_chars_in_buffer(struct cardstate *cs)
-{
-       return cs->cmdbytes;
-}
-
-/*
- * set the break characters on the internal serial adapter
- * using undocumented device commands reverse engineered from USB traces
- * of the Siemens Windows driver
- */
-static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
-{
-       struct usb_device *udev = cs->hw.usb->udev;
-
-       gigaset_dbg_buffer(DEBUG_USBREQ, "brkchars", 6, buf);
-       memcpy(cs->hw.usb->bchars, buf, 6);
-       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
-                              0, 0, &buf, 6, 2000);
-}
-
-static void gigaset_freebcshw(struct bc_state *bcs)
-{
-       /* unused */
-}
-
-/* Initialize the b-channel structure */
-static int gigaset_initbcshw(struct bc_state *bcs)
-{
-       /* unused */
-       bcs->hw.usb = NULL;
-       return 0;
-}
-
-static void gigaset_reinitbcshw(struct bc_state *bcs)
-{
-       /* nothing to do for M10x */
-}
-
-static void gigaset_freecshw(struct cardstate *cs)
-{
-       tasklet_kill(&cs->write_tasklet);
-       kfree(cs->hw.usb);
-}
-
-static int gigaset_initcshw(struct cardstate *cs)
-{
-       struct usb_cardstate *ucs;
-
-       cs->hw.usb = ucs =
-               kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
-       if (!ucs) {
-               pr_err("out of memory\n");
-               return -ENOMEM;
-       }
-
-       ucs->bchars[0] = 0;
-       ucs->bchars[1] = 0;
-       ucs->bchars[2] = 0;
-       ucs->bchars[3] = 0;
-       ucs->bchars[4] = 0x11;
-       ucs->bchars[5] = 0x13;
-       ucs->bulk_out_buffer = NULL;
-       ucs->bulk_out_urb = NULL;
-       ucs->read_urb = NULL;
-       tasklet_init(&cs->write_tasklet,
-                    gigaset_modem_fill, (unsigned long) cs);
-
-       return 0;
-}
-
-/* Send data from current skb to the device. */
-static int write_modem(struct cardstate *cs)
-{
-       int ret = 0;
-       int count;
-       struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
-       struct usb_cardstate *ucs = cs->hw.usb;
-       unsigned long flags;
-
-       gig_dbg(DEBUG_OUTPUT, "len: %d...", bcs->tx_skb->len);
-
-       if (!bcs->tx_skb->len) {
-               dev_kfree_skb_any(bcs->tx_skb);
-               bcs->tx_skb = NULL;
-               return -EINVAL;
-       }
-
-       /* Copy data to bulk out buffer and transmit data */
-       count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
-       skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
-       skb_pull(bcs->tx_skb, count);
-       ucs->busy = 1;
-       gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count);
-
-       spin_lock_irqsave(&cs->lock, flags);
-       if (cs->connected) {
-               usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
-                                 usb_sndbulkpipe(ucs->udev,
-                                                 ucs->bulk_out_epnum),
-                                 ucs->bulk_out_buffer, count,
-                                 gigaset_write_bulk_callback, cs);
-               ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC);
-       } else {
-               ret = -ENODEV;
-       }
-       spin_unlock_irqrestore(&cs->lock, flags);
-
-       if (ret) {
-               dev_err(cs->dev, "could not submit urb (error %d)\n", -ret);
-               ucs->busy = 0;
-       }
-
-       if (!bcs->tx_skb->len) {
-               /* skb sent completely */
-               gigaset_skb_sent(bcs, bcs->tx_skb);
-
-               gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
-                       (unsigned long) bcs->tx_skb);
-               dev_kfree_skb_any(bcs->tx_skb);
-               bcs->tx_skb = NULL;
-       }
-
-       return ret;
-}
-
-static int gigaset_probe(struct usb_interface *interface,
-                        const struct usb_device_id *id)
-{
-       int retval;
-       struct usb_device *udev = interface_to_usbdev(interface);
-       struct usb_host_interface *hostif = interface->cur_altsetting;
-       struct cardstate *cs = NULL;
-       struct usb_cardstate *ucs = NULL;
-       struct usb_endpoint_descriptor *endpoint;
-       int buffer_size;
-
-       gig_dbg(DEBUG_ANY, "%s: Check if device matches ...", __func__);
-
-       /* See if the device offered us matches what we can accept */
-       if ((le16_to_cpu(udev->descriptor.idVendor)  != USB_M105_VENDOR_ID) ||
-           (le16_to_cpu(udev->descriptor.idProduct) != USB_M105_PRODUCT_ID)) {
-               gig_dbg(DEBUG_ANY, "device ID (0x%x, 0x%x) not for me - skip",
-                       le16_to_cpu(udev->descriptor.idVendor),
-                       le16_to_cpu(udev->descriptor.idProduct));
-               return -ENODEV;
-       }
-       if (hostif->desc.bInterfaceNumber != 0) {
-               gig_dbg(DEBUG_ANY, "interface %d not for me - skip",
-                       hostif->desc.bInterfaceNumber);
-               return -ENODEV;
-       }
-       if (hostif->desc.bAlternateSetting != 0) {
-               dev_notice(&udev->dev, "unsupported altsetting %d - skip",
-                          hostif->desc.bAlternateSetting);
-               return -ENODEV;
-       }
-       if (hostif->desc.bInterfaceClass != 255) {
-               dev_notice(&udev->dev, "unsupported interface class %d - skip",
-                          hostif->desc.bInterfaceClass);
-               return -ENODEV;
-       }
-
-       dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);
-
-       /* allocate memory for our device state and initialize it */
-       cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
-       if (!cs)
-               return -ENODEV;
-       ucs = cs->hw.usb;
-
-       /* save off device structure ptrs for later use */
-       usb_get_dev(udev);
-       ucs->udev = udev;
-       ucs->interface = interface;
-       cs->dev = &interface->dev;
-
-       /* save address of controller structure */
-       usb_set_intfdata(interface, cs);
-
-       endpoint = &hostif->endpoint[0].desc;
-
-       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
-       ucs->bulk_out_size = buffer_size;
-       ucs->bulk_out_epnum = usb_endpoint_num(endpoint);
-       ucs->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
-       if (!ucs->bulk_out_buffer) {
-               dev_err(cs->dev, "Couldn't allocate bulk_out_buffer\n");
-               retval = -ENOMEM;
-               goto error;
-       }
-
-       ucs->bulk_out_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (!ucs->bulk_out_urb) {
-               dev_err(cs->dev, "Couldn't allocate bulk_out_urb\n");
-               retval = -ENOMEM;
-               goto error;
-       }
-
-       endpoint = &hostif->endpoint[1].desc;
-
-       ucs->busy = 0;
-
-       ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (!ucs->read_urb) {
-               dev_err(cs->dev, "No free urbs available\n");
-               retval = -ENOMEM;
-               goto error;
-       }
-       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
-       ucs->rcvbuf_size = buffer_size;
-       ucs->rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
-       if (!ucs->rcvbuf) {
-               dev_err(cs->dev, "Couldn't allocate rcvbuf\n");
-               retval = -ENOMEM;
-               goto error;
-       }
-       /* Fill the interrupt urb and send it to the core */
-       usb_fill_int_urb(ucs->read_urb, udev,
-                        usb_rcvintpipe(udev, usb_endpoint_num(endpoint)),
-                        ucs->rcvbuf, buffer_size,
-                        gigaset_read_int_callback,
-                        cs, endpoint->bInterval);
-
-       retval = usb_submit_urb(ucs->read_urb, GFP_KERNEL);
-       if (retval) {
-               dev_err(cs->dev, "Could not submit URB (error %d)\n", -retval);
-               goto error;
-       }
-
-       /* tell common part that the device is ready */
-       if (startmode == SM_LOCKED)
-               cs->mstate = MS_LOCKED;
-
-       retval = gigaset_start(cs);
-       if (retval < 0) {
-               tasklet_kill(&cs->write_tasklet);
-               goto error;
-       }
-       return 0;
-
-error:
-       usb_kill_urb(ucs->read_urb);
-       kfree(ucs->bulk_out_buffer);
-       usb_free_urb(ucs->bulk_out_urb);
-       kfree(ucs->rcvbuf);
-       usb_free_urb(ucs->read_urb);
-       usb_set_intfdata(interface, NULL);
-       ucs->read_urb = ucs->bulk_out_urb = NULL;
-       ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
-       usb_put_dev(ucs->udev);
-       ucs->udev = NULL;
-       ucs->interface = NULL;
-       gigaset_freecs(cs);
-       return retval;
-}
-
-static void gigaset_disconnect(struct usb_interface *interface)
-{
-       struct cardstate *cs;
-       struct usb_cardstate *ucs;
-
-       cs = usb_get_intfdata(interface);
-       ucs = cs->hw.usb;
-
-       dev_info(cs->dev, "disconnecting Gigaset USB adapter\n");
-
-       usb_kill_urb(ucs->read_urb);
-
-       gigaset_stop(cs);
-
-       usb_set_intfdata(interface, NULL);
-       tasklet_kill(&cs->write_tasklet);
-
-       usb_kill_urb(ucs->bulk_out_urb);
-
-       kfree(ucs->bulk_out_buffer);
-       usb_free_urb(ucs->bulk_out_urb);
-       kfree(ucs->rcvbuf);
-       usb_free_urb(ucs->read_urb);
-       ucs->read_urb = ucs->bulk_out_urb = NULL;
-       ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
-
-       usb_put_dev(ucs->udev);
-       ucs->interface = NULL;
-       ucs->udev = NULL;
-       cs->dev = NULL;
-       gigaset_freecs(cs);
-}
-
-/* gigaset_suspend
- * This function is called before the USB connection is suspended or reset.
- */
-static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
-{
-       struct cardstate *cs = usb_get_intfdata(intf);
-
-       /* stop activity */
-       cs->connected = 0;      /* prevent rescheduling */
-       usb_kill_urb(cs->hw.usb->read_urb);
-       tasklet_kill(&cs->write_tasklet);
-       usb_kill_urb(cs->hw.usb->bulk_out_urb);
-
-       gig_dbg(DEBUG_SUSPEND, "suspend complete");
-       return 0;
-}
-
-/* gigaset_resume
- * This function is called after the USB connection has been resumed or reset.
- */
-static int gigaset_resume(struct usb_interface *intf)
-{
-       struct cardstate *cs = usb_get_intfdata(intf);
-       int rc;
-
-       /* resubmit interrupt URB */
-       cs->connected = 1;
-       rc = usb_submit_urb(cs->hw.usb->read_urb, GFP_KERNEL);
-       if (rc) {
-               dev_err(cs->dev, "Could not submit read URB (error %d)\n", -rc);
-               return rc;
-       }
-
-       gig_dbg(DEBUG_SUSPEND, "resume complete");
-       return 0;
-}
-
-/* gigaset_pre_reset
- * This function is called before the USB connection is reset.
- */
-static int gigaset_pre_reset(struct usb_interface *intf)
-{
-       /* same as suspend */
-       return gigaset_suspend(intf, PMSG_ON);
-}
-
-static const struct gigaset_ops ops = {
-       .write_cmd = gigaset_write_cmd,
-       .write_room = gigaset_write_room,
-       .chars_in_buffer = gigaset_chars_in_buffer,
-       .brkchars = gigaset_brkchars,
-       .init_bchannel = gigaset_init_bchannel,
-       .close_bchannel = gigaset_close_bchannel,
-       .initbcshw = gigaset_initbcshw,
-       .freebcshw = gigaset_freebcshw,
-       .reinitbcshw = gigaset_reinitbcshw,
-       .initcshw = gigaset_initcshw,
-       .freecshw = gigaset_freecshw,
-       .set_modem_ctrl = gigaset_set_modem_ctrl,
-       .baud_rate = gigaset_baud_rate,
-       .set_line_ctrl = gigaset_set_line_ctrl,
-       .send_skb = gigaset_m10x_send_skb,
-       .handle_input = gigaset_m10x_input,
-};
-
-/*
- * This function is called while kernel-module is loaded
- */
-static int __init usb_gigaset_init(void)
-{
-       int result;
-
-       /* allocate memory for our driver state and initialize it */
-       driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
-                                   GIGASET_MODULENAME, GIGASET_DEVNAME,
-                                   &ops, THIS_MODULE);
-       if (driver == NULL) {
-               result = -ENOMEM;
-               goto error;
-       }
-
-       /* register this driver with the USB subsystem */
-       result = usb_register(&gigaset_usb_driver);
-       if (result < 0) {
-               pr_err("error %d registering USB driver\n", -result);
-               goto error;
-       }
-
-       pr_info(DRIVER_DESC "\n");
-       return 0;
-
-error:
-       if (driver)
-               gigaset_freedriver(driver);
-       driver = NULL;
-       return result;
-}
-
-/*
- * This function is called while unloading the kernel-module
- */
-static void __exit usb_gigaset_exit(void)
-{
-       int i;
-
-       gigaset_blockdriver(driver); /* => probe will fail
-                                     * => no gigaset_start any more
-                                     */
-
-       /* stop all connected devices */
-       for (i = 0; i < driver->minors; i++)
-               gigaset_shutdown(driver->cs + i);
-
-       /* from now on, no isdn callback should be possible */
-
-       /* deregister this driver with the USB subsystem */
-       usb_deregister(&gigaset_usb_driver);
-       /* this will call the disconnect-callback */
-       /* from now on, no disconnect/probe callback should be running */
-
-       gigaset_freedriver(driver);
-       driver = NULL;
-}
-
-
-module_init(usb_gigaset_init);
-module_exit(usb_gigaset_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/isdn/hardware/Kconfig b/drivers/isdn/hardware/Kconfig
deleted file mode 100644 (file)
index 0d609b5..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# ISDN hardware drivers
-#
-comment "CAPI hardware drivers"
-
-source "drivers/isdn/hardware/avm/Kconfig"
-
index a43760a..96f9eb2 100644 (file)
@@ -3,5 +3,4 @@
 
 # Object files in subdirectories
 
-obj-$(CONFIG_CAPI_AVM)         += avm/
 obj-$(CONFIG_MISDN)            += mISDN/
diff --git a/drivers/isdn/hardware/avm/Kconfig b/drivers/isdn/hardware/avm/Kconfig
deleted file mode 100644 (file)
index 81483db..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# ISDN AVM drivers
-#
-
-menuconfig CAPI_AVM
-       bool "Active AVM cards"
-       help
-         Enable support for AVM active ISDN cards.
-
-if CAPI_AVM
-
-config ISDN_DRV_AVMB1_B1ISA
-       tristate "AVM B1 ISA support"
-       depends on ISA
-       help
-         Enable support for the ISA version of the AVM B1 card.
-
-config ISDN_DRV_AVMB1_B1PCI
-       tristate "AVM B1 PCI support"
-       depends on PCI
-       help
-         Enable support for the PCI version of the AVM B1 card.
-
-config ISDN_DRV_AVMB1_B1PCIV4
-       bool "AVM B1 PCI V4 support"
-       depends on ISDN_DRV_AVMB1_B1PCI
-       help
-         Enable support for the V4 version of AVM B1 PCI card.
-
-config ISDN_DRV_AVMB1_T1ISA
-       tristate "AVM T1/T1-B ISA support"
-       depends on ISA
-       help
-         Enable support for the AVM T1 T1B card.
-         Note: This is a PRI card and handle 30 B-channels.
-
-config ISDN_DRV_AVMB1_B1PCMCIA
-       tristate "AVM B1/M1/M2 PCMCIA support"
-       depends on PCMCIA
-       help
-         Enable support for the PCMCIA version of the AVM B1 card.
-
-config ISDN_DRV_AVMB1_AVM_CS
-       tristate "AVM B1/M1/M2 PCMCIA cs module"
-       depends on ISDN_DRV_AVMB1_B1PCMCIA
-       help
-         Enable the PCMCIA client driver for the AVM B1/M1/M2
-         PCMCIA cards.
-
-config ISDN_DRV_AVMB1_T1PCI
-       tristate "AVM T1/T1-B PCI support"
-       depends on PCI
-       help
-         Enable support for the AVM T1 T1B card.
-         Note: This is a PRI card and handle 30 B-channels.
-
-config ISDN_DRV_AVMB1_C4
-       tristate "AVM C4/C2 support"
-       depends on PCI
-       help
-         Enable support for the AVM C4/C2 PCI cards.
-         These cards handle 4/2 BRI ISDN lines (8/4 channels).
-
-endif # CAPI_AVM
diff --git a/drivers/isdn/hardware/avm/Makefile b/drivers/isdn/hardware/avm/Makefile
deleted file mode 100644 (file)
index 3830a05..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# Makefile for the AVM ISDN device drivers
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_ISDN_DRV_AVMB1_B1ISA)     += b1isa.o b1.o
-obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCI)     += b1pci.o b1.o b1dma.o
-obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCMCIA)  += b1pcmcia.o b1.o
-obj-$(CONFIG_ISDN_DRV_AVMB1_AVM_CS)    += avm_cs.o
-obj-$(CONFIG_ISDN_DRV_AVMB1_T1ISA)     += t1isa.o b1.o
-obj-$(CONFIG_ISDN_DRV_AVMB1_T1PCI)     += t1pci.o b1.o b1dma.o
-obj-$(CONFIG_ISDN_DRV_AVMB1_C4)                += c4.o b1.o
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
deleted file mode 100644 (file)
index 62b8030..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
- *
- * A PCMCIA client driver for AVM B1/M1/M2
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/ptrace.h>
-#include <linux/string.h>
-#include <linux/tty.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <asm/io.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-#include <linux/skbuff.h>
-#include <linux/capi.h>
-#include <linux/b1lli.h>
-#include <linux/b1pcmcia.h>
-
-/*====================================================================*/
-
-MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-/*====================================================================*/
-
-static int avmcs_config(struct pcmcia_device *link);
-static void avmcs_release(struct pcmcia_device *link);
-static void avmcs_detach(struct pcmcia_device *p_dev);
-
-static int avmcs_probe(struct pcmcia_device *p_dev)
-{
-       /* General socket configuration */
-       p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-       p_dev->config_index = 1;
-       p_dev->config_regs = PRESENT_OPTION;
-
-       return avmcs_config(p_dev);
-} /* avmcs_attach */
-
-
-static void avmcs_detach(struct pcmcia_device *link)
-{
-       avmcs_release(link);
-} /* avmcs_detach */
-
-static int avmcs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
-{
-       p_dev->resource[0]->end = 16;
-       p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
-       p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
-
-       return pcmcia_request_io(p_dev);
-}
-
-static int avmcs_config(struct pcmcia_device *link)
-{
-       int i = -1;
-       char devname[128];
-       int cardtype;
-       int (*addcard)(unsigned int port, unsigned irq);
-
-       devname[0] = 0;
-       if (link->prod_id[1])
-               strlcpy(devname, link->prod_id[1], sizeof(devname));
-
-       /*
-        * find IO port
-        */
-       if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
-               return -ENODEV;
-
-       do {
-               if (!link->irq) {
-                       /* undo */
-                       pcmcia_disable_device(link);
-                       break;
-               }
-
-               /*
-                * configure the PCMCIA socket
-                */
-               i = pcmcia_enable_device(link);
-               if (i != 0) {
-                       pcmcia_disable_device(link);
-                       break;
-               }
-
-       } while (0);
-
-       if (devname[0]) {
-               char *s = strrchr(devname, ' ');
-               if (!s)
-                       s = devname;
-               else s++;
-               if (strcmp("M1", s) == 0) {
-                       cardtype = AVM_CARDTYPE_M1;
-               } else if (strcmp("M2", s) == 0) {
-                       cardtype = AVM_CARDTYPE_M2;
-               } else {
-                       cardtype = AVM_CARDTYPE_B1;
-               }
-       } else
-               cardtype = AVM_CARDTYPE_B1;
-
-       /* If any step failed, release any partially configured state */
-       if (i != 0) {
-               avmcs_release(link);
-               return -ENODEV;
-       }
-
-
-       switch (cardtype) {
-       case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
-       case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
-       default:
-       case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
-       }
-       if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
-               dev_err(&link->dev,
-                       "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
-                       (unsigned int) link->resource[0]->start, link->irq);
-               avmcs_release(link);
-               return -ENODEV;
-       }
-       return 0;
-
-} /* avmcs_config */
-
-
-static void avmcs_release(struct pcmcia_device *link)
-{
-       b1pcmcia_delcard(link->resource[0]->start, link->irq);
-       pcmcia_disable_device(link);
-} /* avmcs_release */
-
-
-static const struct pcmcia_device_id avmcs_ids[] = {
-       PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
-       PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
-       PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
-       PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
-
-static struct pcmcia_driver avmcs_driver = {
-       .owner  = THIS_MODULE,
-       .name           = "avm_cs",
-       .probe = avmcs_probe,
-       .remove = avmcs_detach,
-       .id_table = avmcs_ids,
-};
-module_pcmcia_driver(avmcs_driver);
diff --git a/drivers/isdn/hardware/avm/avmcard.h b/drivers/isdn/hardware/avm/avmcard.h
deleted file mode 100644 (file)
index cdfa89c..0000000
+++ /dev/null
@@ -1,581 +0,0 @@
-/* $Id: avmcard.h,v 1.1.4.1.2.1 2001/12/21 15:00:17 kai Exp $
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#ifndef _AVMCARD_H_
-#define _AVMCARD_H_
-
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-
-#define        AVMB1_PORTLEN           0x1f
-#define AVM_MAXVERSION         8
-#define AVM_NCCI_PER_CHANNEL   4
-
-/*
- * Versions
- */
-
-#define        VER_DRIVER      0
-#define        VER_CARDTYPE    1
-#define        VER_HWID        2
-#define        VER_SERIAL      3
-#define        VER_OPTION      4
-#define        VER_PROTO       5
-#define        VER_PROFILE     6
-#define        VER_CAPI        7
-
-enum avmcardtype {
-       avm_b1isa,
-       avm_b1pci,
-       avm_b1pcmcia,
-       avm_m1,
-       avm_m2,
-       avm_t1isa,
-       avm_t1pci,
-       avm_c4,
-       avm_c2
-};
-
-typedef struct avmcard_dmabuf {
-       long        size;
-       u8       *dmabuf;
-       dma_addr_t  dmaaddr;
-} avmcard_dmabuf;
-
-typedef struct avmcard_dmainfo {
-       u32                recvlen;
-       avmcard_dmabuf       recvbuf;
-
-       avmcard_dmabuf       sendbuf;
-       struct sk_buff_head  send_queue;
-
-       struct pci_dev      *pcidev;
-} avmcard_dmainfo;
-
-typedef        struct avmctrl_info {
-       char cardname[32];
-
-       int versionlen;
-       char versionbuf[1024];
-       char *version[AVM_MAXVERSION];
-
-       char infobuf[128];      /* for function procinfo */
-
-       struct avmcard  *card;
-       struct capi_ctr  capi_ctrl;
-
-       struct list_head ncci_head;
-} avmctrl_info;
-
-typedef struct avmcard {
-       char name[32];
-
-       spinlock_t lock;
-       unsigned int port;
-       unsigned irq;
-       unsigned long membase;
-       enum avmcardtype cardtype;
-       unsigned char revision;
-       unsigned char class;
-       int cardnr; /* for t1isa */
-
-       char msgbuf[128];       /* capimsg msg part */
-       char databuf[2048];     /* capimsg data part */
-
-       void __iomem *mbase;
-       volatile u32 csr;
-       avmcard_dmainfo *dma;
-
-       struct avmctrl_info *ctrlinfo;
-
-       u_int nr_controllers;
-       u_int nlogcontr;
-       struct list_head list;
-} avmcard;
-
-extern int b1_irq_table[16];
-
-/*
- * LLI Messages to the ISDN-ControllerISDN Controller
- */
-
-#define        SEND_POLL               0x72    /*
-                                        * after load <- RECEIVE_POLL
-                                        */
-#define SEND_INIT              0x11    /*
-                                        * first message <- RECEIVE_INIT
-                                        * int32 NumApplications  int32
-                                        * NumNCCIs int32 BoardNumber
-                                        */
-#define SEND_REGISTER          0x12    /*
-                                        * register an application int32
-                                        * ApplIDId int32 NumMessages
-                                        * int32 NumB3Connections int32
-                                        * NumB3Blocks int32 B3Size
-                                        *
-                                        * AnzB3Connection != 0 &&
-                                        * AnzB3Blocks >= 1 && B3Size >= 1
-                                        */
-#define SEND_RELEASE           0x14    /*
-                                        * deregister an application int32
-                                        * ApplID
-                                        */
-#define SEND_MESSAGE           0x15    /*
-                                        * send capi-message int32 length
-                                        * capi-data ...
-                                        */
-#define SEND_DATA_B3_REQ       0x13    /*
-                                        * send capi-data-message int32
-                                        * MsgLength capi-data ... int32
-                                        * B3Length data ....
-                                        */
-
-#define SEND_CONFIG            0x21    /*
-                                        */
-
-#define SEND_POLLACK           0x73    /* T1 Watchdog */
-
-/*
- * LLI Messages from the ISDN-ControllerISDN Controller
- */
-
-#define RECEIVE_POLL           0x32    /*
-                                        * <- after SEND_POLL
-                                        */
-#define RECEIVE_INIT           0x27    /*
-                                        * <- after SEND_INIT int32 length
-                                        * byte total length b1struct board
-                                        * driver revision b1struct card
-                                        * type b1struct reserved b1struct
-                                        * serial number b1struct driver
-                                        * capability b1struct d-channel
-                                        * protocol b1struct CAPI-2.0
-                                        * profile b1struct capi version
-                                        */
-#define RECEIVE_MESSAGE                0x21    /*
-                                        * <- after SEND_MESSAGE int32
-                                        * AppllID int32 Length capi-data
-                                        * ....
-                                        */
-#define RECEIVE_DATA_B3_IND    0x22    /*
-                                        * received data int32 AppllID
-                                        * int32 Length capi-data ...
-                                        * int32 B3Length data ...
-                                        */
-#define RECEIVE_START          0x23    /*
-                                        * Handshake
-                                        */
-#define RECEIVE_STOP           0x24    /*
-                                        * Handshake
-                                        */
-#define RECEIVE_NEW_NCCI       0x25    /*
-                                        * int32 AppllID int32 NCCI int32
-                                        * WindowSize
-                                        */
-#define RECEIVE_FREE_NCCI      0x26    /*
-                                        * int32 AppllID int32 NCCI
-                                        */
-#define RECEIVE_RELEASE                0x26    /*
-                                        * int32 AppllID int32 0xffffffff
-                                        */
-#define RECEIVE_TASK_READY     0x31    /*
-                                        * int32 tasknr
-                                        * int32 Length Taskname ...
-                                        */
-#define RECEIVE_DEBUGMSG       0x71    /*
-                                        * int32 Length message
-                                        *
-                                        */
-#define RECEIVE_POLLDWORD      0x75    /* t1pci in dword mode */
-
-#define WRITE_REGISTER         0x00
-#define READ_REGISTER          0x01
-
-/*
- * port offsets
- */
-
-#define B1_READ                        0x00
-#define B1_WRITE               0x01
-#define B1_INSTAT              0x02
-#define B1_OUTSTAT             0x03
-#define B1_ANALYSE             0x04
-#define B1_REVISION            0x05
-#define B1_RESET               0x10
-
-
-#define B1_STAT0(cardtype)  ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l)
-#define B1_STAT1(cardtype)  (0x80E00000l)
-
-/* ---------------------------------------------------------------- */
-
-static inline unsigned char b1outp(unsigned int base,
-                                  unsigned short offset,
-                                  unsigned char value)
-{
-       outb(value, base + offset);
-       return inb(base + B1_ANALYSE);
-}
-
-
-static inline int b1_rx_full(unsigned int base)
-{
-       return inb(base + B1_INSTAT) & 0x1;
-}
-
-static inline unsigned char b1_get_byte(unsigned int base)
-{
-       unsigned long stop = jiffies + 1 * HZ;  /* maximum wait time 1 sec */
-       while (!b1_rx_full(base) && time_before(jiffies, stop));
-       if (b1_rx_full(base))
-               return inb(base + B1_READ);
-       printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
-       return 0;
-}
-
-static inline unsigned int b1_get_word(unsigned int base)
-{
-       unsigned int val = 0;
-       val |= b1_get_byte(base);
-       val |= (b1_get_byte(base) << 8);
-       val |= (b1_get_byte(base) << 16);
-       val |= (b1_get_byte(base) << 24);
-       return val;
-}
-
-static inline int b1_tx_empty(unsigned int base)
-{
-       return inb(base + B1_OUTSTAT) & 0x1;
-}
-
-static inline void b1_put_byte(unsigned int base, unsigned char val)
-{
-       while (!b1_tx_empty(base));
-       b1outp(base, B1_WRITE, val);
-}
-
-static inline int b1_save_put_byte(unsigned int base, unsigned char val)
-{
-       unsigned long stop = jiffies + 2 * HZ;
-       while (!b1_tx_empty(base) && time_before(jiffies, stop));
-       if (!b1_tx_empty(base)) return -1;
-       b1outp(base, B1_WRITE, val);
-       return 0;
-}
-
-static inline void b1_put_word(unsigned int base, unsigned int val)
-{
-       b1_put_byte(base, val & 0xff);
-       b1_put_byte(base, (val >> 8) & 0xff);
-       b1_put_byte(base, (val >> 16) & 0xff);
-       b1_put_byte(base, (val >> 24) & 0xff);
-}
-
-static inline unsigned int b1_get_slice(unsigned int base,
-                                       unsigned char *dp)
-{
-       unsigned int len, i;
-
-       len = i = b1_get_word(base);
-       while (i-- > 0) *dp++ = b1_get_byte(base);
-       return len;
-}
-
-static inline void b1_put_slice(unsigned int base,
-                               unsigned char *dp, unsigned int len)
-{
-       unsigned i = len;
-       b1_put_word(base, i);
-       while (i-- > 0)
-               b1_put_byte(base, *dp++);
-}
-
-static void b1_wr_reg(unsigned int base,
-                     unsigned int reg,
-                     unsigned int value)
-{
-       b1_put_byte(base, WRITE_REGISTER);
-       b1_put_word(base, reg);
-       b1_put_word(base, value);
-}
-
-static inline unsigned int b1_rd_reg(unsigned int base,
-                                    unsigned int reg)
-{
-       b1_put_byte(base, READ_REGISTER);
-       b1_put_word(base, reg);
-       return b1_get_word(base);
-
-}
-
-static inline void b1_reset(unsigned int base)
-{
-       b1outp(base, B1_RESET, 0);
-       mdelay(55 * 2); /* 2 TIC's */
-
-       b1outp(base, B1_RESET, 1);
-       mdelay(55 * 2); /* 2 TIC's */
-
-       b1outp(base, B1_RESET, 0);
-       mdelay(55 * 2); /* 2 TIC's */
-}
-
-static inline unsigned char b1_disable_irq(unsigned int base)
-{
-       return b1outp(base, B1_INSTAT, 0x00);
-}
-
-/* ---------------------------------------------------------------- */
-
-static inline void b1_set_test_bit(unsigned int base,
-                                  enum avmcardtype cardtype,
-                                  int onoff)
-{
-       b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
-}
-
-static inline int b1_get_test_bit(unsigned int base,
-                                 enum avmcardtype cardtype)
-{
-       return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
-}
-
-/* ---------------------------------------------------------------- */
-
-#define T1_FASTLINK            0x00
-#define T1_SLOWLINK            0x08
-
-#define T1_READ                        B1_READ
-#define T1_WRITE               B1_WRITE
-#define T1_INSTAT              B1_INSTAT
-#define T1_OUTSTAT             B1_OUTSTAT
-#define T1_IRQENABLE           0x05
-#define T1_FIFOSTAT            0x06
-#define T1_RESETLINK           0x10
-#define T1_ANALYSE             0x11
-#define T1_IRQMASTER           0x12
-#define T1_IDENT               0x17
-#define T1_RESETBOARD          0x1f
-
-#define        T1F_IREADY              0x01
-#define        T1F_IHALF               0x02
-#define        T1F_IFULL               0x04
-#define        T1F_IEMPTY              0x08
-#define        T1F_IFLAGS              0xF0
-
-#define        T1F_OREADY              0x10
-#define        T1F_OHALF               0x20
-#define        T1F_OEMPTY              0x40
-#define        T1F_OFULL               0x80
-#define        T1F_OFLAGS              0xF0
-
-/* there are HEMA cards with 1k and 4k FIFO out */
-#define FIFO_OUTBSIZE          256
-#define FIFO_INPBSIZE          512
-
-#define HEMA_VERSION_ID                0
-#define HEMA_PAL_ID            0
-
-static inline void t1outp(unsigned int base,
-                         unsigned short offset,
-                         unsigned char value)
-{
-       outb(value, base + offset);
-}
-
-static inline unsigned char t1inp(unsigned int base,
-                                 unsigned short offset)
-{
-       return inb(base + offset);
-}
-
-static inline int t1_isfastlink(unsigned int base)
-{
-       return (inb(base + T1_IDENT) & ~0x82) == 1;
-}
-
-static inline unsigned char t1_fifostatus(unsigned int base)
-{
-       return inb(base + T1_FIFOSTAT);
-}
-
-static inline unsigned int t1_get_slice(unsigned int base,
-                                       unsigned char *dp)
-{
-       unsigned int len, i;
-#ifdef FASTLINK_DEBUG
-       unsigned wcnt = 0, bcnt = 0;
-#endif
-
-       len = i = b1_get_word(base);
-       if (t1_isfastlink(base)) {
-               int status;
-               while (i > 0) {
-                       status = t1_fifostatus(base) & (T1F_IREADY | T1F_IHALF);
-                       if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
-
-                       switch (status) {
-                       case T1F_IREADY | T1F_IHALF | T1F_IFULL:
-                               insb(base + B1_READ, dp, FIFO_INPBSIZE);
-                               dp += FIFO_INPBSIZE;
-                               i -= FIFO_INPBSIZE;
-#ifdef FASTLINK_DEBUG
-                               wcnt += FIFO_INPBSIZE;
-#endif
-                               break;
-                       case T1F_IREADY | T1F_IHALF:
-                               insb(base + B1_READ, dp, i);
-#ifdef FASTLINK_DEBUG
-                               wcnt += i;
-#endif
-                               dp += i;
-                               i = 0;
-                               break;
-                       default:
-                               *dp++ = b1_get_byte(base);
-                               i--;
-#ifdef FASTLINK_DEBUG
-                               bcnt++;
-#endif
-                               break;
-                       }
-               }
-#ifdef FASTLINK_DEBUG
-               if (wcnt)
-                       printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
-                              base, len, wcnt, bcnt);
-#endif
-       } else {
-               while (i-- > 0)
-                       *dp++ = b1_get_byte(base);
-       }
-       return len;
-}
-
-static inline void t1_put_slice(unsigned int base,
-                               unsigned char *dp, unsigned int len)
-{
-       unsigned i = len;
-       b1_put_word(base, i);
-       if (t1_isfastlink(base)) {
-               int status;
-               while (i > 0) {
-                       status = t1_fifostatus(base) & (T1F_OREADY | T1F_OHALF);
-                       if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
-                       switch (status) {
-                       case T1F_OREADY | T1F_OHALF | T1F_OEMPTY:
-                               outsb(base + B1_WRITE, dp, FIFO_OUTBSIZE);
-                               dp += FIFO_OUTBSIZE;
-                               i -= FIFO_OUTBSIZE;
-                               break;
-                       case T1F_OREADY | T1F_OHALF:
-                               outsb(base + B1_WRITE, dp, i);
-                               dp += i;
-                               i = 0;
-                               break;
-                       default:
-                               b1_put_byte(base, *dp++);
-                               i--;
-                               break;
-                       }
-               }
-       } else {
-               while (i-- > 0)
-                       b1_put_byte(base, *dp++);
-       }
-}
-
-static inline void t1_disable_irq(unsigned int base)
-{
-       t1outp(base, T1_IRQMASTER, 0x00);
-}
-
-static inline void t1_reset(unsigned int base)
-{
-       /* reset T1 Controller */
-       b1_reset(base);
-       /* disable irq on HEMA */
-       t1outp(base, B1_INSTAT, 0x00);
-       t1outp(base, B1_OUTSTAT, 0x00);
-       t1outp(base, T1_IRQMASTER, 0x00);
-       /* reset HEMA board configuration */
-       t1outp(base, T1_RESETBOARD, 0xf);
-}
-
-static inline void b1_setinterrupt(unsigned int base, unsigned irq,
-                                  enum avmcardtype cardtype)
-{
-       switch (cardtype) {
-       case avm_t1isa:
-               t1outp(base, B1_INSTAT, 0x00);
-               t1outp(base, B1_INSTAT, 0x02);
-               t1outp(base, T1_IRQMASTER, 0x08);
-               break;
-       case avm_b1isa:
-               b1outp(base, B1_INSTAT, 0x00);
-               b1outp(base, B1_RESET, b1_irq_table[irq]);
-               b1outp(base, B1_INSTAT, 0x02);
-               break;
-       default:
-       case avm_m1:
-       case avm_m2:
-       case avm_b1pci:
-               b1outp(base, B1_INSTAT, 0x00);
-               b1outp(base, B1_RESET, 0xf0);
-               b1outp(base, B1_INSTAT, 0x02);
-               break;
-       case avm_c4:
-       case avm_t1pci:
-               b1outp(base, B1_RESET, 0xf0);
-               break;
-       }
-}
-
-/* b1.c */
-avmcard *b1_alloc_card(int nr_controllers);
-void b1_free_card(avmcard *card);
-int b1_detect(unsigned int base, enum avmcardtype cardtype);
-void b1_getrevision(avmcard *card);
-int b1_load_t4file(avmcard *card, capiloaddatapart *t4file);
-int b1_load_config(avmcard *card, capiloaddatapart *config);
-int b1_loaded(avmcard *card);
-
-int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
-void b1_reset_ctr(struct capi_ctr *ctrl);
-void b1_register_appl(struct capi_ctr *ctrl, u16 appl,
-                     capi_register_params *rp);
-void b1_release_appl(struct capi_ctr *ctrl, u16 appl);
-u16  b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
-void b1_parse_version(avmctrl_info *card);
-irqreturn_t b1_interrupt(int interrupt, void *devptr);
-
-int b1_proc_show(struct seq_file *m, void *v);
-
-avmcard_dmainfo *avmcard_dma_alloc(char *name, struct pci_dev *,
-                                  long rsize, long ssize);
-void avmcard_dma_free(avmcard_dmainfo *);
-
-/* b1dma.c */
-int b1pciv4_detect(avmcard *card);
-int t1pci_detect(avmcard *card);
-void b1dma_reset(avmcard *card);
-irqreturn_t b1dma_interrupt(int interrupt, void *devptr);
-
-int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
-void b1dma_reset_ctr(struct capi_ctr *ctrl);
-void b1dma_remove_ctr(struct capi_ctr *ctrl);
-void b1dma_register_appl(struct capi_ctr *ctrl,
-                        u16 appl,
-                        capi_register_params *rp);
-void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl);
-u16  b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
-int b1dma_proc_show(struct seq_file *m, void *v);
-
-#endif /* _AVMCARD_H_ */
diff --git a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c
deleted file mode 100644 (file)
index 40ca1e8..0000000
+++ /dev/null
@@ -1,804 +0,0 @@
-/* $Id: b1.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
- *
- * Common module for AVM B1 cards.
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/capi.h>
-#include <linux/kernelcapi.h>
-#include <linux/slab.h>
-#include <asm/io.h>
-#include <linux/init.h>
-#include <linux/uaccess.h>
-#include <linux/netdevice.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-
-static char *revision = "$Revision: 1.1.2.2 $";
-
-/* ------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------- */
-
-int b1_irq_table[16] =
-{0,
- 0,
- 0,
- 192,                          /* irq 3 */
- 32,                           /* irq 4 */
- 160,                          /* irq 5 */
- 96,                           /* irq 6 */
- 224,                          /* irq 7 */
- 0,
- 64,                           /* irq 9 */
- 80,                           /* irq 10 */
- 208,                          /* irq 11 */
- 48,                           /* irq 12 */
- 0,
- 0,
- 112,                          /* irq 15 */
-};
-
-/* ------------------------------------------------------------- */
-
-avmcard *b1_alloc_card(int nr_controllers)
-{
-       avmcard *card;
-       avmctrl_info *cinfo;
-       int i;
-
-       card = kzalloc(sizeof(*card), GFP_KERNEL);
-       if (!card)
-               return NULL;
-
-       cinfo = kcalloc(nr_controllers, sizeof(*cinfo), GFP_KERNEL);
-       if (!cinfo) {
-               kfree(card);
-               return NULL;
-       }
-
-       card->ctrlinfo = cinfo;
-       for (i = 0; i < nr_controllers; i++) {
-               INIT_LIST_HEAD(&cinfo[i].ncci_head);
-               cinfo[i].card = card;
-       }
-       spin_lock_init(&card->lock);
-       card->nr_controllers = nr_controllers;
-
-       return card;
-}
-
-/* ------------------------------------------------------------- */
-
-void b1_free_card(avmcard *card)
-{
-       kfree(card->ctrlinfo);
-       kfree(card);
-}
-
-/* ------------------------------------------------------------- */
-
-int b1_detect(unsigned int base, enum avmcardtype cardtype)
-{
-       int onoff, i;
-
-       /*
-        * Statusregister 0000 00xx
-        */
-       if ((inb(base + B1_INSTAT) & 0xfc)
-           || (inb(base + B1_OUTSTAT) & 0xfc))
-               return 1;
-       /*
-        * Statusregister 0000 001x
-        */
-       b1outp(base, B1_INSTAT, 0x2);   /* enable irq */
-       /* b1outp(base, B1_OUTSTAT, 0x2); */
-       if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
-           /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
-               return 2;
-       /*
-        * Statusregister 0000 000x
-        */
-       b1outp(base, B1_INSTAT, 0x0);   /* disable irq */
-       b1outp(base, B1_OUTSTAT, 0x0);
-       if ((inb(base + B1_INSTAT) & 0xfe)
-           || (inb(base + B1_OUTSTAT) & 0xfe))
-               return 3;
-
-       for (onoff = !0, i = 0; i < 10; i++) {
-               b1_set_test_bit(base, cardtype, onoff);
-               if (b1_get_test_bit(base, cardtype) != onoff)
-                       return 4;
-               onoff = !onoff;
-       }
-
-       if (cardtype == avm_m1)
-               return 0;
-
-       if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
-               return 5;
-
-       return 0;
-}
-
-void b1_getrevision(avmcard *card)
-{
-       card->class = inb(card->port + B1_ANALYSE);
-       card->revision = inb(card->port + B1_REVISION);
-}
-
-#define FWBUF_SIZE     256
-int b1_load_t4file(avmcard *card, capiloaddatapart *t4file)
-{
-       unsigned char buf[FWBUF_SIZE];
-       unsigned char *dp;
-       int i, left;
-       unsigned int base = card->port;
-
-       dp = t4file->data;
-       left = t4file->len;
-       while (left > FWBUF_SIZE) {
-               if (t4file->user) {
-                       if (copy_from_user(buf, dp, FWBUF_SIZE))
-                               return -EFAULT;
-               } else {
-                       memcpy(buf, dp, FWBUF_SIZE);
-               }
-               for (i = 0; i < FWBUF_SIZE; i++)
-                       if (b1_save_put_byte(base, buf[i]) < 0) {
-                               printk(KERN_ERR "%s: corrupted firmware file ?\n",
-                                      card->name);
-                               return -EIO;
-                       }
-               left -= FWBUF_SIZE;
-               dp += FWBUF_SIZE;
-       }
-       if (left) {
-               if (t4file->user) {
-                       if (copy_from_user(buf, dp, left))
-                               return -EFAULT;
-               } else {
-                       memcpy(buf, dp, left);
-               }
-               for (i = 0; i < left; i++)
-                       if (b1_save_put_byte(base, buf[i]) < 0) {
-                               printk(KERN_ERR "%s: corrupted firmware file ?\n",
-                                      card->name);
-                               return -EIO;
-                       }
-       }
-       return 0;
-}
-
-int b1_load_config(avmcard *card, capiloaddatapart *config)
-{
-       unsigned char buf[FWBUF_SIZE];
-       unsigned char *dp;
-       unsigned int base = card->port;
-       int i, j, left;
-
-       dp = config->data;
-       left = config->len;
-       if (left) {
-               b1_put_byte(base, SEND_CONFIG);
-               b1_put_word(base, 1);
-               b1_put_byte(base, SEND_CONFIG);
-               b1_put_word(base, left);
-       }
-       while (left > FWBUF_SIZE) {
-               if (config->user) {
-                       if (copy_from_user(buf, dp, FWBUF_SIZE))
-                               return -EFAULT;
-               } else {
-                       memcpy(buf, dp, FWBUF_SIZE);
-               }
-               for (i = 0; i < FWBUF_SIZE; ) {
-                       b1_put_byte(base, SEND_CONFIG);
-                       for (j = 0; j < 4; j++) {
-                               b1_put_byte(base, buf[i++]);
-                       }
-               }
-               left -= FWBUF_SIZE;
-               dp += FWBUF_SIZE;
-       }
-       if (left) {
-               if (config->user) {
-                       if (copy_from_user(buf, dp, left))
-                               return -EFAULT;
-               } else {
-                       memcpy(buf, dp, left);
-               }
-               for (i = 0; i < left; ) {
-                       b1_put_byte(base, SEND_CONFIG);
-                       for (j = 0; j < 4; j++) {
-                               if (i < left)
-                                       b1_put_byte(base, buf[i++]);
-                               else
-                                       b1_put_byte(base, 0);
-                       }
-               }
-       }
-       return 0;
-}
-
-int b1_loaded(avmcard *card)
-{
-       unsigned int base = card->port;
-       unsigned long stop;
-       unsigned char ans;
-       unsigned long tout = 2;
-
-       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
-               if (b1_tx_empty(base))
-                       break;
-       }
-       if (!b1_tx_empty(base)) {
-               printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n",
-                      card->name);
-               return 0;
-       }
-       b1_put_byte(base, SEND_POLL);
-       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
-               if (b1_rx_full(base)) {
-                       if ((ans = b1_get_byte(base)) == RECEIVE_POLL) {
-                               return 1;
-                       }
-                       printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n",
-                              card->name, ans);
-                       return 0;
-               }
-       }
-       printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name);
-       return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-       int retval;
-
-       b1_reset(port);
-
-       if ((retval = b1_load_t4file(card, &data->firmware))) {
-               b1_reset(port);
-               printk(KERN_ERR "%s: failed to load t4file!!\n",
-                      card->name);
-               return retval;
-       }
-
-       b1_disable_irq(port);
-
-       if (data->configuration.len > 0 && data->configuration.data) {
-               if ((retval = b1_load_config(card, &data->configuration))) {
-                       b1_reset(port);
-                       printk(KERN_ERR "%s: failed to load config!!\n",
-                              card->name);
-                       return retval;
-               }
-       }
-
-       if (!b1_loaded(card)) {
-               printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
-               return -EIO;
-       }
-
-       spin_lock_irqsave(&card->lock, flags);
-       b1_setinterrupt(port, card->irq, card->cardtype);
-       b1_put_byte(port, SEND_INIT);
-       b1_put_word(port, CAPI_MAXAPPL);
-       b1_put_word(port, AVM_NCCI_PER_CHANNEL * 2);
-       b1_put_word(port, ctrl->cnr - 1);
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       return 0;
-}
-
-void b1_reset_ctr(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-
-       b1_reset(port);
-       b1_reset(port);
-
-       memset(cinfo->version, 0, sizeof(cinfo->version));
-       spin_lock_irqsave(&card->lock, flags);
-       capilib_release(&cinfo->ncci_head);
-       spin_unlock_irqrestore(&card->lock, flags);
-       capi_ctr_down(ctrl);
-}
-
-void b1_register_appl(struct capi_ctr *ctrl,
-                     u16 appl,
-                     capi_register_params *rp)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-       int nconn, want = rp->level3cnt;
-
-       if (want > 0) nconn = want;
-       else nconn = ctrl->profile.nbchannel * -want;
-       if (nconn == 0) nconn = ctrl->profile.nbchannel;
-
-       spin_lock_irqsave(&card->lock, flags);
-       b1_put_byte(port, SEND_REGISTER);
-       b1_put_word(port, appl);
-       b1_put_word(port, 1024 * (nconn + 1));
-       b1_put_word(port, nconn);
-       b1_put_word(port, rp->datablkcnt);
-       b1_put_word(port, rp->datablklen);
-       spin_unlock_irqrestore(&card->lock, flags);
-}
-
-void b1_release_appl(struct capi_ctr *ctrl, u16 appl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-       capilib_release_appl(&cinfo->ncci_head, appl);
-       b1_put_byte(port, SEND_RELEASE);
-       b1_put_word(port, appl);
-       spin_unlock_irqrestore(&card->lock, flags);
-}
-
-u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-       u16 len = CAPIMSG_LEN(skb->data);
-       u8 cmd = CAPIMSG_COMMAND(skb->data);
-       u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
-       u16 dlen, retval;
-
-       spin_lock_irqsave(&card->lock, flags);
-       if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
-               retval = capilib_data_b3_req(&cinfo->ncci_head,
-                                            CAPIMSG_APPID(skb->data),
-                                            CAPIMSG_NCCI(skb->data),
-                                            CAPIMSG_MSGID(skb->data));
-               if (retval != CAPI_NOERROR) {
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       return retval;
-               }
-
-               dlen = CAPIMSG_DATALEN(skb->data);
-
-               b1_put_byte(port, SEND_DATA_B3_REQ);
-               b1_put_slice(port, skb->data, len);
-               b1_put_slice(port, skb->data + len, dlen);
-       } else {
-               b1_put_byte(port, SEND_MESSAGE);
-               b1_put_slice(port, skb->data, len);
-       }
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       dev_kfree_skb_any(skb);
-       return CAPI_NOERROR;
-}
-
-/* ------------------------------------------------------------- */
-
-void b1_parse_version(avmctrl_info *cinfo)
-{
-       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
-       avmcard *card = cinfo->card;
-       capi_profile *profp;
-       u8 *dversion;
-       u8 flag;
-       int i, j;
-
-       for (j = 0; j < AVM_MAXVERSION; j++)
-               cinfo->version[j] = "";
-       for (i = 0, j = 0;
-            j < AVM_MAXVERSION && i < cinfo->versionlen;
-            j++, i += cinfo->versionbuf[i] + 1)
-               cinfo->version[j] = &cinfo->versionbuf[i + 1];
-
-       strlcpy(ctrl->serial, cinfo->version[VER_SERIAL], sizeof(ctrl->serial));
-       memcpy(&ctrl->profile, cinfo->version[VER_PROFILE], sizeof(capi_profile));
-       strlcpy(ctrl->manu, "AVM GmbH", sizeof(ctrl->manu));
-       dversion = cinfo->version[VER_DRIVER];
-       ctrl->version.majorversion = 2;
-       ctrl->version.minorversion = 0;
-       ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
-       ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf);
-       ctrl->version.minormanuversion = (dversion[3] - '0') << 4;
-       ctrl->version.minormanuversion |=
-               (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
-
-       profp = &ctrl->profile;
-
-       flag = ((u8 *)(profp->manu))[1];
-       switch (flag) {
-       case 0: if (cinfo->version[VER_CARDTYPE])
-                       strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]);
-               else strcpy(cinfo->cardname, "B1");
-               break;
-       case 3: strcpy(cinfo->cardname, "PCMCIA B"); break;
-       case 4: strcpy(cinfo->cardname, "PCMCIA M1"); break;
-       case 5: strcpy(cinfo->cardname, "PCMCIA M2"); break;
-       case 6: strcpy(cinfo->cardname, "B1 V3.0"); break;
-       case 7: strcpy(cinfo->cardname, "B1 PCI"); break;
-       default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break;
-       }
-       printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n",
-              card->name, ctrl->cnr, cinfo->cardname);
-
-       flag = ((u8 *)(profp->manu))[3];
-       if (flag)
-               printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n",
-                      card->name,
-                      ctrl->cnr,
-                      (flag & 0x01) ? " DSS1" : "",
-                      (flag & 0x02) ? " CT1" : "",
-                      (flag & 0x04) ? " VN3" : "",
-                      (flag & 0x08) ? " NI1" : "",
-                      (flag & 0x10) ? " AUSTEL" : "",
-                      (flag & 0x20) ? " ESS" : "",
-                      (flag & 0x40) ? " 1TR6" : ""
-                       );
-
-       flag = ((u8 *)(profp->manu))[5];
-       if (flag)
-               printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n",
-                      card->name,
-                      ctrl->cnr,
-                      (flag & 0x01) ? " point to point" : "",
-                      (flag & 0x02) ? " point to multipoint" : "",
-                      (flag & 0x08) ? " leased line without D-channel" : "",
-                      (flag & 0x04) ? " leased line with D-channel" : ""
-                       );
-}
-
-/* ------------------------------------------------------------- */
-
-irqreturn_t b1_interrupt(int interrupt, void *devptr)
-{
-       avmcard *card = devptr;
-       avmctrl_info *cinfo = &card->ctrlinfo[0];
-       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
-       unsigned char b1cmd;
-       struct sk_buff *skb;
-
-       unsigned ApplId;
-       unsigned MsgLen;
-       unsigned DataB3Len;
-       unsigned NCCI;
-       unsigned WindowSize;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-
-       if (!b1_rx_full(card->port)) {
-               spin_unlock_irqrestore(&card->lock, flags);
-               return IRQ_NONE;
-       }
-
-       b1cmd = b1_get_byte(card->port);
-
-       switch (b1cmd) {
-
-       case RECEIVE_DATA_B3_IND:
-
-               ApplId = (unsigned) b1_get_word(card->port);
-               MsgLen = b1_get_slice(card->port, card->msgbuf);
-               DataB3Len = b1_get_slice(card->port, card->databuf);
-               spin_unlock_irqrestore(&card->lock, flags);
-
-               if (MsgLen < 30) { /* not CAPI 64Bit */
-                       memset(card->msgbuf + MsgLen, 0, 30-MsgLen);
-                       MsgLen = 30;
-                       CAPIMSG_SETLEN(card->msgbuf, 30);
-               }
-               if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
-                       printk(KERN_ERR "%s: incoming packet dropped\n",
-                              card->name);
-               } else {
-                       skb_put_data(skb, card->msgbuf, MsgLen);
-                       skb_put_data(skb, card->databuf, DataB3Len);
-                       capi_ctr_handle_message(ctrl, ApplId, skb);
-               }
-               break;
-
-       case RECEIVE_MESSAGE:
-
-               ApplId = (unsigned) b1_get_word(card->port);
-               MsgLen = b1_get_slice(card->port, card->msgbuf);
-               if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
-                       printk(KERN_ERR "%s: incoming packet dropped\n",
-                              card->name);
-                       spin_unlock_irqrestore(&card->lock, flags);
-               } else {
-                       skb_put_data(skb, card->msgbuf, MsgLen);
-                       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
-                               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
-                                                    CAPIMSG_NCCI(skb->data),
-                                                    CAPIMSG_MSGID(skb->data));
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       capi_ctr_handle_message(ctrl, ApplId, skb);
-               }
-               break;
-
-       case RECEIVE_NEW_NCCI:
-
-               ApplId = b1_get_word(card->port);
-               NCCI = b1_get_word(card->port);
-               WindowSize = b1_get_word(card->port);
-               capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
-               spin_unlock_irqrestore(&card->lock, flags);
-               break;
-
-       case RECEIVE_FREE_NCCI:
-
-               ApplId = b1_get_word(card->port);
-               NCCI = b1_get_word(card->port);
-               if (NCCI != 0xffffffff)
-                       capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
-               spin_unlock_irqrestore(&card->lock, flags);
-               break;
-
-       case RECEIVE_START:
-               /* b1_put_byte(card->port, SEND_POLLACK); */
-               spin_unlock_irqrestore(&card->lock, flags);
-               capi_ctr_resume_output(ctrl);
-               break;
-
-       case RECEIVE_STOP:
-               spin_unlock_irqrestore(&card->lock, flags);
-               capi_ctr_suspend_output(ctrl);
-               break;
-
-       case RECEIVE_INIT:
-
-               cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf);
-               spin_unlock_irqrestore(&card->lock, flags);
-               b1_parse_version(cinfo);
-               printk(KERN_INFO "%s: %s-card (%s) now active\n",
-                      card->name,
-                      cinfo->version[VER_CARDTYPE],
-                      cinfo->version[VER_DRIVER]);
-               capi_ctr_ready(ctrl);
-               break;
-
-       case RECEIVE_TASK_READY:
-               ApplId = (unsigned) b1_get_word(card->port);
-               MsgLen = b1_get_slice(card->port, card->msgbuf);
-               spin_unlock_irqrestore(&card->lock, flags);
-               card->msgbuf[MsgLen] = 0;
-               while (MsgLen > 0
-                      && (card->msgbuf[MsgLen - 1] == '\n'
-                          || card->msgbuf[MsgLen - 1] == '\r')) {
-                       card->msgbuf[MsgLen - 1] = 0;
-                       MsgLen--;
-               }
-               printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
-                      card->name, ApplId, card->msgbuf);
-               break;
-
-       case RECEIVE_DEBUGMSG:
-               MsgLen = b1_get_slice(card->port, card->msgbuf);
-               spin_unlock_irqrestore(&card->lock, flags);
-               card->msgbuf[MsgLen] = 0;
-               while (MsgLen > 0
-                      && (card->msgbuf[MsgLen - 1] == '\n'
-                          || card->msgbuf[MsgLen - 1] == '\r')) {
-                       card->msgbuf[MsgLen - 1] = 0;
-                       MsgLen--;
-               }
-               printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
-               break;
-
-       case 0xff:
-               spin_unlock_irqrestore(&card->lock, flags);
-               printk(KERN_ERR "%s: card removed ?\n", card->name);
-               return IRQ_NONE;
-       default:
-               spin_unlock_irqrestore(&card->lock, flags);
-               printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
-                      card->name, b1cmd);
-               return IRQ_HANDLED;
-       }
-       return IRQ_HANDLED;
-}
-
-/* ------------------------------------------------------------- */
-int b1_proc_show(struct seq_file *m, void *v)
-{
-       struct capi_ctr *ctrl = m->private;
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       u8 flag;
-       char *s;
-
-       seq_printf(m, "%-16s %s\n", "name", card->name);
-       seq_printf(m, "%-16s 0x%x\n", "io", card->port);
-       seq_printf(m, "%-16s %d\n", "irq", card->irq);
-       switch (card->cardtype) {
-       case avm_b1isa: s = "B1 ISA"; break;
-       case avm_b1pci: s = "B1 PCI"; break;
-       case avm_b1pcmcia: s = "B1 PCMCIA"; break;
-       case avm_m1: s = "M1"; break;
-       case avm_m2: s = "M2"; break;
-       case avm_t1isa: s = "T1 ISA (HEMA)"; break;
-       case avm_t1pci: s = "T1 PCI"; break;
-       case avm_c4: s = "C4"; break;
-       case avm_c2: s = "C2"; break;
-       default: s = "???"; break;
-       }
-       seq_printf(m, "%-16s %s\n", "type", s);
-       if (card->cardtype == avm_t1isa)
-               seq_printf(m, "%-16s %d\n", "cardnr", card->cardnr);
-       if ((s = cinfo->version[VER_DRIVER]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_driver", s);
-       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
-       if ((s = cinfo->version[VER_SERIAL]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_serial", s);
-
-       if (card->cardtype != avm_m1) {
-               flag = ((u8 *)(ctrl->profile.manu))[3];
-               if (flag)
-                       seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
-                                  "protocol",
-                                  (flag & 0x01) ? " DSS1" : "",
-                                  (flag & 0x02) ? " CT1" : "",
-                                  (flag & 0x04) ? " VN3" : "",
-                                  (flag & 0x08) ? " NI1" : "",
-                                  (flag & 0x10) ? " AUSTEL" : "",
-                                  (flag & 0x20) ? " ESS" : "",
-                                  (flag & 0x40) ? " 1TR6" : ""
-                               );
-       }
-       if (card->cardtype != avm_m1) {
-               flag = ((u8 *)(ctrl->profile.manu))[5];
-               if (flag)
-                       seq_printf(m, "%-16s%s%s%s%s\n",
-                                  "linetype",
-                                  (flag & 0x01) ? " point to point" : "",
-                                  (flag & 0x02) ? " point to multipoint" : "",
-                                  (flag & 0x08) ? " leased line without D-channel" : "",
-                                  (flag & 0x04) ? " leased line with D-channel" : ""
-                               );
-       }
-       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
-
-       return 0;
-}
-EXPORT_SYMBOL(b1_proc_show);
-
-/* ------------------------------------------------------------- */
-
-#ifdef CONFIG_PCI
-
-avmcard_dmainfo *
-avmcard_dma_alloc(char *name, struct pci_dev *pdev, long rsize, long ssize)
-{
-       avmcard_dmainfo *p;
-       void *buf;
-
-       p = kzalloc(sizeof(avmcard_dmainfo), GFP_KERNEL);
-       if (!p) {
-               printk(KERN_WARNING "%s: no memory.\n", name);
-               goto err;
-       }
-
-       p->recvbuf.size = rsize;
-       buf = pci_alloc_consistent(pdev, rsize, &p->recvbuf.dmaaddr);
-       if (!buf) {
-               printk(KERN_WARNING "%s: allocation of receive dma buffer failed.\n", name);
-               goto err_kfree;
-       }
-       p->recvbuf.dmabuf = buf;
-
-       p->sendbuf.size = ssize;
-       buf = pci_alloc_consistent(pdev, ssize, &p->sendbuf.dmaaddr);
-       if (!buf) {
-               printk(KERN_WARNING "%s: allocation of send dma buffer failed.\n", name);
-               goto err_free_consistent;
-       }
-
-       p->sendbuf.dmabuf = buf;
-       skb_queue_head_init(&p->send_queue);
-
-       return p;
-
-err_free_consistent:
-       pci_free_consistent(p->pcidev, p->recvbuf.size,
-                           p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
-err_kfree:
-       kfree(p);
-err:
-       return NULL;
-}
-
-void avmcard_dma_free(avmcard_dmainfo *p)
-{
-       pci_free_consistent(p->pcidev, p->recvbuf.size,
-                           p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
-       pci_free_consistent(p->pcidev, p->sendbuf.size,
-                           p->sendbuf.dmabuf, p->sendbuf.dmaaddr);
-       skb_queue_purge(&p->send_queue);
-       kfree(p);
-}
-
-EXPORT_SYMBOL(avmcard_dma_alloc);
-EXPORT_SYMBOL(avmcard_dma_free);
-
-#endif
-
-EXPORT_SYMBOL(b1_irq_table);
-
-EXPORT_SYMBOL(b1_alloc_card);
-EXPORT_SYMBOL(b1_free_card);
-EXPORT_SYMBOL(b1_detect);
-EXPORT_SYMBOL(b1_getrevision);
-EXPORT_SYMBOL(b1_load_t4file);
-EXPORT_SYMBOL(b1_load_config);
-EXPORT_SYMBOL(b1_loaded);
-EXPORT_SYMBOL(b1_load_firmware);
-EXPORT_SYMBOL(b1_reset_ctr);
-EXPORT_SYMBOL(b1_register_appl);
-EXPORT_SYMBOL(b1_release_appl);
-EXPORT_SYMBOL(b1_send_message);
-
-EXPORT_SYMBOL(b1_parse_version);
-EXPORT_SYMBOL(b1_interrupt);
-
-static int __init b1_init(void)
-{
-       char *p;
-       char rev[32];
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, 32);
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-       printk(KERN_INFO "b1: revision %s\n", rev);
-
-       return 0;
-}
-
-static void __exit b1_exit(void)
-{
-}
-
-module_init(b1_init);
-module_exit(b1_exit);
diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c
deleted file mode 100644 (file)
index 6a3dc99..0000000
+++ /dev/null
@@ -1,981 +0,0 @@
-/* $Id: b1dma.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
- *
- * Common module for AVM B1 cards that support dma with AMCC
- *
- * Copyright 2000 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/capi.h>
-#include <linux/kernelcapi.h>
-#include <linux/gfp.h>
-#include <asm/io.h>
-#include <linux/init.h>
-#include <linux/uaccess.h>
-#include <linux/netdevice.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-
-static char *revision = "$Revision: 1.1.2.3 $";
-
-#undef AVM_B1DMA_DEBUG
-
-/* ------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("CAPI4Linux: DMA support for active AVM cards");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-static bool suppress_pollack = 0;
-module_param(suppress_pollack, bool, 0);
-
-/* ------------------------------------------------------------- */
-
-static void b1dma_dispatch_tx(avmcard *card);
-
-/* ------------------------------------------------------------- */
-
-/* S5933 */
-
-#define        AMCC_RXPTR      0x24
-#define        AMCC_RXLEN      0x28
-#define        AMCC_TXPTR      0x2c
-#define        AMCC_TXLEN      0x30
-
-#define        AMCC_INTCSR     0x38
-#      define EN_READ_TC_INT           0x00008000L
-#      define EN_WRITE_TC_INT          0x00004000L
-#      define EN_TX_TC_INT             EN_READ_TC_INT
-#      define EN_RX_TC_INT             EN_WRITE_TC_INT
-#      define AVM_FLAG                 0x30000000L
-
-#      define ANY_S5933_INT            0x00800000L
-#      define READ_TC_INT              0x00080000L
-#      define WRITE_TC_INT             0x00040000L
-#      define  TX_TC_INT               READ_TC_INT
-#      define  RX_TC_INT               WRITE_TC_INT
-#      define MASTER_ABORT_INT         0x00100000L
-#      define TARGET_ABORT_INT         0x00200000L
-#      define BUS_MASTER_INT           0x00200000L
-#      define ALL_INT                  0x000C0000L
-
-#define        AMCC_MCSR       0x3c
-#      define A2P_HI_PRIORITY          0x00000100L
-#      define EN_A2P_TRANSFERS         0x00000400L
-#      define P2A_HI_PRIORITY          0x00001000L
-#      define EN_P2A_TRANSFERS         0x00004000L
-#      define RESET_A2P_FLAGS          0x04000000L
-#      define RESET_P2A_FLAGS          0x02000000L
-
-/* ------------------------------------------------------------- */
-
-static inline void b1dma_writel(avmcard *card, u32 value, int off)
-{
-       writel(value, card->mbase + off);
-}
-
-static inline u32 b1dma_readl(avmcard *card, int off)
-{
-       return readl(card->mbase + off);
-}
-
-/* ------------------------------------------------------------- */
-
-static inline int b1dma_tx_empty(unsigned int port)
-{
-       return inb(port + 0x03) & 0x1;
-}
-
-static inline int b1dma_rx_full(unsigned int port)
-{
-       return inb(port + 0x02) & 0x1;
-}
-
-static int b1dma_tolink(avmcard *card, void *buf, unsigned int len)
-{
-       unsigned long stop = jiffies + 1 * HZ;  /* maximum wait time 1 sec */
-       unsigned char *s = (unsigned char *)buf;
-       while (len--) {
-               while (!b1dma_tx_empty(card->port)
-                      && time_before(jiffies, stop));
-               if (!b1dma_tx_empty(card->port))
-                       return -1;
-               t1outp(card->port, 0x01, *s++);
-       }
-       return 0;
-}
-
-static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len)
-{
-       unsigned long stop = jiffies + 1 * HZ;  /* maximum wait time 1 sec */
-       unsigned char *s = (unsigned char *)buf;
-       while (len--) {
-               while (!b1dma_rx_full(card->port)
-                      && time_before(jiffies, stop));
-               if (!b1dma_rx_full(card->port))
-                       return -1;
-               *s++ = t1inp(card->port, 0x00);
-       }
-       return 0;
-}
-
-static int WriteReg(avmcard *card, u32 reg, u8 val)
-{
-       u8 cmd = 0x00;
-       if (b1dma_tolink(card, &cmd, 1) == 0
-           && b1dma_tolink(card, &reg, 4) == 0) {
-               u32 tmp = val;
-               return b1dma_tolink(card, &tmp, 4);
-       }
-       return -1;
-}
-
-static u8 ReadReg(avmcard *card, u32 reg)
-{
-       u8 cmd = 0x01;
-       if (b1dma_tolink(card, &cmd, 1) == 0
-           && b1dma_tolink(card, &reg, 4) == 0) {
-               u32 tmp;
-               if (b1dma_fromlink(card, &tmp, 4) == 0)
-                       return (u8)tmp;
-       }
-       return 0xff;
-}
-
-/* ------------------------------------------------------------- */
-
-static inline void _put_byte(void **pp, u8 val)
-{
-       u8 *s = *pp;
-       *s++ = val;
-       *pp = s;
-}
-
-static inline void _put_word(void **pp, u32 val)
-{
-       u8 *s = *pp;
-       *s++ = val & 0xff;
-       *s++ = (val >> 8) & 0xff;
-       *s++ = (val >> 16) & 0xff;
-       *s++ = (val >> 24) & 0xff;
-       *pp = s;
-}
-
-static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
-{
-       unsigned i = len;
-       _put_word(pp, i);
-       while (i-- > 0)
-               _put_byte(pp, *dp++);
-}
-
-static inline u8 _get_byte(void **pp)
-{
-       u8 *s = *pp;
-       u8 val;
-       val = *s++;
-       *pp = s;
-       return val;
-}
-
-static inline u32 _get_word(void **pp)
-{
-       u8 *s = *pp;
-       u32 val;
-       val = *s++;
-       val |= (*s++ << 8);
-       val |= (*s++ << 16);
-       val |= (*s++ << 24);
-       *pp = s;
-       return val;
-}
-
-static inline u32 _get_slice(void **pp, unsigned char *dp)
-{
-       unsigned int len, i;
-
-       len = i = _get_word(pp);
-       while (i-- > 0) *dp++ = _get_byte(pp);
-       return len;
-}
-
-/* ------------------------------------------------------------- */
-
-void b1dma_reset(avmcard *card)
-{
-       card->csr = 0x0;
-       b1dma_writel(card, card->csr, AMCC_INTCSR);
-       b1dma_writel(card, 0, AMCC_MCSR);
-       b1dma_writel(card, 0, AMCC_RXLEN);
-       b1dma_writel(card, 0, AMCC_TXLEN);
-
-       t1outp(card->port, 0x10, 0x00);
-       t1outp(card->port, 0x07, 0x00);
-
-       b1dma_writel(card, 0, AMCC_MCSR);
-       mdelay(10);
-       b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
-       mdelay(10);
-       b1dma_writel(card, 0, AMCC_MCSR);
-       if (card->cardtype == avm_t1pci)
-               mdelay(42);
-       else
-               mdelay(10);
-}
-
-/* ------------------------------------------------------------- */
-
-static int b1dma_detect(avmcard *card)
-{
-       b1dma_writel(card, 0, AMCC_MCSR);
-       mdelay(10);
-       b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
-       mdelay(10);
-       b1dma_writel(card, 0, AMCC_MCSR);
-       mdelay(42);
-
-       b1dma_writel(card, 0, AMCC_RXLEN);
-       b1dma_writel(card, 0, AMCC_TXLEN);
-       card->csr = 0x0;
-       b1dma_writel(card, card->csr, AMCC_INTCSR);
-
-       if (b1dma_readl(card, AMCC_MCSR) != 0x000000E6)
-               return 1;
-
-       b1dma_writel(card, 0xffffffff, AMCC_RXPTR);
-       b1dma_writel(card, 0xffffffff, AMCC_TXPTR);
-       if (b1dma_readl(card, AMCC_RXPTR) != 0xfffffffc
-           || b1dma_readl(card, AMCC_TXPTR) != 0xfffffffc)
-               return 2;
-
-       b1dma_writel(card, 0x0, AMCC_RXPTR);
-       b1dma_writel(card, 0x0, AMCC_TXPTR);
-       if (b1dma_readl(card, AMCC_RXPTR) != 0x0
-           || b1dma_readl(card, AMCC_TXPTR) != 0x0)
-               return 3;
-
-       t1outp(card->port, 0x10, 0x00);
-       t1outp(card->port, 0x07, 0x00);
-
-       t1outp(card->port, 0x02, 0x02);
-       t1outp(card->port, 0x03, 0x02);
-
-       if ((t1inp(card->port, 0x02) & 0xFE) != 0x02
-           || t1inp(card->port, 0x3) != 0x03)
-               return 4;
-
-       t1outp(card->port, 0x02, 0x00);
-       t1outp(card->port, 0x03, 0x00);
-
-       if ((t1inp(card->port, 0x02) & 0xFE) != 0x00
-           || t1inp(card->port, 0x3) != 0x01)
-               return 5;
-
-       return 0;
-}
-
-int t1pci_detect(avmcard *card)
-{
-       int ret;
-
-       if ((ret = b1dma_detect(card)) != 0)
-               return ret;
-
-       /* Transputer test */
-
-       if (WriteReg(card, 0x80001000, 0x11) != 0
-           || WriteReg(card, 0x80101000, 0x22) != 0
-           || WriteReg(card, 0x80201000, 0x33) != 0
-           || WriteReg(card, 0x80301000, 0x44) != 0)
-               return 6;
-
-       if (ReadReg(card, 0x80001000) != 0x11
-           || ReadReg(card, 0x80101000) != 0x22
-           || ReadReg(card, 0x80201000) != 0x33
-           || ReadReg(card, 0x80301000) != 0x44)
-               return 7;
-
-       if (WriteReg(card, 0x80001000, 0x55) != 0
-           || WriteReg(card, 0x80101000, 0x66) != 0
-           || WriteReg(card, 0x80201000, 0x77) != 0
-           || WriteReg(card, 0x80301000, 0x88) != 0)
-               return 8;
-
-       if (ReadReg(card, 0x80001000) != 0x55
-           || ReadReg(card, 0x80101000) != 0x66
-           || ReadReg(card, 0x80201000) != 0x77
-           || ReadReg(card, 0x80301000) != 0x88)
-               return 9;
-
-       return 0;
-}
-
-int b1pciv4_detect(avmcard *card)
-{
-       int ret, i;
-
-       if ((ret = b1dma_detect(card)) != 0)
-               return ret;
-
-       for (i = 0; i < 5; i++) {
-               if (WriteReg(card, 0x80A00000, 0x21) != 0)
-                       return 6;
-               if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01)
-                       return 7;
-       }
-       for (i = 0; i < 5; i++) {
-               if (WriteReg(card, 0x80A00000, 0x20) != 0)
-                       return 8;
-               if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00)
-                       return 9;
-       }
-
-       return 0;
-}
-
-static void b1dma_queue_tx(avmcard *card, struct sk_buff *skb)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-
-       skb_queue_tail(&card->dma->send_queue, skb);
-
-       if (!(card->csr & EN_TX_TC_INT)) {
-               b1dma_dispatch_tx(card);
-               b1dma_writel(card, card->csr, AMCC_INTCSR);
-       }
-
-       spin_unlock_irqrestore(&card->lock, flags);
-}
-
-/* ------------------------------------------------------------- */
-
-static void b1dma_dispatch_tx(avmcard *card)
-{
-       avmcard_dmainfo *dma = card->dma;
-       struct sk_buff *skb;
-       u8 cmd, subcmd;
-       u16 len;
-       u32 txlen;
-       void *p;
-
-       skb = skb_dequeue(&dma->send_queue);
-
-       len = CAPIMSG_LEN(skb->data);
-
-       if (len) {
-               cmd = CAPIMSG_COMMAND(skb->data);
-               subcmd = CAPIMSG_SUBCOMMAND(skb->data);
-
-               p = dma->sendbuf.dmabuf;
-
-               if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
-                       u16 dlen = CAPIMSG_DATALEN(skb->data);
-                       _put_byte(&p, SEND_DATA_B3_REQ);
-                       _put_slice(&p, skb->data, len);
-                       _put_slice(&p, skb->data + len, dlen);
-               } else {
-                       _put_byte(&p, SEND_MESSAGE);
-                       _put_slice(&p, skb->data, len);
-               }
-               txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
-#ifdef AVM_B1DMA_DEBUG
-               printk(KERN_DEBUG "tx: put msg len=%d\n", txlen);
-#endif
-       } else {
-               txlen = skb->len - 2;
-#ifdef AVM_B1DMA_POLLDEBUG
-               if (skb->data[2] == SEND_POLLACK)
-                       printk(KERN_INFO "%s: send ack\n", card->name);
-#endif
-#ifdef AVM_B1DMA_DEBUG
-               printk(KERN_DEBUG "tx: put 0x%x len=%d\n",
-                      skb->data[2], txlen);
-#endif
-               skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
-                                                skb->len - 2);
-       }
-       txlen = (txlen + 3) & ~3;
-
-       b1dma_writel(card, dma->sendbuf.dmaaddr, AMCC_TXPTR);
-       b1dma_writel(card, txlen, AMCC_TXLEN);
-
-       card->csr |= EN_TX_TC_INT;
-
-       dev_kfree_skb_any(skb);
-}
-
-/* ------------------------------------------------------------- */
-
-static void queue_pollack(avmcard *card)
-{
-       struct sk_buff *skb;
-       void *p;
-
-       skb = alloc_skb(3, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, lost poll ack\n",
-                      card->name);
-               return;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_POLLACK);
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       b1dma_queue_tx(card, skb);
-}
-
-/* ------------------------------------------------------------- */
-
-static void b1dma_handle_rx(avmcard *card)
-{
-       avmctrl_info *cinfo = &card->ctrlinfo[0];
-       avmcard_dmainfo *dma = card->dma;
-       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
-       struct sk_buff *skb;
-       void *p = dma->recvbuf.dmabuf + 4;
-       u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
-       u8 b1cmd =  _get_byte(&p);
-
-#ifdef AVM_B1DMA_DEBUG
-       printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
-#endif
-
-       switch (b1cmd) {
-       case RECEIVE_DATA_B3_IND:
-
-               ApplId = (unsigned) _get_word(&p);
-               MsgLen = _get_slice(&p, card->msgbuf);
-               DataB3Len = _get_slice(&p, card->databuf);
-
-               if (MsgLen < 30) { /* not CAPI 64Bit */
-                       memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
-                       MsgLen = 30;
-                       CAPIMSG_SETLEN(card->msgbuf, 30);
-               }
-               if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
-                       printk(KERN_ERR "%s: incoming packet dropped\n",
-                              card->name);
-               } else {
-                       skb_put_data(skb, card->msgbuf, MsgLen);
-                       skb_put_data(skb, card->databuf, DataB3Len);
-                       capi_ctr_handle_message(ctrl, ApplId, skb);
-               }
-               break;
-
-       case RECEIVE_MESSAGE:
-
-               ApplId = (unsigned) _get_word(&p);
-               MsgLen = _get_slice(&p, card->msgbuf);
-               if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
-                       printk(KERN_ERR "%s: incoming packet dropped\n",
-                              card->name);
-               } else {
-                       skb_put_data(skb, card->msgbuf, MsgLen);
-                       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) {
-                               spin_lock(&card->lock);
-                               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
-                                                    CAPIMSG_NCCI(skb->data),
-                                                    CAPIMSG_MSGID(skb->data));
-                               spin_unlock(&card->lock);
-                       }
-                       capi_ctr_handle_message(ctrl, ApplId, skb);
-               }
-               break;
-
-       case RECEIVE_NEW_NCCI:
-
-               ApplId = _get_word(&p);
-               NCCI = _get_word(&p);
-               WindowSize = _get_word(&p);
-               spin_lock(&card->lock);
-               capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
-               spin_unlock(&card->lock);
-               break;
-
-       case RECEIVE_FREE_NCCI:
-
-               ApplId = _get_word(&p);
-               NCCI = _get_word(&p);
-
-               if (NCCI != 0xffffffff) {
-                       spin_lock(&card->lock);
-                       capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
-                       spin_unlock(&card->lock);
-               }
-               break;
-
-       case RECEIVE_START:
-#ifdef AVM_B1DMA_POLLDEBUG
-               printk(KERN_INFO "%s: receive poll\n", card->name);
-#endif
-               if (!suppress_pollack)
-                       queue_pollack(card);
-               capi_ctr_resume_output(ctrl);
-               break;
-
-       case RECEIVE_STOP:
-               capi_ctr_suspend_output(ctrl);
-               break;
-
-       case RECEIVE_INIT:
-
-               cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
-               b1_parse_version(cinfo);
-               printk(KERN_INFO "%s: %s-card (%s) now active\n",
-                      card->name,
-                      cinfo->version[VER_CARDTYPE],
-                      cinfo->version[VER_DRIVER]);
-               capi_ctr_ready(ctrl);
-               break;
-
-       case RECEIVE_TASK_READY:
-               ApplId = (unsigned) _get_word(&p);
-               MsgLen = _get_slice(&p, card->msgbuf);
-               card->msgbuf[MsgLen] = 0;
-               while (MsgLen > 0
-                      && (card->msgbuf[MsgLen - 1] == '\n'
-                          || card->msgbuf[MsgLen - 1] == '\r')) {
-                       card->msgbuf[MsgLen - 1] = 0;
-                       MsgLen--;
-               }
-               printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
-                      card->name, ApplId, card->msgbuf);
-               break;
-
-       case RECEIVE_DEBUGMSG:
-               MsgLen = _get_slice(&p, card->msgbuf);
-               card->msgbuf[MsgLen] = 0;
-               while (MsgLen > 0
-                      && (card->msgbuf[MsgLen - 1] == '\n'
-                          || card->msgbuf[MsgLen - 1] == '\r')) {
-                       card->msgbuf[MsgLen - 1] = 0;
-                       MsgLen--;
-               }
-               printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
-               break;
-
-       default:
-               printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n",
-                      card->name, b1cmd);
-               return;
-       }
-}
-
-/* ------------------------------------------------------------- */
-
-static void b1dma_handle_interrupt(avmcard *card)
-{
-       u32 status;
-       u32 newcsr;
-
-       spin_lock(&card->lock);
-
-       status = b1dma_readl(card, AMCC_INTCSR);
-       if ((status & ANY_S5933_INT) == 0) {
-               spin_unlock(&card->lock);
-               return;
-       }
-
-       newcsr = card->csr | (status & ALL_INT);
-       if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
-       if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
-       b1dma_writel(card, newcsr, AMCC_INTCSR);
-
-       if ((status & RX_TC_INT) != 0) {
-               struct avmcard_dmainfo *dma = card->dma;
-               u32 rxlen;
-               if (card->dma->recvlen == 0) {
-                       rxlen = b1dma_readl(card, AMCC_RXLEN);
-                       if (rxlen == 0) {
-                               dma->recvlen = *((u32 *)dma->recvbuf.dmabuf);
-                               rxlen = (dma->recvlen + 3) & ~3;
-                               b1dma_writel(card, dma->recvbuf.dmaaddr + 4, AMCC_RXPTR);
-                               b1dma_writel(card, rxlen, AMCC_RXLEN);
-#ifdef AVM_B1DMA_DEBUG
-                       } else {
-                               printk(KERN_ERR "%s: rx not complete (%d).\n",
-                                      card->name, rxlen);
-#endif
-                       }
-               } else {
-                       spin_unlock(&card->lock);
-                       b1dma_handle_rx(card);
-                       dma->recvlen = 0;
-                       spin_lock(&card->lock);
-                       b1dma_writel(card, dma->recvbuf.dmaaddr, AMCC_RXPTR);
-                       b1dma_writel(card, 4, AMCC_RXLEN);
-               }
-       }
-
-       if ((status & TX_TC_INT) != 0) {
-               if (skb_queue_empty(&card->dma->send_queue))
-                       card->csr &= ~EN_TX_TC_INT;
-               else
-                       b1dma_dispatch_tx(card);
-       }
-       b1dma_writel(card, card->csr, AMCC_INTCSR);
-
-       spin_unlock(&card->lock);
-}
-
-irqreturn_t b1dma_interrupt(int interrupt, void *devptr)
-{
-       avmcard *card = devptr;
-
-       b1dma_handle_interrupt(card);
-       return IRQ_HANDLED;
-}
-
-/* ------------------------------------------------------------- */
-
-static int b1dma_loaded(avmcard *card)
-{
-       unsigned long stop;
-       unsigned char ans;
-       unsigned long tout = 2;
-       unsigned int base = card->port;
-
-       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
-               if (b1_tx_empty(base))
-                       break;
-       }
-       if (!b1_tx_empty(base)) {
-               printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n",
-                      card->name);
-               return 0;
-       }
-       b1_put_byte(base, SEND_POLLACK);
-       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
-               if (b1_rx_full(base)) {
-                       if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
-                               return 1;
-                       }
-                       printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
-                       return 0;
-               }
-       }
-       printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name);
-       return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static void b1dma_send_init(avmcard *card)
-{
-       struct sk_buff *skb;
-       void *p;
-
-       skb = alloc_skb(15, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, lost register appl.\n",
-                      card->name);
-               return;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_INIT);
-       _put_word(&p, CAPI_MAXAPPL);
-       _put_word(&p, AVM_NCCI_PER_CHANNEL * 30);
-       _put_word(&p, card->cardnr - 1);
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       b1dma_queue_tx(card, skb);
-}
-
-int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       int retval;
-
-       b1dma_reset(card);
-
-       if ((retval = b1_load_t4file(card, &data->firmware))) {
-               b1dma_reset(card);
-               printk(KERN_ERR "%s: failed to load t4file!!\n",
-                      card->name);
-               return retval;
-       }
-
-       if (data->configuration.len > 0 && data->configuration.data) {
-               if ((retval = b1_load_config(card, &data->configuration))) {
-                       b1dma_reset(card);
-                       printk(KERN_ERR "%s: failed to load config!!\n",
-                              card->name);
-                       return retval;
-               }
-       }
-
-       if (!b1dma_loaded(card)) {
-               b1dma_reset(card);
-               printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
-               return -EIO;
-       }
-
-       card->csr = AVM_FLAG;
-       b1dma_writel(card, card->csr, AMCC_INTCSR);
-       b1dma_writel(card, EN_A2P_TRANSFERS | EN_P2A_TRANSFERS | A2P_HI_PRIORITY |
-                    P2A_HI_PRIORITY | RESET_A2P_FLAGS | RESET_P2A_FLAGS,
-                    AMCC_MCSR);
-       t1outp(card->port, 0x07, 0x30);
-       t1outp(card->port, 0x10, 0xF0);
-
-       card->dma->recvlen = 0;
-       b1dma_writel(card, card->dma->recvbuf.dmaaddr, AMCC_RXPTR);
-       b1dma_writel(card, 4, AMCC_RXLEN);
-       card->csr |= EN_RX_TC_INT;
-       b1dma_writel(card, card->csr, AMCC_INTCSR);
-
-       b1dma_send_init(card);
-
-       return 0;
-}
-
-void b1dma_reset_ctr(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-       b1dma_reset(card);
-
-       memset(cinfo->version, 0, sizeof(cinfo->version));
-       capilib_release(&cinfo->ncci_head);
-       spin_unlock_irqrestore(&card->lock, flags);
-       capi_ctr_down(ctrl);
-}
-
-/* ------------------------------------------------------------- */
-
-void b1dma_register_appl(struct capi_ctr *ctrl,
-                        u16 appl,
-                        capi_register_params *rp)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       struct sk_buff *skb;
-       int want = rp->level3cnt;
-       int nconn;
-       void *p;
-
-       if (want > 0) nconn = want;
-       else nconn = ctrl->profile.nbchannel * -want;
-       if (nconn == 0) nconn = ctrl->profile.nbchannel;
-
-       skb = alloc_skb(23, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, lost register appl.\n",
-                      card->name);
-               return;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_REGISTER);
-       _put_word(&p, appl);
-       _put_word(&p, 1024 * (nconn + 1));
-       _put_word(&p, nconn);
-       _put_word(&p, rp->datablkcnt);
-       _put_word(&p, rp->datablklen);
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       b1dma_queue_tx(card, skb);
-}
-
-/* ------------------------------------------------------------- */
-
-void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       struct sk_buff *skb;
-       void *p;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-       capilib_release_appl(&cinfo->ncci_head, appl);
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       skb = alloc_skb(7, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, lost release appl.\n",
-                      card->name);
-               return;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_RELEASE);
-       _put_word(&p, appl);
-
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       b1dma_queue_tx(card, skb);
-}
-
-/* ------------------------------------------------------------- */
-
-u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       u16 retval = CAPI_NOERROR;
-
-       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
-               unsigned long flags;
-               spin_lock_irqsave(&card->lock, flags);
-               retval = capilib_data_b3_req(&cinfo->ncci_head,
-                                            CAPIMSG_APPID(skb->data),
-                                            CAPIMSG_NCCI(skb->data),
-                                            CAPIMSG_MSGID(skb->data));
-               spin_unlock_irqrestore(&card->lock, flags);
-       }
-       if (retval == CAPI_NOERROR)
-               b1dma_queue_tx(card, skb);
-
-       return retval;
-}
-
-/* ------------------------------------------------------------- */
-
-int b1dma_proc_show(struct seq_file *m, void *v)
-{
-       struct capi_ctr *ctrl = m->private;
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       u8 flag;
-       char *s;
-       u32 txoff, txlen, rxoff, rxlen, csr;
-       unsigned long flags;
-
-       seq_printf(m, "%-16s %s\n", "name", card->name);
-       seq_printf(m, "%-16s 0x%x\n", "io", card->port);
-       seq_printf(m, "%-16s %d\n", "irq", card->irq);
-       seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase);
-       switch (card->cardtype) {
-       case avm_b1isa: s = "B1 ISA"; break;
-       case avm_b1pci: s = "B1 PCI"; break;
-       case avm_b1pcmcia: s = "B1 PCMCIA"; break;
-       case avm_m1: s = "M1"; break;
-       case avm_m2: s = "M2"; break;
-       case avm_t1isa: s = "T1 ISA (HEMA)"; break;
-       case avm_t1pci: s = "T1 PCI"; break;
-       case avm_c4: s = "C4"; break;
-       case avm_c2: s = "C2"; break;
-       default: s = "???"; break;
-       }
-       seq_printf(m, "%-16s %s\n", "type", s);
-       if ((s = cinfo->version[VER_DRIVER]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_driver", s);
-       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
-       if ((s = cinfo->version[VER_SERIAL]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_serial", s);
-
-       if (card->cardtype != avm_m1) {
-               flag = ((u8 *)(ctrl->profile.manu))[3];
-               if (flag)
-                       seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
-                                  "protocol",
-                                  (flag & 0x01) ? " DSS1" : "",
-                                  (flag & 0x02) ? " CT1" : "",
-                                  (flag & 0x04) ? " VN3" : "",
-                                  (flag & 0x08) ? " NI1" : "",
-                                  (flag & 0x10) ? " AUSTEL" : "",
-                                  (flag & 0x20) ? " ESS" : "",
-                                  (flag & 0x40) ? " 1TR6" : ""
-                               );
-       }
-       if (card->cardtype != avm_m1) {
-               flag = ((u8 *)(ctrl->profile.manu))[5];
-               if (flag)
-                       seq_printf(m, "%-16s%s%s%s%s\n",
-                                  "linetype",
-                                  (flag & 0x01) ? " point to point" : "",
-                                  (flag & 0x02) ? " point to multipoint" : "",
-                                  (flag & 0x08) ? " leased line without D-channel" : "",
-                                  (flag & 0x04) ? " leased line with D-channel" : ""
-                               );
-       }
-       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
-
-
-       spin_lock_irqsave(&card->lock, flags);
-
-       txoff = (dma_addr_t)b1dma_readl(card, AMCC_TXPTR)-card->dma->sendbuf.dmaaddr;
-       txlen = b1dma_readl(card, AMCC_TXLEN);
-
-       rxoff = (dma_addr_t)b1dma_readl(card, AMCC_RXPTR)-card->dma->recvbuf.dmaaddr;
-       rxlen = b1dma_readl(card, AMCC_RXLEN);
-
-       csr  = b1dma_readl(card, AMCC_INTCSR);
-
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       seq_printf(m, "%-16s 0x%lx\n", "csr (cached)", (unsigned long)card->csr);
-       seq_printf(m, "%-16s 0x%lx\n", "csr", (unsigned long)csr);
-       seq_printf(m, "%-16s %lu\n", "txoff", (unsigned long)txoff);
-       seq_printf(m, "%-16s %lu\n", "txlen", (unsigned long)txlen);
-       seq_printf(m, "%-16s %lu\n", "rxoff", (unsigned long)rxoff);
-       seq_printf(m, "%-16s %lu\n", "rxlen", (unsigned long)rxlen);
-
-       return 0;
-}
-EXPORT_SYMBOL(b1dma_proc_show);
-
-/* ------------------------------------------------------------- */
-
-EXPORT_SYMBOL(b1dma_reset);
-EXPORT_SYMBOL(t1pci_detect);
-EXPORT_SYMBOL(b1pciv4_detect);
-EXPORT_SYMBOL(b1dma_interrupt);
-
-EXPORT_SYMBOL(b1dma_load_firmware);
-EXPORT_SYMBOL(b1dma_reset_ctr);
-EXPORT_SYMBOL(b1dma_register_appl);
-EXPORT_SYMBOL(b1dma_release_appl);
-EXPORT_SYMBOL(b1dma_send_message);
-
-static int __init b1dma_init(void)
-{
-       char *p;
-       char rev[32];
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, sizeof(rev));
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-       printk(KERN_INFO "b1dma: revision %s\n", rev);
-
-       return 0;
-}
-
-static void __exit b1dma_exit(void)
-{
-}
-
-module_init(b1dma_init);
-module_exit(b1dma_exit);
diff --git a/drivers/isdn/hardware/avm/b1isa.c b/drivers/isdn/hardware/avm/b1isa.c
deleted file mode 100644 (file)
index cdfea72..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/* $Id: b1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
- *
- * Module for AVM B1 ISA-card.
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/capi.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <asm/io.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-
-/* ------------------------------------------------------------- */
-
-static char *revision = "$Revision: 1.1.2.3 $";
-
-/* ------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 ISA card");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------- */
-
-static void b1isa_remove(struct pci_dev *pdev)
-{
-       avmctrl_info *cinfo = pci_get_drvdata(pdev);
-       avmcard *card;
-
-       if (!cinfo)
-               return;
-
-       card = cinfo->card;
-
-       b1_reset(card->port);
-       b1_reset(card->port);
-
-       detach_capi_ctr(&cinfo->capi_ctrl);
-       free_irq(card->irq, card);
-       release_region(card->port, AVMB1_PORTLEN);
-       b1_free_card(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static char *b1isa_procinfo(struct capi_ctr *ctrl);
-
-static int b1isa_probe(struct pci_dev *pdev)
-{
-       avmctrl_info *cinfo;
-       avmcard *card;
-       int retval;
-
-       card = b1_alloc_card(1);
-       if (!card) {
-               printk(KERN_WARNING "b1isa: no memory.\n");
-               retval = -ENOMEM;
-               goto err;
-       }
-
-       cinfo = card->ctrlinfo;
-
-       card->port = pci_resource_start(pdev, 0);
-       card->irq = pdev->irq;
-       card->cardtype = avm_b1isa;
-       sprintf(card->name, "b1isa-%x", card->port);
-
-       if (card->port != 0x150 && card->port != 0x250
-           && card->port != 0x300 && card->port != 0x340) {
-               printk(KERN_WARNING "b1isa: invalid port 0x%x.\n", card->port);
-               retval = -EINVAL;
-               goto err_free;
-       }
-       if (b1_irq_table[card->irq & 0xf] == 0) {
-               printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq);
-               retval = -EINVAL;
-               goto err_free;
-       }
-       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
-               printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n",
-                      card->port, card->port + AVMB1_PORTLEN);
-               retval = -EBUSY;
-               goto err_free;
-       }
-       retval = request_irq(card->irq, b1_interrupt, 0, card->name, card);
-       if (retval) {
-               printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq);
-               goto err_release_region;
-       }
-       b1_reset(card->port);
-       if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
-               printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n",
-                      card->port, retval);
-               retval = -ENODEV;
-               goto err_free_irq;
-       }
-       b1_reset(card->port);
-       b1_getrevision(card);
-
-       cinfo->capi_ctrl.owner = THIS_MODULE;
-       cinfo->capi_ctrl.driver_name   = "b1isa";
-       cinfo->capi_ctrl.driverdata    = cinfo;
-       cinfo->capi_ctrl.register_appl = b1_register_appl;
-       cinfo->capi_ctrl.release_appl  = b1_release_appl;
-       cinfo->capi_ctrl.send_message  = b1_send_message;
-       cinfo->capi_ctrl.load_firmware = b1_load_firmware;
-       cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
-       cinfo->capi_ctrl.procinfo      = b1isa_procinfo;
-       cinfo->capi_ctrl.proc_show     = b1_proc_show;
-       strcpy(cinfo->capi_ctrl.name, card->name);
-
-       retval = attach_capi_ctr(&cinfo->capi_ctrl);
-       if (retval) {
-               printk(KERN_ERR "b1isa: attach controller failed.\n");
-               goto err_free_irq;
-       }
-
-       printk(KERN_INFO "b1isa: AVM B1 ISA at i/o %#x, irq %d, revision %d\n",
-              card->port, card->irq, card->revision);
-
-       pci_set_drvdata(pdev, cinfo);
-       return 0;
-
-err_free_irq:
-       free_irq(card->irq, card);
-err_release_region:
-       release_region(card->port, AVMB1_PORTLEN);
-err_free:
-       b1_free_card(card);
-err:
-       return retval;
-}
-
-static char *b1isa_procinfo(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->port : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               cinfo->card ? cinfo->card->revision : 0
-               );
-       return cinfo->infobuf;
-}
-
-/* ------------------------------------------------------------- */
-
-#define MAX_CARDS 4
-static struct pci_dev isa_dev[MAX_CARDS];
-static int io[MAX_CARDS];
-static int irq[MAX_CARDS];
-
-module_param_hw_array(io, int, ioport, NULL, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-MODULE_PARM_DESC(io, "I/O base address(es)");
-MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
-
-static int b1isa_add_card(struct capi_driver *driver, capicardparams *data)
-{
-       int i;
-
-       for (i = 0; i < MAX_CARDS; i++) {
-               if (isa_dev[i].resource[0].start)
-                       continue;
-
-               isa_dev[i].resource[0].start = data->port;
-               isa_dev[i].irq = data->irq;
-
-               if (b1isa_probe(&isa_dev[i]) == 0)
-                       return 0;
-       }
-       return -ENODEV;
-}
-
-static struct capi_driver capi_driver_b1isa = {
-       .name           = "b1isa",
-       .revision       = "1.0",
-       .add_card       = b1isa_add_card,
-};
-
-static int __init b1isa_init(void)
-{
-       char *p;
-       char rev[32];
-       int i;
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, 32);
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-       for (i = 0; i < MAX_CARDS; i++) {
-               if (!io[i])
-                       break;
-
-               isa_dev[i].resource[0].start = io[i];
-               isa_dev[i].irq = irq[i];
-
-               if (b1isa_probe(&isa_dev[i]) != 0)
-                       return -ENODEV;
-       }
-
-       strlcpy(capi_driver_b1isa.revision, rev, 32);
-       register_capi_driver(&capi_driver_b1isa);
-       printk(KERN_INFO "b1isa: revision %s\n", rev);
-
-       return 0;
-}
-
-static void __exit b1isa_exit(void)
-{
-       int i;
-
-       for (i = 0; i < MAX_CARDS; i++) {
-               if (isa_dev[i].resource[0].start)
-                       b1isa_remove(&isa_dev[i]);
-       }
-       unregister_capi_driver(&capi_driver_b1isa);
-}
-
-module_init(b1isa_init);
-module_exit(b1isa_exit);
diff --git a/drivers/isdn/hardware/avm/b1pci.c b/drivers/isdn/hardware/avm/b1pci.c
deleted file mode 100644 (file)
index b76b57a..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-/* $Id: b1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
- *
- * Module for AVM B1 PCI-card.
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/capi.h>
-#include <asm/io.h>
-#include <linux/init.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-
-/* ------------------------------------------------------------- */
-
-static char *revision = "$Revision: 1.1.2.2 $";
-
-/* ------------------------------------------------------------- */
-
-static struct pci_device_id b1pci_pci_tbl[] = {
-       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, PCI_ANY_ID, PCI_ANY_ID },
-       { }                             /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(pci, b1pci_pci_tbl);
-MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 PCI card");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------- */
-
-static char *b1pci_procinfo(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->port : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               cinfo->card ? cinfo->card->revision : 0
-               );
-       return cinfo->infobuf;
-}
-
-/* ------------------------------------------------------------- */
-
-static int b1pci_probe(struct capicardparams *p, struct pci_dev *pdev)
-{
-       avmcard *card;
-       avmctrl_info *cinfo;
-       int retval;
-
-       card = b1_alloc_card(1);
-       if (!card) {
-               printk(KERN_WARNING "b1pci: no memory.\n");
-               retval = -ENOMEM;
-               goto err;
-       }
-
-       cinfo = card->ctrlinfo;
-       sprintf(card->name, "b1pci-%x", p->port);
-       card->port = p->port;
-       card->irq = p->irq;
-       card->cardtype = avm_b1pci;
-
-       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
-               printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
-                      card->port, card->port + AVMB1_PORTLEN);
-               retval = -EBUSY;
-               goto err_free;
-       }
-       b1_reset(card->port);
-       retval = b1_detect(card->port, card->cardtype);
-       if (retval) {
-               printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
-                      card->port, retval);
-               retval = -ENODEV;
-               goto err_release_region;
-       }
-       b1_reset(card->port);
-       b1_getrevision(card);
-
-       retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
-       if (retval) {
-               printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq);
-               retval = -EBUSY;
-               goto err_release_region;
-       }
-
-       cinfo->capi_ctrl.driver_name   = "b1pci";
-       cinfo->capi_ctrl.driverdata    = cinfo;
-       cinfo->capi_ctrl.register_appl = b1_register_appl;
-       cinfo->capi_ctrl.release_appl  = b1_release_appl;
-       cinfo->capi_ctrl.send_message  = b1_send_message;
-       cinfo->capi_ctrl.load_firmware = b1_load_firmware;
-       cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
-       cinfo->capi_ctrl.procinfo      = b1pci_procinfo;
-       cinfo->capi_ctrl.proc_show     = b1_proc_show;
-       strcpy(cinfo->capi_ctrl.name, card->name);
-       cinfo->capi_ctrl.owner         = THIS_MODULE;
-
-       retval = attach_capi_ctr(&cinfo->capi_ctrl);
-       if (retval) {
-               printk(KERN_ERR "b1pci: attach controller failed.\n");
-               goto err_free_irq;
-       }
-
-       if (card->revision >= 4) {
-               printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n",
-                      card->port, card->irq, card->revision);
-       } else {
-               printk(KERN_INFO "b1pci: AVM B1 PCI at i/o %#x, irq %d, revision %d\n",
-                      card->port, card->irq, card->revision);
-       }
-
-       pci_set_drvdata(pdev, card);
-       return 0;
-
-err_free_irq:
-       free_irq(card->irq, card);
-err_release_region:
-       release_region(card->port, AVMB1_PORTLEN);
-err_free:
-       b1_free_card(card);
-err:
-       return retval;
-}
-
-static void b1pci_remove(struct pci_dev *pdev)
-{
-       avmcard *card = pci_get_drvdata(pdev);
-       avmctrl_info *cinfo = card->ctrlinfo;
-       unsigned int port = card->port;
-
-       b1_reset(port);
-       b1_reset(port);
-
-       detach_capi_ctr(&cinfo->capi_ctrl);
-       free_irq(card->irq, card);
-       release_region(card->port, AVMB1_PORTLEN);
-       b1_free_card(card);
-}
-
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-/* ------------------------------------------------------------- */
-
-static char *b1pciv4_procinfo(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->port : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               cinfo->card ? cinfo->card->membase : 0,
-               cinfo->card ? cinfo->card->revision : 0
-               );
-       return cinfo->infobuf;
-}
-
-/* ------------------------------------------------------------- */
-
-static int b1pciv4_probe(struct capicardparams *p, struct pci_dev *pdev)
-{
-       avmcard *card;
-       avmctrl_info *cinfo;
-       int retval;
-
-       card = b1_alloc_card(1);
-       if (!card) {
-               printk(KERN_WARNING "b1pci: no memory.\n");
-               retval = -ENOMEM;
-               goto err;
-       }
-
-       card->dma = avmcard_dma_alloc("b1pci", pdev, 2048 + 128, 2048 + 128);
-       if (!card->dma) {
-               printk(KERN_WARNING "b1pci: dma alloc.\n");
-               retval = -ENOMEM;
-               goto err_free;
-       }
-
-       cinfo = card->ctrlinfo;
-       sprintf(card->name, "b1pciv4-%x", p->port);
-       card->port = p->port;
-       card->irq = p->irq;
-       card->membase = p->membase;
-       card->cardtype = avm_b1pci;
-
-       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
-               printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
-                      card->port, card->port + AVMB1_PORTLEN);
-               retval = -EBUSY;
-               goto err_free_dma;
-       }
-
-       card->mbase = ioremap(card->membase, 64);
-       if (!card->mbase) {
-               printk(KERN_NOTICE "b1pci: can't remap memory at 0x%lx\n",
-                      card->membase);
-               retval = -ENOMEM;
-               goto err_release_region;
-       }
-
-       b1dma_reset(card);
-
-       retval = b1pciv4_detect(card);
-       if (retval) {
-               printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
-                      card->port, retval);
-               retval = -ENODEV;
-               goto err_unmap;
-       }
-       b1dma_reset(card);
-       b1_getrevision(card);
-
-       retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
-       if (retval) {
-               printk(KERN_ERR "b1pci: unable to get IRQ %d.\n",
-                      card->irq);
-               retval = -EBUSY;
-               goto err_unmap;
-       }
-
-       cinfo->capi_ctrl.owner         = THIS_MODULE;
-       cinfo->capi_ctrl.driver_name   = "b1pciv4";
-       cinfo->capi_ctrl.driverdata    = cinfo;
-       cinfo->capi_ctrl.register_appl = b1dma_register_appl;
-       cinfo->capi_ctrl.release_appl  = b1dma_release_appl;
-       cinfo->capi_ctrl.send_message  = b1dma_send_message;
-       cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
-       cinfo->capi_ctrl.reset_ctr     = b1dma_reset_ctr;
-       cinfo->capi_ctrl.procinfo      = b1pciv4_procinfo;
-       cinfo->capi_ctrl.proc_show     = b1dma_proc_show;
-       strcpy(cinfo->capi_ctrl.name, card->name);
-
-       retval = attach_capi_ctr(&cinfo->capi_ctrl);
-       if (retval) {
-               printk(KERN_ERR "b1pci: attach controller failed.\n");
-               goto err_free_irq;
-       }
-       card->cardnr = cinfo->capi_ctrl.cnr;
-
-       printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n",
-              card->port, card->irq, card->membase, card->revision);
-
-       pci_set_drvdata(pdev, card);
-       return 0;
-
-err_free_irq:
-       free_irq(card->irq, card);
-err_unmap:
-       iounmap(card->mbase);
-err_release_region:
-       release_region(card->port, AVMB1_PORTLEN);
-err_free_dma:
-       avmcard_dma_free(card->dma);
-err_free:
-       b1_free_card(card);
-err:
-       return retval;
-
-}
-
-static void b1pciv4_remove(struct pci_dev *pdev)
-{
-       avmcard *card = pci_get_drvdata(pdev);
-       avmctrl_info *cinfo = card->ctrlinfo;
-
-       b1dma_reset(card);
-
-       detach_capi_ctr(&cinfo->capi_ctrl);
-       free_irq(card->irq, card);
-       iounmap(card->mbase);
-       release_region(card->port, AVMB1_PORTLEN);
-       avmcard_dma_free(card->dma);
-       b1_free_card(card);
-}
-
-#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
-
-static int b1pci_pci_probe(struct pci_dev *pdev,
-                          const struct pci_device_id *ent)
-{
-       struct capicardparams param;
-       int retval;
-
-       if (pci_enable_device(pdev) < 0) {
-               printk(KERN_ERR "b1pci: failed to enable AVM-B1\n");
-               return -ENODEV;
-       }
-       param.irq = pdev->irq;
-
-       if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-               pci_set_master(pdev);
-#endif
-               param.membase = pci_resource_start(pdev, 0);
-               param.port = pci_resource_start(pdev, 2);
-
-               printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
-                      param.port, param.irq, param.membase);
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-               retval = b1pciv4_probe(&param, pdev);
-#else
-               retval = b1pci_probe(&param, pdev);
-#endif
-               if (retval != 0) {
-                       printk(KERN_ERR "b1pci: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n",
-                              param.port, param.irq, param.membase);
-               }
-       } else {
-               param.membase = 0;
-               param.port = pci_resource_start(pdev, 1);
-
-               printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
-                      param.port, param.irq);
-               retval = b1pci_probe(&param, pdev);
-               if (retval != 0) {
-                       printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
-                              param.port, param.irq);
-               }
-       }
-       return retval;
-}
-
-static void b1pci_pci_remove(struct pci_dev *pdev)
-{
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-       avmcard *card = pci_get_drvdata(pdev);
-
-       if (card->dma)
-               b1pciv4_remove(pdev);
-       else
-               b1pci_remove(pdev);
-#else
-       b1pci_remove(pdev);
-#endif
-}
-
-static struct pci_driver b1pci_pci_driver = {
-       .name           = "b1pci",
-       .id_table       = b1pci_pci_tbl,
-       .probe          = b1pci_pci_probe,
-       .remove         = b1pci_pci_remove,
-};
-
-static struct capi_driver capi_driver_b1pci = {
-       .name           = "b1pci",
-       .revision       = "1.0",
-};
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-static struct capi_driver capi_driver_b1pciv4 = {
-       .name           = "b1pciv4",
-       .revision       = "1.0",
-};
-#endif
-
-static int __init b1pci_init(void)
-{
-       char *p;
-       char rev[32];
-       int err;
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, 32);
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-
-       err = pci_register_driver(&b1pci_pci_driver);
-       if (!err) {
-               strlcpy(capi_driver_b1pci.revision, rev, 32);
-               register_capi_driver(&capi_driver_b1pci);
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-               strlcpy(capi_driver_b1pciv4.revision, rev, 32);
-               register_capi_driver(&capi_driver_b1pciv4);
-#endif
-               printk(KERN_INFO "b1pci: revision %s\n", rev);
-       }
-       return err;
-}
-
-static void __exit b1pci_exit(void)
-{
-       unregister_capi_driver(&capi_driver_b1pci);
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-       unregister_capi_driver(&capi_driver_b1pciv4);
-#endif
-       pci_unregister_driver(&b1pci_pci_driver);
-}
-
-module_init(b1pci_init);
-module_exit(b1pci_exit);
diff --git a/drivers/isdn/hardware/avm/b1pcmcia.c b/drivers/isdn/hardware/avm/b1pcmcia.c
deleted file mode 100644 (file)
index 3aca16e..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/* $Id: b1pcmcia.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
- *
- * Module for AVM B1/M1/M2 PCMCIA-card.
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/capi.h>
-#include <linux/b1pcmcia.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-
-/* ------------------------------------------------------------- */
-
-static char *revision = "$Revision: 1.1.2.2 $";
-
-/* ------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM PCMCIA cards");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------- */
-
-static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-
-       b1_reset(port);
-       b1_reset(port);
-
-       detach_capi_ctr(ctrl);
-       free_irq(card->irq, card);
-       b1_free_card(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static LIST_HEAD(cards);
-
-static char *b1pcmcia_procinfo(struct capi_ctr *ctrl);
-
-static int b1pcmcia_add_card(unsigned int port, unsigned irq,
-                            enum avmcardtype cardtype)
-{
-       avmctrl_info *cinfo;
-       avmcard *card;
-       char *cardname;
-       int retval;
-
-       card = b1_alloc_card(1);
-       if (!card) {
-               printk(KERN_WARNING "b1pcmcia: no memory.\n");
-               retval = -ENOMEM;
-               goto err;
-       }
-       cinfo = card->ctrlinfo;
-
-       switch (cardtype) {
-       case avm_m1: sprintf(card->name, "m1-%x", port); break;
-       case avm_m2: sprintf(card->name, "m2-%x", port); break;
-       default: sprintf(card->name, "b1pcmcia-%x", port); break;
-       }
-       card->port = port;
-       card->irq = irq;
-       card->cardtype = cardtype;
-
-       retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
-       if (retval) {
-               printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n",
-                      card->irq);
-               retval = -EBUSY;
-               goto err_free;
-       }
-       b1_reset(card->port);
-       if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
-               printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n",
-                      card->port, retval);
-               retval = -ENODEV;
-               goto err_free_irq;
-       }
-       b1_reset(card->port);
-       b1_getrevision(card);
-
-       cinfo->capi_ctrl.owner         = THIS_MODULE;
-       cinfo->capi_ctrl.driver_name   = "b1pcmcia";
-       cinfo->capi_ctrl.driverdata    = cinfo;
-       cinfo->capi_ctrl.register_appl = b1_register_appl;
-       cinfo->capi_ctrl.release_appl  = b1_release_appl;
-       cinfo->capi_ctrl.send_message  = b1_send_message;
-       cinfo->capi_ctrl.load_firmware = b1_load_firmware;
-       cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
-       cinfo->capi_ctrl.procinfo      = b1pcmcia_procinfo;
-       cinfo->capi_ctrl.proc_show     = b1_proc_show;
-       strcpy(cinfo->capi_ctrl.name, card->name);
-
-       retval = attach_capi_ctr(&cinfo->capi_ctrl);
-       if (retval) {
-               printk(KERN_ERR "b1pcmcia: attach controller failed.\n");
-               goto err_free_irq;
-       }
-       switch (cardtype) {
-       case avm_m1: cardname = "M1"; break;
-       case avm_m2: cardname = "M2"; break;
-       default: cardname = "B1 PCMCIA"; break;
-       }
-
-       printk(KERN_INFO "b1pcmcia: AVM %s at i/o %#x, irq %d, revision %d\n",
-              cardname, card->port, card->irq, card->revision);
-
-       list_add(&card->list, &cards);
-       return cinfo->capi_ctrl.cnr;
-
-err_free_irq:
-       free_irq(card->irq, card);
-err_free:
-       b1_free_card(card);
-err:
-       return retval;
-}
-
-/* ------------------------------------------------------------- */
-
-static char *b1pcmcia_procinfo(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->port : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               cinfo->card ? cinfo->card->revision : 0
-               );
-       return cinfo->infobuf;
-}
-
-/* ------------------------------------------------------------- */
-
-int b1pcmcia_addcard_b1(unsigned int port, unsigned irq)
-{
-       return b1pcmcia_add_card(port, irq, avm_b1pcmcia);
-}
-
-int b1pcmcia_addcard_m1(unsigned int port, unsigned irq)
-{
-       return b1pcmcia_add_card(port, irq, avm_m1);
-}
-
-int b1pcmcia_addcard_m2(unsigned int port, unsigned irq)
-{
-       return b1pcmcia_add_card(port, irq, avm_m2);
-}
-
-int b1pcmcia_delcard(unsigned int port, unsigned irq)
-{
-       struct list_head *l;
-       avmcard *card;
-
-       list_for_each(l, &cards) {
-               card = list_entry(l, avmcard, list);
-               if (card->port == port && card->irq == irq) {
-                       b1pcmcia_remove_ctr(&card->ctrlinfo[0].capi_ctrl);
-                       return 0;
-               }
-       }
-       return -ESRCH;
-}
-
-EXPORT_SYMBOL(b1pcmcia_addcard_b1);
-EXPORT_SYMBOL(b1pcmcia_addcard_m1);
-EXPORT_SYMBOL(b1pcmcia_addcard_m2);
-EXPORT_SYMBOL(b1pcmcia_delcard);
-
-static struct capi_driver capi_driver_b1pcmcia = {
-       .name           = "b1pcmcia",
-       .revision       = "1.0",
-};
-
-static int __init b1pcmcia_init(void)
-{
-       char *p;
-       char rev[32];
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, 32);
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-       strlcpy(capi_driver_b1pcmcia.revision, rev, 32);
-       register_capi_driver(&capi_driver_b1pcmcia);
-       printk(KERN_INFO "b1pci: revision %s\n", rev);
-
-       return 0;
-}
-
-static void __exit b1pcmcia_exit(void)
-{
-       unregister_capi_driver(&capi_driver_b1pcmcia);
-}
-
-module_init(b1pcmcia_init);
-module_exit(b1pcmcia_exit);
diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c
deleted file mode 100644 (file)
index ac72cd2..0000000
+++ /dev/null
@@ -1,1317 +0,0 @@
-/* $Id: c4.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
- *
- * Module for AVM C4 & C2 card.
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/capi.h>
-#include <linux/kernelcapi.h>
-#include <linux/init.h>
-#include <linux/gfp.h>
-#include <asm/io.h>
-#include <linux/uaccess.h>
-#include <linux/netdevice.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-
-#undef AVM_C4_DEBUG
-#undef AVM_C4_POLLDEBUG
-
-/* ------------------------------------------------------------- */
-
-static char *revision = "$Revision: 1.1.2.2 $";
-
-/* ------------------------------------------------------------- */
-
-static bool suppress_pollack;
-
-static const struct pci_device_id c4_pci_tbl[] = {
-       { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 },
-       { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 },
-       { }                     /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(pci, c4_pci_tbl);
-MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-module_param(suppress_pollack, bool, 0);
-
-/* ------------------------------------------------------------- */
-
-static void c4_dispatch_tx(avmcard *card);
-
-/* ------------------------------------------------------------- */
-
-#define DC21285_DRAM_A0MR      0x40000000
-#define DC21285_DRAM_A1MR      0x40004000
-#define DC21285_DRAM_A2MR      0x40008000
-#define DC21285_DRAM_A3MR      0x4000C000
-
-#define        CAS_OFFSET      0x88
-
-#define DC21285_ARMCSR_BASE    0x42000000
-
-#define        PCI_OUT_INT_STATUS      0x30
-#define        PCI_OUT_INT_MASK        0x34
-#define        MAILBOX_0               0x50
-#define        MAILBOX_1               0x54
-#define        MAILBOX_2               0x58
-#define        MAILBOX_3               0x5C
-#define        DOORBELL                0x60
-#define        DOORBELL_SETUP          0x64
-
-#define CHAN_1_CONTROL         0x90
-#define CHAN_2_CONTROL         0xB0
-#define DRAM_TIMING            0x10C
-#define DRAM_ADDR_SIZE_0       0x110
-#define DRAM_ADDR_SIZE_1       0x114
-#define DRAM_ADDR_SIZE_2       0x118
-#define DRAM_ADDR_SIZE_3       0x11C
-#define        SA_CONTROL              0x13C
-#define        XBUS_CYCLE              0x148
-#define        XBUS_STROBE             0x14C
-#define        DBELL_PCI_MASK          0x150
-#define DBELL_SA_MASK          0x154
-
-#define SDRAM_SIZE             0x1000000
-
-/* ------------------------------------------------------------- */
-
-#define        MBOX_PEEK_POKE          MAILBOX_0
-
-#define DBELL_ADDR             0x01
-#define DBELL_DATA             0x02
-#define DBELL_RNWR             0x40
-#define DBELL_INIT             0x80
-
-/* ------------------------------------------------------------- */
-
-#define        MBOX_UP_ADDR            MAILBOX_0
-#define        MBOX_UP_LEN             MAILBOX_1
-#define        MBOX_DOWN_ADDR          MAILBOX_2
-#define        MBOX_DOWN_LEN           MAILBOX_3
-
-#define        DBELL_UP_HOST           0x00000100
-#define        DBELL_UP_ARM            0x00000200
-#define        DBELL_DOWN_HOST         0x00000400
-#define        DBELL_DOWN_ARM          0x00000800
-#define        DBELL_RESET_HOST        0x40000000
-#define        DBELL_RESET_ARM         0x80000000
-
-/* ------------------------------------------------------------- */
-
-#define        DRAM_TIMING_DEF         0x001A01A5
-#define DRAM_AD_SZ_DEF0                0x00000045
-#define DRAM_AD_SZ_NULL                0x00000000
-
-#define SA_CTL_ALLRIGHT                0x64AA0271
-
-#define        INIT_XBUS_CYCLE         0x100016DB
-#define        INIT_XBUS_STROBE        0xF1F1F1F1
-
-/* ------------------------------------------------------------- */
-
-#define        RESET_TIMEOUT           (15 * HZ)       /* 15 sec */
-#define        PEEK_POKE_TIMEOUT       (HZ / 10)       /* 0.1 sec */
-
-/* ------------------------------------------------------------- */
-
-#define c4outmeml(addr, value) writel(value, addr)
-#define c4inmeml(addr) readl(addr)
-#define c4outmemw(addr, value) writew(value, addr)
-#define c4inmemw(addr) readw(addr)
-#define c4outmemb(addr, value) writeb(value, addr)
-#define c4inmemb(addr) readb(addr)
-
-/* ------------------------------------------------------------- */
-
-static inline int wait_for_doorbell(avmcard *card, unsigned long t)
-{
-       unsigned long stop;
-
-       stop = jiffies + t;
-       while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) {
-               if (!time_before(jiffies, stop))
-                       return -1;
-               mb();
-       }
-       return 0;
-}
-
-static int c4_poke(avmcard *card,  unsigned long off, unsigned long value)
-{
-
-       if (wait_for_doorbell(card, HZ / 10) < 0)
-               return -1;
-
-       c4outmeml(card->mbase + MBOX_PEEK_POKE, off);
-       c4outmeml(card->mbase + DOORBELL, DBELL_ADDR);
-
-       if (wait_for_doorbell(card, HZ / 10) < 0)
-               return -1;
-
-       c4outmeml(card->mbase + MBOX_PEEK_POKE, value);
-       c4outmeml(card->mbase + DOORBELL, DBELL_DATA | DBELL_ADDR);
-
-       return 0;
-}
-
-static int c4_peek(avmcard *card,  unsigned long off, unsigned long *valuep)
-{
-       if (wait_for_doorbell(card, HZ / 10) < 0)
-               return -1;
-
-       c4outmeml(card->mbase + MBOX_PEEK_POKE, off);
-       c4outmeml(card->mbase + DOORBELL, DBELL_RNWR | DBELL_ADDR);
-
-       if (wait_for_doorbell(card, HZ / 10) < 0)
-               return -1;
-
-       *valuep = c4inmeml(card->mbase + MBOX_PEEK_POKE);
-
-       return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static int c4_load_t4file(avmcard *card, capiloaddatapart *t4file)
-{
-       u32 val;
-       unsigned char *dp;
-       u_int left;
-       u32 loadoff = 0;
-
-       dp = t4file->data;
-       left = t4file->len;
-       while (left >= sizeof(u32)) {
-               if (t4file->user) {
-                       if (copy_from_user(&val, dp, sizeof(val)))
-                               return -EFAULT;
-               } else {
-                       memcpy(&val, dp, sizeof(val));
-               }
-               if (c4_poke(card, loadoff, val)) {
-                       printk(KERN_ERR "%s: corrupted firmware file ?\n",
-                              card->name);
-                       return -EIO;
-               }
-               left -= sizeof(u32);
-               dp += sizeof(u32);
-               loadoff += sizeof(u32);
-       }
-       if (left) {
-               val = 0;
-               if (t4file->user) {
-                       if (copy_from_user(&val, dp, left))
-                               return -EFAULT;
-               } else {
-                       memcpy(&val, dp, left);
-               }
-               if (c4_poke(card, loadoff, val)) {
-                       printk(KERN_ERR "%s: corrupted firmware file ?\n",
-                              card->name);
-                       return -EIO;
-               }
-       }
-       return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static inline void _put_byte(void **pp, u8 val)
-{
-       u8 *s = *pp;
-       *s++ = val;
-       *pp = s;
-}
-
-static inline void _put_word(void **pp, u32 val)
-{
-       u8 *s = *pp;
-       *s++ = val & 0xff;
-       *s++ = (val >> 8) & 0xff;
-       *s++ = (val >> 16) & 0xff;
-       *s++ = (val >> 24) & 0xff;
-       *pp = s;
-}
-
-static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
-{
-       unsigned i = len;
-       _put_word(pp, i);
-       while (i-- > 0)
-               _put_byte(pp, *dp++);
-}
-
-static inline u8 _get_byte(void **pp)
-{
-       u8 *s = *pp;
-       u8 val;
-       val = *s++;
-       *pp = s;
-       return val;
-}
-
-static inline u32 _get_word(void **pp)
-{
-       u8 *s = *pp;
-       u32 val;
-       val = *s++;
-       val |= (*s++ << 8);
-       val |= (*s++ << 16);
-       val |= (*s++ << 24);
-       *pp = s;
-       return val;
-}
-
-static inline u32 _get_slice(void **pp, unsigned char *dp)
-{
-       unsigned int len, i;
-
-       len = i = _get_word(pp);
-       while (i-- > 0) *dp++ = _get_byte(pp);
-       return len;
-}
-
-/* ------------------------------------------------------------- */
-
-static void c4_reset(avmcard *card)
-{
-       unsigned long stop;
-
-       c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM);
-
-       stop = jiffies + HZ * 10;
-       while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) {
-               if (!time_before(jiffies, stop))
-                       return;
-               c4outmeml(card->mbase + DOORBELL, DBELL_ADDR);
-               mb();
-       }
-
-       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
-       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
-}
-
-/* ------------------------------------------------------------- */
-
-static int c4_detect(avmcard *card)
-{
-       unsigned long stop, dummy;
-
-       c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c);
-       if (c4inmeml(card->mbase + PCI_OUT_INT_MASK) != 0x0c)
-               return  1;
-
-       c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM);
-
-       stop = jiffies + HZ * 10;
-       while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) {
-               if (!time_before(jiffies, stop))
-                       return 2;
-               c4outmeml(card->mbase + DOORBELL, DBELL_ADDR);
-               mb();
-       }
-
-       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
-       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
-
-       c4outmeml(card->mbase + MAILBOX_0, 0x55aa55aa);
-       if (c4inmeml(card->mbase + MAILBOX_0) != 0x55aa55aa) return 3;
-
-       c4outmeml(card->mbase + MAILBOX_0, 0xaa55aa55);
-       if (c4inmeml(card->mbase + MAILBOX_0) != 0xaa55aa55) return 4;
-
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_SA_MASK, 0)) return 5;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_PCI_MASK, 0)) return 6;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + SA_CONTROL, SA_CTL_ALLRIGHT))
-               return 7;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_CYCLE, INIT_XBUS_CYCLE))
-               return 8;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_STROBE, INIT_XBUS_STROBE))
-               return 8;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, 0)) return 9;
-
-       mdelay(1);
-
-       if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10;
-       if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11;
-       if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12;
-       if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13;
-
-       if (c4_poke(card, DC21285_DRAM_A0MR + CAS_OFFSET, 0)) return 14;
-       if (c4_poke(card, DC21285_DRAM_A1MR + CAS_OFFSET, 0)) return 15;
-       if (c4_poke(card, DC21285_DRAM_A2MR + CAS_OFFSET, 0)) return 16;
-       if (c4_poke(card, DC21285_DRAM_A3MR + CAS_OFFSET, 0)) return 17;
-
-       mdelay(1);
-
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, DRAM_TIMING_DEF))
-               return 18;
-
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_0, DRAM_AD_SZ_DEF0))
-               return 19;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_1, DRAM_AD_SZ_NULL))
-               return 20;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_2, DRAM_AD_SZ_NULL))
-               return 21;
-       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_3, DRAM_AD_SZ_NULL))
-               return 22;
-
-       /* Transputer test */
-
-       if (c4_poke(card, 0x000000, 0x11111111)
-           || c4_poke(card, 0x400000, 0x22222222)
-              || c4_poke(card, 0x800000, 0x33333333)
-              || c4_poke(card, 0xC00000, 0x44444444))
-               return 23;
-
-       if (c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111
-           || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222
-              || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333
-              || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444)
-               return 24;
-
-       if (c4_poke(card, 0x000000, 0x55555555)
-           || c4_poke(card, 0x400000, 0x66666666)
-              || c4_poke(card, 0x800000, 0x77777777)
-              || c4_poke(card, 0xC00000, 0x88888888))
-               return 25;
-
-       if (c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555
-           || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666
-              || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777
-              || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888)
-               return 26;
-
-       return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static void c4_dispatch_tx(avmcard *card)
-{
-       avmcard_dmainfo *dma = card->dma;
-       struct sk_buff *skb;
-       u8 cmd, subcmd;
-       u16 len;
-       u32 txlen;
-       void *p;
-
-
-       if (card->csr & DBELL_DOWN_ARM) { /* tx busy */
-               return;
-       }
-
-       skb = skb_dequeue(&dma->send_queue);
-       if (!skb) {
-#ifdef AVM_C4_DEBUG
-               printk(KERN_DEBUG "%s: tx underrun\n", card->name);
-#endif
-               return;
-       }
-
-       len = CAPIMSG_LEN(skb->data);
-
-       if (len) {
-               cmd = CAPIMSG_COMMAND(skb->data);
-               subcmd = CAPIMSG_SUBCOMMAND(skb->data);
-
-               p = dma->sendbuf.dmabuf;
-
-               if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
-                       u16 dlen = CAPIMSG_DATALEN(skb->data);
-                       _put_byte(&p, SEND_DATA_B3_REQ);
-                       _put_slice(&p, skb->data, len);
-                       _put_slice(&p, skb->data + len, dlen);
-               } else {
-                       _put_byte(&p, SEND_MESSAGE);
-                       _put_slice(&p, skb->data, len);
-               }
-               txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
-#ifdef AVM_C4_DEBUG
-               printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen);
-#endif
-       } else {
-               txlen = skb->len - 2;
-#ifdef AVM_C4_POLLDEBUG
-               if (skb->data[2] == SEND_POLLACK)
-                       printk(KERN_INFO "%s: ack to c4\n", card->name);
-#endif
-#ifdef AVM_C4_DEBUG
-               printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n",
-                      card->name, skb->data[2], txlen);
-#endif
-               skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
-                                                skb->len - 2);
-       }
-       txlen = (txlen + 3) & ~3;
-
-       c4outmeml(card->mbase + MBOX_DOWN_ADDR, dma->sendbuf.dmaaddr);
-       c4outmeml(card->mbase + MBOX_DOWN_LEN, txlen);
-
-       card->csr |= DBELL_DOWN_ARM;
-
-       c4outmeml(card->mbase + DOORBELL, DBELL_DOWN_ARM);
-
-       dev_kfree_skb_any(skb);
-}
-
-/* ------------------------------------------------------------- */
-
-static void queue_pollack(avmcard *card)
-{
-       struct sk_buff *skb;
-       void *p;
-
-       skb = alloc_skb(3, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, lost poll ack\n",
-                      card->name);
-               return;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_POLLACK);
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       skb_queue_tail(&card->dma->send_queue, skb);
-       c4_dispatch_tx(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static void c4_handle_rx(avmcard *card)
-{
-       avmcard_dmainfo *dma = card->dma;
-       struct capi_ctr *ctrl;
-       avmctrl_info *cinfo;
-       struct sk_buff *skb;
-       void *p = dma->recvbuf.dmabuf;
-       u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
-       u8 b1cmd =  _get_byte(&p);
-       u32 cidx;
-
-
-#ifdef AVM_C4_DEBUG
-       printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name,
-              b1cmd, (unsigned long)dma->recvlen);
-#endif
-
-       switch (b1cmd) {
-       case RECEIVE_DATA_B3_IND:
-
-               ApplId = (unsigned) _get_word(&p);
-               MsgLen = _get_slice(&p, card->msgbuf);
-               DataB3Len = _get_slice(&p, card->databuf);
-               cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
-               if (cidx >= card->nlogcontr) cidx = 0;
-               ctrl = &card->ctrlinfo[cidx].capi_ctrl;
-
-               if (MsgLen < 30) { /* not CAPI 64Bit */
-                       memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
-                       MsgLen = 30;
-                       CAPIMSG_SETLEN(card->msgbuf, 30);
-               }
-               if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
-                       printk(KERN_ERR "%s: incoming packet dropped\n",
-                              card->name);
-               } else {
-                       skb_put_data(skb, card->msgbuf, MsgLen);
-                       skb_put_data(skb, card->databuf, DataB3Len);
-                       capi_ctr_handle_message(ctrl, ApplId, skb);
-               }
-               break;
-
-       case RECEIVE_MESSAGE:
-
-               ApplId = (unsigned) _get_word(&p);
-               MsgLen = _get_slice(&p, card->msgbuf);
-               cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
-               if (cidx >= card->nlogcontr) cidx = 0;
-               cinfo = &card->ctrlinfo[cidx];
-               ctrl = &card->ctrlinfo[cidx].capi_ctrl;
-
-               if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
-                       printk(KERN_ERR "%s: incoming packet dropped\n",
-                              card->name);
-               } else {
-                       skb_put_data(skb, card->msgbuf, MsgLen);
-                       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
-                               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
-                                                    CAPIMSG_NCCI(skb->data),
-                                                    CAPIMSG_MSGID(skb->data));
-
-                       capi_ctr_handle_message(ctrl, ApplId, skb);
-               }
-               break;
-
-       case RECEIVE_NEW_NCCI:
-
-               ApplId = _get_word(&p);
-               NCCI = _get_word(&p);
-               WindowSize = _get_word(&p);
-               cidx = (NCCI & 0x7f) - card->cardnr;
-               if (cidx >= card->nlogcontr) cidx = 0;
-
-               capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize);
-
-               break;
-
-       case RECEIVE_FREE_NCCI:
-
-               ApplId = _get_word(&p);
-               NCCI = _get_word(&p);
-
-               if (NCCI != 0xffffffff) {
-                       cidx = (NCCI & 0x7f) - card->cardnr;
-                       if (cidx >= card->nlogcontr) cidx = 0;
-                       capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI);
-               }
-               break;
-
-       case RECEIVE_START:
-#ifdef AVM_C4_POLLDEBUG
-               printk(KERN_INFO "%s: poll from c4\n", card->name);
-#endif
-               if (!suppress_pollack)
-                       queue_pollack(card);
-               for (cidx = 0; cidx < card->nr_controllers; cidx++) {
-                       ctrl = &card->ctrlinfo[cidx].capi_ctrl;
-                       capi_ctr_resume_output(ctrl);
-               }
-               break;
-
-       case RECEIVE_STOP:
-               for (cidx = 0; cidx < card->nr_controllers; cidx++) {
-                       ctrl = &card->ctrlinfo[cidx].capi_ctrl;
-                       capi_ctr_suspend_output(ctrl);
-               }
-               break;
-
-       case RECEIVE_INIT:
-
-               cidx = card->nlogcontr;
-               if (cidx >= card->nr_controllers) {
-                       printk(KERN_ERR "%s: card with %d controllers ??\n",
-                              card->name, cidx + 1);
-                       break;
-               }
-               card->nlogcontr++;
-               cinfo = &card->ctrlinfo[cidx];
-               ctrl = &cinfo->capi_ctrl;
-               cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
-               b1_parse_version(cinfo);
-               printk(KERN_INFO "%s: %s-card (%s) now active\n",
-                      card->name,
-                      cinfo->version[VER_CARDTYPE],
-                      cinfo->version[VER_DRIVER]);
-               capi_ctr_ready(&cinfo->capi_ctrl);
-               break;
-
-       case RECEIVE_TASK_READY:
-               ApplId = (unsigned) _get_word(&p);
-               MsgLen = _get_slice(&p, card->msgbuf);
-               card->msgbuf[MsgLen] = 0;
-               while (MsgLen > 0
-                      && (card->msgbuf[MsgLen - 1] == '\n'
-                          || card->msgbuf[MsgLen - 1] == '\r')) {
-                       card->msgbuf[MsgLen - 1] = 0;
-                       MsgLen--;
-               }
-               printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
-                      card->name, ApplId, card->msgbuf);
-               break;
-
-       case RECEIVE_DEBUGMSG:
-               MsgLen = _get_slice(&p, card->msgbuf);
-               card->msgbuf[MsgLen] = 0;
-               while (MsgLen > 0
-                      && (card->msgbuf[MsgLen - 1] == '\n'
-                          || card->msgbuf[MsgLen - 1] == '\r')) {
-                       card->msgbuf[MsgLen - 1] = 0;
-                       MsgLen--;
-               }
-               printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
-               break;
-
-       default:
-               printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n",
-                      card->name, b1cmd);
-               return;
-       }
-}
-
-/* ------------------------------------------------------------- */
-
-static irqreturn_t c4_handle_interrupt(avmcard *card)
-{
-       unsigned long flags;
-       u32 status;
-
-       spin_lock_irqsave(&card->lock, flags);
-       status = c4inmeml(card->mbase + DOORBELL);
-
-       if (status & DBELL_RESET_HOST) {
-               u_int i;
-               c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c);
-               spin_unlock_irqrestore(&card->lock, flags);
-               if (card->nlogcontr == 0)
-                       return IRQ_HANDLED;
-               printk(KERN_ERR "%s: unexpected reset\n", card->name);
-               for (i = 0; i < card->nr_controllers; i++) {
-                       avmctrl_info *cinfo = &card->ctrlinfo[i];
-                       memset(cinfo->version, 0, sizeof(cinfo->version));
-                       spin_lock_irqsave(&card->lock, flags);
-                       capilib_release(&cinfo->ncci_head);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       capi_ctr_down(&cinfo->capi_ctrl);
-               }
-               card->nlogcontr = 0;
-               return IRQ_HANDLED;
-       }
-
-       status &= (DBELL_UP_HOST | DBELL_DOWN_HOST);
-       if (!status) {
-               spin_unlock_irqrestore(&card->lock, flags);
-               return IRQ_HANDLED;
-       }
-       c4outmeml(card->mbase + DOORBELL, status);
-
-       if ((status & DBELL_UP_HOST) != 0) {
-               card->dma->recvlen = c4inmeml(card->mbase + MBOX_UP_LEN);
-               c4outmeml(card->mbase + MBOX_UP_LEN, 0);
-               c4_handle_rx(card);
-               card->dma->recvlen = 0;
-               c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size);
-               c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM);
-       }
-
-       if ((status & DBELL_DOWN_HOST) != 0) {
-               card->csr &= ~DBELL_DOWN_ARM;
-               c4_dispatch_tx(card);
-       } else if (card->csr & DBELL_DOWN_HOST) {
-               if (c4inmeml(card->mbase + MBOX_DOWN_LEN) == 0) {
-                       card->csr &= ~DBELL_DOWN_ARM;
-                       c4_dispatch_tx(card);
-               }
-       }
-       spin_unlock_irqrestore(&card->lock, flags);
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t c4_interrupt(int interrupt, void *devptr)
-{
-       avmcard *card = devptr;
-
-       return c4_handle_interrupt(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static void c4_send_init(avmcard *card)
-{
-       struct sk_buff *skb;
-       void *p;
-       unsigned long flags;
-
-       skb = alloc_skb(15, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, lost register appl.\n",
-                      card->name);
-               return;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_INIT);
-       _put_word(&p, CAPI_MAXAPPL);
-       _put_word(&p, AVM_NCCI_PER_CHANNEL * 30);
-       _put_word(&p, card->cardnr - 1);
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       skb_queue_tail(&card->dma->send_queue, skb);
-       spin_lock_irqsave(&card->lock, flags);
-       c4_dispatch_tx(card);
-       spin_unlock_irqrestore(&card->lock, flags);
-}
-
-static int queue_sendconfigword(avmcard *card, u32 val)
-{
-       struct sk_buff *skb;
-       unsigned long flags;
-       void *p;
-
-       skb = alloc_skb(3 + 4, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, send config\n",
-                      card->name);
-               return -ENOMEM;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_CONFIG);
-       _put_word(&p, val);
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       skb_queue_tail(&card->dma->send_queue, skb);
-       spin_lock_irqsave(&card->lock, flags);
-       c4_dispatch_tx(card);
-       spin_unlock_irqrestore(&card->lock, flags);
-       return 0;
-}
-
-static int queue_sendconfig(avmcard *card, char cval[4])
-{
-       struct sk_buff *skb;
-       unsigned long flags;
-       void *p;
-
-       skb = alloc_skb(3 + 4, GFP_ATOMIC);
-       if (!skb) {
-               printk(KERN_CRIT "%s: no memory, send config\n",
-                      card->name);
-               return -ENOMEM;
-       }
-       p = skb->data;
-       _put_byte(&p, 0);
-       _put_byte(&p, 0);
-       _put_byte(&p, SEND_CONFIG);
-       _put_byte(&p, cval[0]);
-       _put_byte(&p, cval[1]);
-       _put_byte(&p, cval[2]);
-       _put_byte(&p, cval[3]);
-       skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-       skb_queue_tail(&card->dma->send_queue, skb);
-
-       spin_lock_irqsave(&card->lock, flags);
-       c4_dispatch_tx(card);
-       spin_unlock_irqrestore(&card->lock, flags);
-       return 0;
-}
-
-static int c4_send_config(avmcard *card, capiloaddatapart *config)
-{
-       u8 val[4];
-       unsigned char *dp;
-       u_int left;
-       int retval;
-
-       if ((retval = queue_sendconfigword(card, 1)) != 0)
-               return retval;
-       if ((retval = queue_sendconfigword(card, config->len)) != 0)
-               return retval;
-
-       dp = config->data;
-       left = config->len;
-       while (left >= sizeof(u32)) {
-               if (config->user) {
-                       if (copy_from_user(val, dp, sizeof(val)))
-                               return -EFAULT;
-               } else {
-                       memcpy(val, dp, sizeof(val));
-               }
-               if ((retval = queue_sendconfig(card, val)) != 0)
-                       return retval;
-               left -= sizeof(val);
-               dp += sizeof(val);
-       }
-       if (left) {
-               memset(val, 0, sizeof(val));
-               if (config->user) {
-                       if (copy_from_user(&val, dp, left))
-                               return -EFAULT;
-               } else {
-                       memcpy(&val, dp, left);
-               }
-               if ((retval = queue_sendconfig(card, val)) != 0)
-                       return retval;
-       }
-
-       return 0;
-}
-
-static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       int retval;
-
-       if ((retval = c4_load_t4file(card, &data->firmware))) {
-               printk(KERN_ERR "%s: failed to load t4file!!\n",
-                      card->name);
-               c4_reset(card);
-               return retval;
-       }
-
-       card->csr = 0;
-       c4outmeml(card->mbase + MBOX_UP_LEN, 0);
-       c4outmeml(card->mbase + MBOX_DOWN_LEN, 0);
-       c4outmeml(card->mbase + DOORBELL, DBELL_INIT);
-       mdelay(1);
-       c4outmeml(card->mbase + DOORBELL,
-                 DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST);
-
-       c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x08);
-
-       card->dma->recvlen = 0;
-       c4outmeml(card->mbase + MBOX_UP_ADDR, card->dma->recvbuf.dmaaddr);
-       c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size);
-       c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM);
-
-       if (data->configuration.len > 0 && data->configuration.data) {
-               retval = c4_send_config(card, &data->configuration);
-               if (retval) {
-                       printk(KERN_ERR "%s: failed to set config!!\n",
-                              card->name);
-                       c4_reset(card);
-                       return retval;
-               }
-       }
-
-       c4_send_init(card);
-
-       return 0;
-}
-
-
-static void c4_reset_ctr(struct capi_ctr *ctrl)
-{
-       avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card;
-       avmctrl_info *cinfo;
-       u_int i;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-
-       c4_reset(card);
-
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       for (i = 0; i < card->nr_controllers; i++) {
-               cinfo = &card->ctrlinfo[i];
-               memset(cinfo->version, 0, sizeof(cinfo->version));
-               capi_ctr_down(&cinfo->capi_ctrl);
-       }
-       card->nlogcontr = 0;
-}
-
-static void c4_remove(struct pci_dev *pdev)
-{
-       avmcard *card = pci_get_drvdata(pdev);
-       avmctrl_info *cinfo;
-       u_int i;
-
-       if (!card)
-               return;
-
-       c4_reset(card);
-
-       for (i = 0; i < card->nr_controllers; i++) {
-               cinfo = &card->ctrlinfo[i];
-               detach_capi_ctr(&cinfo->capi_ctrl);
-       }
-
-       free_irq(card->irq, card);
-       iounmap(card->mbase);
-       release_region(card->port, AVMB1_PORTLEN);
-       avmcard_dma_free(card->dma);
-       pci_set_drvdata(pdev, NULL);
-       b1_free_card(card);
-}
-
-/* ------------------------------------------------------------- */
-
-
-static void c4_register_appl(struct capi_ctr *ctrl,
-                            u16 appl,
-                            capi_register_params *rp)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       struct sk_buff *skb;
-       int want = rp->level3cnt;
-       unsigned long flags;
-       int nconn;
-       void *p;
-
-       if (ctrl->cnr == card->cardnr) {
-
-               if (want > 0) nconn = want;
-               else nconn = ctrl->profile.nbchannel * 4 * -want;
-               if (nconn == 0) nconn = ctrl->profile.nbchannel * 4;
-
-               skb = alloc_skb(23, GFP_ATOMIC);
-               if (!skb) {
-                       printk(KERN_CRIT "%s: no memory, lost register appl.\n",
-                              card->name);
-                       return;
-               }
-               p = skb->data;
-               _put_byte(&p, 0);
-               _put_byte(&p, 0);
-               _put_byte(&p, SEND_REGISTER);
-               _put_word(&p, appl);
-               _put_word(&p, 1024 * (nconn + 1));
-               _put_word(&p, nconn);
-               _put_word(&p, rp->datablkcnt);
-               _put_word(&p, rp->datablklen);
-               skb_put(skb, (u8 *)p - (u8 *)skb->data);
-
-               skb_queue_tail(&card->dma->send_queue, skb);
-
-               spin_lock_irqsave(&card->lock, flags);
-               c4_dispatch_tx(card);
-               spin_unlock_irqrestore(&card->lock, flags);
-       }
-}
-
-/* ------------------------------------------------------------- */
-
-static void c4_release_appl(struct capi_ctr *ctrl, u16 appl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned long flags;
-       struct sk_buff *skb;
-       void *p;
-
-       spin_lock_irqsave(&card->lock, flags);
-       capilib_release_appl(&cinfo->ncci_head, appl);
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       if (ctrl->cnr == card->cardnr) {
-               skb = alloc_skb(7, GFP_ATOMIC);
-               if (!skb) {
-                       printk(KERN_CRIT "%s: no memory, lost release appl.\n",
-                              card->name);
-                       return;
-               }
-               p = skb->data;
-               _put_byte(&p, 0);
-               _put_byte(&p, 0);
-               _put_byte(&p, SEND_RELEASE);
-               _put_word(&p, appl);
-
-               skb_put(skb, (u8 *)p - (u8 *)skb->data);
-               skb_queue_tail(&card->dma->send_queue, skb);
-               spin_lock_irqsave(&card->lock, flags);
-               c4_dispatch_tx(card);
-               spin_unlock_irqrestore(&card->lock, flags);
-       }
-}
-
-/* ------------------------------------------------------------- */
-
-
-static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       u16 retval = CAPI_NOERROR;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
-               retval = capilib_data_b3_req(&cinfo->ncci_head,
-                                            CAPIMSG_APPID(skb->data),
-                                            CAPIMSG_NCCI(skb->data),
-                                            CAPIMSG_MSGID(skb->data));
-       }
-       if (retval == CAPI_NOERROR) {
-               skb_queue_tail(&card->dma->send_queue, skb);
-               c4_dispatch_tx(card);
-       }
-       spin_unlock_irqrestore(&card->lock, flags);
-       return retval;
-}
-
-/* ------------------------------------------------------------- */
-
-static char *c4_procinfo(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->port : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               cinfo->card ? cinfo->card->membase : 0
-               );
-       return cinfo->infobuf;
-}
-
-static int c4_proc_show(struct seq_file *m, void *v)
-{
-       struct capi_ctr *ctrl = m->private;
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       u8 flag;
-       char *s;
-
-       seq_printf(m, "%-16s %s\n", "name", card->name);
-       seq_printf(m, "%-16s 0x%x\n", "io", card->port);
-       seq_printf(m, "%-16s %d\n", "irq", card->irq);
-       seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase);
-       switch (card->cardtype) {
-       case avm_b1isa: s = "B1 ISA"; break;
-       case avm_b1pci: s = "B1 PCI"; break;
-       case avm_b1pcmcia: s = "B1 PCMCIA"; break;
-       case avm_m1: s = "M1"; break;
-       case avm_m2: s = "M2"; break;
-       case avm_t1isa: s = "T1 ISA (HEMA)"; break;
-       case avm_t1pci: s = "T1 PCI"; break;
-       case avm_c4: s = "C4"; break;
-       case avm_c2: s = "C2"; break;
-       default: s = "???"; break;
-       }
-       seq_printf(m, "%-16s %s\n", "type", s);
-       if ((s = cinfo->version[VER_DRIVER]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_driver", s);
-       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
-       if ((s = cinfo->version[VER_SERIAL]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_serial", s);
-
-       if (card->cardtype != avm_m1) {
-               flag = ((u8 *)(ctrl->profile.manu))[3];
-               if (flag)
-                       seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
-                                  "protocol",
-                                  (flag & 0x01) ? " DSS1" : "",
-                                  (flag & 0x02) ? " CT1" : "",
-                                  (flag & 0x04) ? " VN3" : "",
-                                  (flag & 0x08) ? " NI1" : "",
-                                  (flag & 0x10) ? " AUSTEL" : "",
-                                  (flag & 0x20) ? " ESS" : "",
-                                  (flag & 0x40) ? " 1TR6" : ""
-                               );
-       }
-       if (card->cardtype != avm_m1) {
-               flag = ((u8 *)(ctrl->profile.manu))[5];
-               if (flag)
-                       seq_printf(m, "%-16s%s%s%s%s\n",
-                                  "linetype",
-                                  (flag & 0x01) ? " point to point" : "",
-                                  (flag & 0x02) ? " point to multipoint" : "",
-                                  (flag & 0x08) ? " leased line without D-channel" : "",
-                                  (flag & 0x04) ? " leased line with D-channel" : ""
-                               );
-       }
-       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
-
-       return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static int c4_add_card(struct capicardparams *p, struct pci_dev *dev,
-                      int nr_controllers)
-{
-       avmcard *card;
-       avmctrl_info *cinfo;
-       int retval;
-       int i;
-
-       card = b1_alloc_card(nr_controllers);
-       if (!card) {
-               printk(KERN_WARNING "c4: no memory.\n");
-               retval = -ENOMEM;
-               goto err;
-       }
-       card->dma = avmcard_dma_alloc("c4", dev, 2048 + 128, 2048 + 128);
-       if (!card->dma) {
-               printk(KERN_WARNING "c4: no memory.\n");
-               retval = -ENOMEM;
-               goto err_free;
-       }
-
-       sprintf(card->name, "c%d-%x", nr_controllers, p->port);
-       card->port = p->port;
-       card->irq = p->irq;
-       card->membase = p->membase;
-       card->cardtype = (nr_controllers == 4) ? avm_c4 : avm_c2;
-
-       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
-               printk(KERN_WARNING "c4: ports 0x%03x-0x%03x in use.\n",
-                      card->port, card->port + AVMB1_PORTLEN);
-               retval = -EBUSY;
-               goto err_free_dma;
-       }
-
-       card->mbase = ioremap(card->membase, 128);
-       if (card->mbase == NULL) {
-               printk(KERN_NOTICE "c4: can't remap memory at 0x%lx\n",
-                      card->membase);
-               retval = -EIO;
-               goto err_release_region;
-       }
-
-       retval = c4_detect(card);
-       if (retval != 0) {
-               printk(KERN_NOTICE "c4: NO card at 0x%x error(%d)\n",
-                      card->port, retval);
-               retval = -EIO;
-               goto err_unmap;
-       }
-       c4_reset(card);
-
-       retval = request_irq(card->irq, c4_interrupt, IRQF_SHARED, card->name, card);
-       if (retval) {
-               printk(KERN_ERR "c4: unable to get IRQ %d.\n", card->irq);
-               retval = -EBUSY;
-               goto err_unmap;
-       }
-
-       for (i = 0; i < nr_controllers; i++) {
-               cinfo = &card->ctrlinfo[i];
-               cinfo->capi_ctrl.owner = THIS_MODULE;
-               cinfo->capi_ctrl.driver_name   = "c4";
-               cinfo->capi_ctrl.driverdata    = cinfo;
-               cinfo->capi_ctrl.register_appl = c4_register_appl;
-               cinfo->capi_ctrl.release_appl  = c4_release_appl;
-               cinfo->capi_ctrl.send_message  = c4_send_message;
-               cinfo->capi_ctrl.load_firmware = c4_load_firmware;
-               cinfo->capi_ctrl.reset_ctr     = c4_reset_ctr;
-               cinfo->capi_ctrl.procinfo      = c4_procinfo;
-               cinfo->capi_ctrl.proc_show     = c4_proc_show;
-               strcpy(cinfo->capi_ctrl.name, card->name);
-
-               retval = attach_capi_ctr(&cinfo->capi_ctrl);
-               if (retval) {
-                       printk(KERN_ERR "c4: attach controller failed (%d).\n", i);
-                       for (i--; i >= 0; i--) {
-                               cinfo = &card->ctrlinfo[i];
-                               detach_capi_ctr(&cinfo->capi_ctrl);
-                       }
-                       goto err_free_irq;
-               }
-               if (i == 0)
-                       card->cardnr = cinfo->capi_ctrl.cnr;
-       }
-
-       printk(KERN_INFO "c4: AVM C%d at i/o %#x, irq %d, mem %#lx\n",
-              nr_controllers, card->port, card->irq,
-              card->membase);
-       pci_set_drvdata(dev, card);
-       return 0;
-
-err_free_irq:
-       free_irq(card->irq, card);
-err_unmap:
-       iounmap(card->mbase);
-err_release_region:
-       release_region(card->port, AVMB1_PORTLEN);
-err_free_dma:
-       avmcard_dma_free(card->dma);
-err_free:
-       b1_free_card(card);
-err:
-       return retval;
-}
-
-/* ------------------------------------------------------------- */
-
-static int c4_probe(struct pci_dev *dev, const struct pci_device_id *ent)
-{
-       int nr = ent->driver_data;
-       int retval = 0;
-       struct capicardparams param;
-
-       if (pci_enable_device(dev) < 0) {
-               printk(KERN_ERR "c4: failed to enable AVM-C%d\n", nr);
-               return -ENODEV;
-       }
-       pci_set_master(dev);
-
-       param.port = pci_resource_start(dev, 1);
-       param.irq = dev->irq;
-       param.membase = pci_resource_start(dev, 0);
-
-       printk(KERN_INFO "c4: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n",
-              nr, param.port, param.irq, param.membase);
-
-       retval = c4_add_card(&param, dev, nr);
-       if (retval != 0) {
-               printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n",
-                      nr, param.port, param.irq, param.membase);
-               pci_disable_device(dev);
-               return -ENODEV;
-       }
-       return 0;
-}
-
-static struct pci_driver c4_pci_driver = {
-       .name           = "c4",
-       .id_table       = c4_pci_tbl,
-       .probe          = c4_probe,
-       .remove         = c4_remove,
-};
-
-static struct capi_driver capi_driver_c2 = {
-       .name           = "c2",
-       .revision       = "1.0",
-};
-
-static struct capi_driver capi_driver_c4 = {
-       .name           = "c4",
-       .revision       = "1.0",
-};
-
-static int __init c4_init(void)
-{
-       char *p;
-       char rev[32];
-       int err;
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, 32);
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-       err = pci_register_driver(&c4_pci_driver);
-       if (!err) {
-               strlcpy(capi_driver_c2.revision, rev, 32);
-               register_capi_driver(&capi_driver_c2);
-               strlcpy(capi_driver_c4.revision, rev, 32);
-               register_capi_driver(&capi_driver_c4);
-               printk(KERN_INFO "c4: revision %s\n", rev);
-       }
-       return err;
-}
-
-static void __exit c4_exit(void)
-{
-       unregister_capi_driver(&capi_driver_c2);
-       unregister_capi_driver(&capi_driver_c4);
-       pci_unregister_driver(&c4_pci_driver);
-}
-
-module_init(c4_init);
-module_exit(c4_exit);
diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c
deleted file mode 100644 (file)
index 2153619..0000000
+++ /dev/null
@@ -1,594 +0,0 @@
-/* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
- *
- * Module for AVM T1 HEMA-card.
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/capi.h>
-#include <linux/netdevice.h>
-#include <linux/kernelcapi.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/gfp.h>
-#include <asm/io.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-
-/* ------------------------------------------------------------- */
-
-static char *revision = "$Revision: 1.1.2.3 $";
-
-/* ------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------- */
-
-static int hema_irq_table[16] =
-{0,
- 0,
- 0,
- 0x80,                         /* irq 3 */
- 0,
- 0x90,                         /* irq 5 */
- 0,
- 0xA0,                         /* irq 7 */
- 0,
- 0xB0,                         /* irq 9 */
- 0xC0,                         /* irq 10 */
- 0xD0,                         /* irq 11 */
- 0xE0,                         /* irq 12 */
- 0,
- 0,
- 0xF0,                         /* irq 15 */
-};
-
-static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr)
-{
-       unsigned char cregs[8];
-       unsigned char reverse_cardnr;
-       unsigned char dummy;
-       int i;
-
-       reverse_cardnr =   ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
-               | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
-       cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
-       cregs[1] = 0x00; /* fast & slow link connected to CON1 */
-       cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
-       cregs[3] = 0;
-       cregs[4] = 0x11; /* zero wait state */
-       cregs[5] = hema_irq_table[irq & 0xf];
-       cregs[6] = 0;
-       cregs[7] = 0;
-
-       /*
-        * no one else should use the ISA bus in this moment,
-        * but no function there to prevent this :-(
-        * save_flags(flags); cli();
-        */
-
-       /* board reset */
-       t1outp(base, T1_RESETBOARD, 0xf);
-       mdelay(100);
-       dummy = t1inp(base, T1_FASTLINK + T1_OUTSTAT); /* first read */
-
-       /* write config */
-       dummy = (base >> 4) & 0xff;
-       for (i = 1; i <= 0xf; i++) t1outp(base, i, dummy);
-       t1outp(base, HEMA_PAL_ID & 0xf, dummy);
-       t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
-       for (i = 1; i < 7; i++) t1outp(base, 0, cregs[i]);
-       t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
-       /* restore_flags(flags); */
-
-       mdelay(100);
-       t1outp(base, T1_FASTLINK + T1_RESETLINK, 0);
-       t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0);
-       mdelay(10);
-       t1outp(base, T1_FASTLINK + T1_RESETLINK, 1);
-       t1outp(base, T1_SLOWLINK + T1_RESETLINK, 1);
-       mdelay(100);
-       t1outp(base, T1_FASTLINK + T1_RESETLINK, 0);
-       t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0);
-       mdelay(10);
-       t1outp(base, T1_FASTLINK + T1_ANALYSE, 0);
-       mdelay(5);
-       t1outp(base, T1_SLOWLINK + T1_ANALYSE, 0);
-
-       if (t1inp(base, T1_FASTLINK + T1_OUTSTAT) != 0x1) /* tx empty */
-               return 1;
-       if (t1inp(base, T1_FASTLINK + T1_INSTAT) != 0x0) /* rx empty */
-               return 2;
-       if (t1inp(base, T1_FASTLINK + T1_IRQENABLE) != 0x0)
-               return 3;
-       if ((t1inp(base, T1_FASTLINK + T1_FIFOSTAT) & 0xf0) != 0x70)
-               return 4;
-       if ((t1inp(base, T1_FASTLINK + T1_IRQMASTER) & 0x0e) != 0)
-               return 5;
-       if ((t1inp(base, T1_FASTLINK + T1_IDENT) & 0x7d) != 1)
-               return 6;
-       if (t1inp(base, T1_SLOWLINK + T1_OUTSTAT) != 0x1) /* tx empty */
-               return 7;
-       if ((t1inp(base, T1_SLOWLINK + T1_IRQMASTER) & 0x0e) != 0)
-               return 8;
-       if ((t1inp(base, T1_SLOWLINK + T1_IDENT) & 0x7d) != 0)
-               return 9;
-       return 0;
-}
-
-static irqreturn_t t1isa_interrupt(int interrupt, void *devptr)
-{
-       avmcard *card = devptr;
-       avmctrl_info *cinfo = &card->ctrlinfo[0];
-       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
-       unsigned char b1cmd;
-       struct sk_buff *skb;
-
-       unsigned ApplId;
-       unsigned MsgLen;
-       unsigned DataB3Len;
-       unsigned NCCI;
-       unsigned WindowSize;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->lock, flags);
-
-       while (b1_rx_full(card->port)) {
-
-               b1cmd = b1_get_byte(card->port);
-
-               switch (b1cmd) {
-
-               case RECEIVE_DATA_B3_IND:
-
-                       ApplId = (unsigned) b1_get_word(card->port);
-                       MsgLen = t1_get_slice(card->port, card->msgbuf);
-                       DataB3Len = t1_get_slice(card->port, card->databuf);
-                       spin_unlock_irqrestore(&card->lock, flags);
-
-                       if (MsgLen < 30) { /* not CAPI 64Bit */
-                               memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
-                               MsgLen = 30;
-                               CAPIMSG_SETLEN(card->msgbuf, 30);
-                       }
-                       if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
-                               printk(KERN_ERR "%s: incoming packet dropped\n",
-                                      card->name);
-                       } else {
-                               skb_put_data(skb, card->msgbuf, MsgLen);
-                               skb_put_data(skb, card->databuf, DataB3Len);
-                               capi_ctr_handle_message(ctrl, ApplId, skb);
-                       }
-                       break;
-
-               case RECEIVE_MESSAGE:
-
-                       ApplId = (unsigned) b1_get_word(card->port);
-                       MsgLen = t1_get_slice(card->port, card->msgbuf);
-                       if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
-                               spin_unlock_irqrestore(&card->lock, flags);
-                               printk(KERN_ERR "%s: incoming packet dropped\n",
-                                      card->name);
-                       } else {
-                               skb_put_data(skb, card->msgbuf, MsgLen);
-                               if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3)
-                                       capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
-                                                            CAPIMSG_NCCI(skb->data),
-                                                            CAPIMSG_MSGID(skb->data));
-                               spin_unlock_irqrestore(&card->lock, flags);
-                               capi_ctr_handle_message(ctrl, ApplId, skb);
-                       }
-                       break;
-
-               case RECEIVE_NEW_NCCI:
-
-                       ApplId = b1_get_word(card->port);
-                       NCCI = b1_get_word(card->port);
-                       WindowSize = b1_get_word(card->port);
-                       capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       break;
-
-               case RECEIVE_FREE_NCCI:
-
-                       ApplId = b1_get_word(card->port);
-                       NCCI = b1_get_word(card->port);
-                       if (NCCI != 0xffffffff)
-                               capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       break;
-
-               case RECEIVE_START:
-                       b1_put_byte(card->port, SEND_POLLACK);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       capi_ctr_resume_output(ctrl);
-                       break;
-
-               case RECEIVE_STOP:
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       capi_ctr_suspend_output(ctrl);
-                       break;
-
-               case RECEIVE_INIT:
-
-                       cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       b1_parse_version(cinfo);
-                       printk(KERN_INFO "%s: %s-card (%s) now active\n",
-                              card->name,
-                              cinfo->version[VER_CARDTYPE],
-                              cinfo->version[VER_DRIVER]);
-                       capi_ctr_ready(ctrl);
-                       break;
-
-               case RECEIVE_TASK_READY:
-                       ApplId = (unsigned) b1_get_word(card->port);
-                       MsgLen = t1_get_slice(card->port, card->msgbuf);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       card->msgbuf[MsgLen] = 0;
-                       while (MsgLen > 0
-                              && (card->msgbuf[MsgLen - 1] == '\n'
-                                  || card->msgbuf[MsgLen - 1] == '\r')) {
-                               card->msgbuf[MsgLen - 1] = 0;
-                               MsgLen--;
-                       }
-                       printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
-                              card->name, ApplId, card->msgbuf);
-                       break;
-
-               case RECEIVE_DEBUGMSG:
-                       MsgLen = t1_get_slice(card->port, card->msgbuf);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       card->msgbuf[MsgLen] = 0;
-                       while (MsgLen > 0
-                              && (card->msgbuf[MsgLen - 1] == '\n'
-                                  || card->msgbuf[MsgLen - 1] == '\r')) {
-                               card->msgbuf[MsgLen - 1] = 0;
-                               MsgLen--;
-                       }
-                       printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
-                       break;
-
-
-               case 0xff:
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       printk(KERN_ERR "%s: card reseted ?\n", card->name);
-                       return IRQ_HANDLED;
-               default:
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
-                              card->name, b1cmd);
-                       return IRQ_NONE;
-               }
-       }
-       return IRQ_HANDLED;
-}
-
-/* ------------------------------------------------------------- */
-
-static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-       int retval;
-
-       t1_disable_irq(port);
-       b1_reset(port);
-
-       if ((retval = b1_load_t4file(card, &data->firmware))) {
-               b1_reset(port);
-               printk(KERN_ERR "%s: failed to load t4file!!\n",
-                      card->name);
-               return retval;
-       }
-
-       if (data->configuration.len > 0 && data->configuration.data) {
-               if ((retval = b1_load_config(card, &data->configuration))) {
-                       b1_reset(port);
-                       printk(KERN_ERR "%s: failed to load config!!\n",
-                              card->name);
-                       return retval;
-               }
-       }
-
-       if (!b1_loaded(card)) {
-               printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
-               return -EIO;
-       }
-
-       spin_lock_irqsave(&card->lock, flags);
-       b1_setinterrupt(port, card->irq, card->cardtype);
-       b1_put_byte(port, SEND_INIT);
-       b1_put_word(port, CAPI_MAXAPPL);
-       b1_put_word(port, AVM_NCCI_PER_CHANNEL * 30);
-       b1_put_word(port, ctrl->cnr - 1);
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       return 0;
-}
-
-static void t1isa_reset_ctr(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-
-       t1_disable_irq(port);
-       b1_reset(port);
-       b1_reset(port);
-
-       memset(cinfo->version, 0, sizeof(cinfo->version));
-       spin_lock_irqsave(&card->lock, flags);
-       capilib_release(&cinfo->ncci_head);
-       spin_unlock_irqrestore(&card->lock, flags);
-       capi_ctr_down(ctrl);
-}
-
-static void t1isa_remove(struct pci_dev *pdev)
-{
-       avmctrl_info *cinfo = pci_get_drvdata(pdev);
-       avmcard *card;
-
-       if (!cinfo)
-               return;
-
-       card = cinfo->card;
-
-       t1_disable_irq(card->port);
-       b1_reset(card->port);
-       b1_reset(card->port);
-       t1_reset(card->port);
-
-       detach_capi_ctr(&cinfo->capi_ctrl);
-       free_irq(card->irq, card);
-       release_region(card->port, AVMB1_PORTLEN);
-       b1_free_card(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
-static char *t1isa_procinfo(struct capi_ctr *ctrl);
-
-static int t1isa_probe(struct pci_dev *pdev, int cardnr)
-{
-       avmctrl_info *cinfo;
-       avmcard *card;
-       int retval;
-
-       card = b1_alloc_card(1);
-       if (!card) {
-               printk(KERN_WARNING "t1isa: no memory.\n");
-               retval = -ENOMEM;
-               goto err;
-       }
-
-       cinfo = card->ctrlinfo;
-       card->port = pci_resource_start(pdev, 0);
-       card->irq = pdev->irq;
-       card->cardtype = avm_t1isa;
-       card->cardnr = cardnr;
-       sprintf(card->name, "t1isa-%x", card->port);
-
-       if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) {
-               printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port);
-               retval = -EINVAL;
-               goto err_free;
-       }
-       if (hema_irq_table[card->irq & 0xf] == 0) {
-               printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq);
-               retval = -EINVAL;
-               goto err_free;
-       }
-       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
-               printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n",
-                      card->port, card->port + AVMB1_PORTLEN);
-               retval = -EBUSY;
-               goto err_free;
-       }
-       retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card);
-       if (retval) {
-               printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq);
-               retval = -EBUSY;
-               goto err_release_region;
-       }
-
-       if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) {
-               printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n",
-                      card->port, retval);
-               retval = -ENODEV;
-               goto err_free_irq;
-       }
-       t1_disable_irq(card->port);
-       b1_reset(card->port);
-
-       cinfo->capi_ctrl.owner = THIS_MODULE;
-       cinfo->capi_ctrl.driver_name   = "t1isa";
-       cinfo->capi_ctrl.driverdata    = cinfo;
-       cinfo->capi_ctrl.register_appl = b1_register_appl;
-       cinfo->capi_ctrl.release_appl  = b1_release_appl;
-       cinfo->capi_ctrl.send_message  = t1isa_send_message;
-       cinfo->capi_ctrl.load_firmware = t1isa_load_firmware;
-       cinfo->capi_ctrl.reset_ctr     = t1isa_reset_ctr;
-       cinfo->capi_ctrl.procinfo      = t1isa_procinfo;
-       cinfo->capi_ctrl.proc_show     = b1_proc_show;
-       strcpy(cinfo->capi_ctrl.name, card->name);
-
-       retval = attach_capi_ctr(&cinfo->capi_ctrl);
-       if (retval) {
-               printk(KERN_INFO "t1isa: attach controller failed.\n");
-               goto err_free_irq;
-       }
-
-       printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
-              card->port, card->irq, card->cardnr);
-
-       pci_set_drvdata(pdev, cinfo);
-       return 0;
-
-err_free_irq:
-       free_irq(card->irq, card);
-err_release_region:
-       release_region(card->port, AVMB1_PORTLEN);
-err_free:
-       b1_free_card(card);
-err:
-       return retval;
-}
-
-static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-       avmcard *card = cinfo->card;
-       unsigned int port = card->port;
-       unsigned long flags;
-       u16 len = CAPIMSG_LEN(skb->data);
-       u8 cmd = CAPIMSG_COMMAND(skb->data);
-       u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
-       u16 dlen, retval;
-
-       spin_lock_irqsave(&card->lock, flags);
-       if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
-               retval = capilib_data_b3_req(&cinfo->ncci_head,
-                                            CAPIMSG_APPID(skb->data),
-                                            CAPIMSG_NCCI(skb->data),
-                                            CAPIMSG_MSGID(skb->data));
-               if (retval != CAPI_NOERROR) {
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       return retval;
-               }
-               dlen = CAPIMSG_DATALEN(skb->data);
-
-               b1_put_byte(port, SEND_DATA_B3_REQ);
-               t1_put_slice(port, skb->data, len);
-               t1_put_slice(port, skb->data + len, dlen);
-       } else {
-               b1_put_byte(port, SEND_MESSAGE);
-               t1_put_slice(port, skb->data, len);
-       }
-       spin_unlock_irqrestore(&card->lock, flags);
-       dev_kfree_skb_any(skb);
-       return CAPI_NOERROR;
-}
-/* ------------------------------------------------------------- */
-
-static char *t1isa_procinfo(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d %d",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->port : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               cinfo->card ? cinfo->card->cardnr : 0
-               );
-       return cinfo->infobuf;
-}
-
-
-/* ------------------------------------------------------------- */
-
-#define MAX_CARDS 4
-static struct pci_dev isa_dev[MAX_CARDS];
-static int io[MAX_CARDS];
-static int irq[MAX_CARDS];
-static int cardnr[MAX_CARDS];
-
-module_param_hw_array(io, int, ioport, NULL, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param_array(cardnr, int, NULL, 0);
-MODULE_PARM_DESC(io, "I/O base address(es)");
-MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
-MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)");
-
-static int t1isa_add_card(struct capi_driver *driver, capicardparams *data)
-{
-       int i;
-
-       for (i = 0; i < MAX_CARDS; i++) {
-               if (isa_dev[i].resource[0].start)
-                       continue;
-
-               isa_dev[i].resource[0].start = data->port;
-               isa_dev[i].irq = data->irq;
-
-               if (t1isa_probe(&isa_dev[i], data->cardnr) == 0)
-                       return 0;
-       }
-       return -ENODEV;
-}
-
-static struct capi_driver capi_driver_t1isa = {
-       .name           = "t1isa",
-       .revision       = "1.0",
-       .add_card       = t1isa_add_card,
-};
-
-static int __init t1isa_init(void)
-{
-       char rev[32];
-       char *p;
-       int i;
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, 32);
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-       for (i = 0; i < MAX_CARDS; i++) {
-               if (!io[i])
-                       break;
-
-               isa_dev[i].resource[0].start = io[i];
-               isa_dev[i].irq = irq[i];
-
-               if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0)
-                       return -ENODEV;
-       }
-
-       strlcpy(capi_driver_t1isa.revision, rev, 32);
-       register_capi_driver(&capi_driver_t1isa);
-       printk(KERN_INFO "t1isa: revision %s\n", rev);
-
-       return 0;
-}
-
-static void __exit t1isa_exit(void)
-{
-       int i;
-
-       unregister_capi_driver(&capi_driver_t1isa);
-       for (i = 0; i < MAX_CARDS; i++) {
-               if (!io[i])
-                       break;
-
-               t1isa_remove(&isa_dev[i]);
-       }
-}
-
-module_init(t1isa_init);
-module_exit(t1isa_exit);
diff --git a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c
deleted file mode 100644 (file)
index f5ed1d5..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/* $Id: t1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
- *
- * Module for AVM T1 PCI-card.
- *
- * Copyright 1999 by Carsten Paeth <calle@calle.de>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/capi.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-#include "avmcard.h"
-
-#undef CONFIG_T1PCI_DEBUG
-#undef CONFIG_T1PCI_POLLDEBUG
-
-/* ------------------------------------------------------------- */
-static char *revision = "$Revision: 1.1.2.2 $";
-/* ------------------------------------------------------------- */
-
-static struct pci_device_id t1pci_pci_tbl[] = {
-       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, PCI_ANY_ID, PCI_ANY_ID },
-       { }                             /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(pci, t1pci_pci_tbl);
-MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 PCI card");
-MODULE_AUTHOR("Carsten Paeth");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------- */
-
-static char *t1pci_procinfo(struct capi_ctr *ctrl);
-
-static int t1pci_add_card(struct capicardparams *p, struct pci_dev *pdev)
-{
-       avmcard *card;
-       avmctrl_info *cinfo;
-       int retval;
-
-       card = b1_alloc_card(1);
-       if (!card) {
-               printk(KERN_WARNING "t1pci: no memory.\n");
-               retval = -ENOMEM;
-               goto err;
-       }
-
-       card->dma = avmcard_dma_alloc("t1pci", pdev, 2048 + 128, 2048 + 128);
-       if (!card->dma) {
-               printk(KERN_WARNING "t1pci: no memory.\n");
-               retval = -ENOMEM;
-               goto err_free;
-       }
-
-       cinfo = card->ctrlinfo;
-       sprintf(card->name, "t1pci-%x", p->port);
-       card->port = p->port;
-       card->irq = p->irq;
-       card->membase = p->membase;
-       card->cardtype = avm_t1pci;
-
-       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
-               printk(KERN_WARNING "t1pci: ports 0x%03x-0x%03x in use.\n",
-                      card->port, card->port + AVMB1_PORTLEN);
-               retval = -EBUSY;
-               goto err_free_dma;
-       }
-
-       card->mbase = ioremap(card->membase, 64);
-       if (!card->mbase) {
-               printk(KERN_NOTICE "t1pci: can't remap memory at 0x%lx\n",
-                      card->membase);
-               retval = -EIO;
-               goto err_release_region;
-       }
-
-       b1dma_reset(card);
-
-       retval = t1pci_detect(card);
-       if (retval != 0) {
-               if (retval < 6)
-                       printk(KERN_NOTICE "t1pci: NO card at 0x%x (%d)\n",
-                              card->port, retval);
-               else
-                       printk(KERN_NOTICE "t1pci: card at 0x%x, but cable not connected or T1 has no power (%d)\n",
-                              card->port, retval);
-               retval = -EIO;
-               goto err_unmap;
-       }
-       b1dma_reset(card);
-
-       retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
-       if (retval) {
-               printk(KERN_ERR "t1pci: unable to get IRQ %d.\n", card->irq);
-               retval = -EBUSY;
-               goto err_unmap;
-       }
-
-       cinfo->capi_ctrl.owner         = THIS_MODULE;
-       cinfo->capi_ctrl.driver_name   = "t1pci";
-       cinfo->capi_ctrl.driverdata    = cinfo;
-       cinfo->capi_ctrl.register_appl = b1dma_register_appl;
-       cinfo->capi_ctrl.release_appl  = b1dma_release_appl;
-       cinfo->capi_ctrl.send_message  = b1dma_send_message;
-       cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
-       cinfo->capi_ctrl.reset_ctr     = b1dma_reset_ctr;
-       cinfo->capi_ctrl.procinfo      = t1pci_procinfo;
-       cinfo->capi_ctrl.proc_show     = b1dma_proc_show;
-       strcpy(cinfo->capi_ctrl.name, card->name);
-
-       retval = attach_capi_ctr(&cinfo->capi_ctrl);
-       if (retval) {
-               printk(KERN_ERR "t1pci: attach controller failed.\n");
-               retval = -EBUSY;
-               goto err_free_irq;
-       }
-       card->cardnr = cinfo->capi_ctrl.cnr;
-
-       printk(KERN_INFO "t1pci: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n",
-              card->port, card->irq, card->membase);
-
-       pci_set_drvdata(pdev, card);
-       return 0;
-
-err_free_irq:
-       free_irq(card->irq, card);
-err_unmap:
-       iounmap(card->mbase);
-err_release_region:
-       release_region(card->port, AVMB1_PORTLEN);
-err_free_dma:
-       avmcard_dma_free(card->dma);
-err_free:
-       b1_free_card(card);
-err:
-       return retval;
-}
-
-/* ------------------------------------------------------------- */
-
-static void t1pci_remove(struct pci_dev *pdev)
-{
-       avmcard *card = pci_get_drvdata(pdev);
-       avmctrl_info *cinfo = card->ctrlinfo;
-
-       b1dma_reset(card);
-
-       detach_capi_ctr(&cinfo->capi_ctrl);
-       free_irq(card->irq, card);
-       iounmap(card->mbase);
-       release_region(card->port, AVMB1_PORTLEN);
-       avmcard_dma_free(card->dma);
-       b1_free_card(card);
-}
-
-/* ------------------------------------------------------------- */
-
-static char *t1pci_procinfo(struct capi_ctr *ctrl)
-{
-       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->port : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               cinfo->card ? cinfo->card->membase : 0
-               );
-       return cinfo->infobuf;
-}
-
-/* ------------------------------------------------------------- */
-
-static int t1pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
-{
-       struct capicardparams param;
-       int retval;
-
-       if (pci_enable_device(dev) < 0) {
-               printk(KERN_ERR "t1pci: failed to enable AVM-T1-PCI\n");
-               return -ENODEV;
-       }
-       pci_set_master(dev);
-
-       param.port = pci_resource_start(dev, 1);
-       param.irq = dev->irq;
-       param.membase = pci_resource_start(dev, 0);
-
-       printk(KERN_INFO "t1pci: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n",
-              param.port, param.irq, param.membase);
-
-       retval = t1pci_add_card(&param, dev);
-       if (retval != 0) {
-               printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n",
-                      param.port, param.irq, param.membase);
-               pci_disable_device(dev);
-               return -ENODEV;
-       }
-       return 0;
-}
-
-static struct pci_driver t1pci_pci_driver = {
-       .name           = "t1pci",
-       .id_table       = t1pci_pci_tbl,
-       .probe          = t1pci_probe,
-       .remove         = t1pci_remove,
-};
-
-static struct capi_driver capi_driver_t1pci = {
-       .name           = "t1pci",
-       .revision       = "1.0",
-};
-
-static int __init t1pci_init(void)
-{
-       char *p;
-       char rev[32];
-       int err;
-
-       if ((p = strchr(revision, ':')) != NULL && p[1]) {
-               strlcpy(rev, p + 2, 32);
-               if ((p = strchr(rev, '$')) != NULL && p > rev)
-                       *(p - 1) = 0;
-       } else
-               strcpy(rev, "1.0");
-
-       err = pci_register_driver(&t1pci_pci_driver);
-       if (!err) {
-               strlcpy(capi_driver_t1pci.revision, rev, 32);
-               register_capi_driver(&capi_driver_t1pci);
-               printk(KERN_INFO "t1pci: revision %s\n", rev);
-       }
-       return err;
-}
-
-static void __exit t1pci_exit(void)
-{
-       unregister_capi_driver(&capi_driver_t1pci);
-       pci_unregister_driver(&t1pci_pci_driver);
-}
-
-module_init(t1pci_init);
-module_exit(t1pci_exit);
diff --git a/drivers/isdn/hysdn/Kconfig b/drivers/isdn/hysdn/Kconfig
deleted file mode 100644 (file)
index 1971ef8..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-config HYSDN
-       tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
-       depends on m && PROC_FS && PCI
-       help
-         Say Y here if you have one of Hypercope's active PCI ISDN cards
-         Champ, Ergo and Metro. You will then get a module called hysdn.
-         Please read the file <file:Documentation/isdn/README.hysdn> for more
-         information.
-
-config HYSDN_CAPI
-       bool "HYSDN CAPI 2.0 support"
-       depends on HYSDN && ISDN_CAPI
-       help
-         Say Y here if you like to use Hypercope's CAPI 2.0 interface.
diff --git a/drivers/isdn/hysdn/Makefile b/drivers/isdn/hysdn/Makefile
deleted file mode 100644 (file)
index e01f17f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-# Makefile for the hysdn ISDN device driver
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_HYSDN)            += hysdn.o
-
-# Multipart objects.
-
-hysdn-y                                := hysdn_procconf.o hysdn_proclog.o boardergo.o \
-                                  hysdn_boot.o hysdn_sched.o hysdn_net.o hysdn_init.o
-hysdn-$(CONFIG_HYSDN_CAPI)     += hycapi.o
diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c
deleted file mode 100644 (file)
index 2aa2a0e..0000000
+++ /dev/null
@@ -1,445 +0,0 @@
-/* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 kai Exp $
- *
- * Linux driver for HYSDN cards, specific routines for ergo type boards.
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
- * DPRAM interface and layout with only minor differences all related
- * stuff is done here, not in separate modules.
- *
- */
-
-#include <linux/signal.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/vmalloc.h>
-#include <linux/delay.h>
-#include <asm/io.h>
-
-#include "hysdn_defs.h"
-#include "boardergo.h"
-
-#define byteout(addr, val) outb(val, addr)
-#define bytein(addr) inb(addr)
-
-/***************************************************/
-/* The cards interrupt handler. Called from system */
-/***************************************************/
-static irqreturn_t
-ergo_interrupt(int intno, void *dev_id)
-{
-       hysdn_card *card = dev_id;      /* parameter from irq */
-       tErgDpram *dpr;
-       unsigned long flags;
-       unsigned char volatile b;
-
-       if (!card)
-               return IRQ_NONE;                /* error -> spurious interrupt */
-       if (!card->irq_enabled)
-               return IRQ_NONE;                /* other device interrupting or irq switched off */
-
-       spin_lock_irqsave(&card->hysdn_lock, flags); /* no further irqs allowed */
-
-       if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
-               spin_unlock_irqrestore(&card->hysdn_lock, flags);       /* restore old state */
-               return IRQ_NONE;                /* no interrupt requested by E1 */
-       }
-       /* clear any pending ints on the board */
-       dpr = card->dpram;
-       b = dpr->ToPcInt;       /* clear for ergo */
-       b |= dpr->ToPcIntMetro; /* same for metro */
-       b |= dpr->ToHyInt;      /* and for champ */
-
-       /* start kernel task immediately after leaving all interrupts */
-       if (!card->hw_lock)
-               schedule_work(&card->irq_queue);
-       spin_unlock_irqrestore(&card->hysdn_lock, flags);
-       return IRQ_HANDLED;
-}                              /* ergo_interrupt */
-
-/******************************************************************************/
-/* ergo_irq_bh will be called as part of the kernel clearing its shared work  */
-/* queue sometime after a call to schedule_work has been made passing our     */
-/* work_struct. This task is the only one handling data transfer from or to   */
-/* the card after booting. The task may be queued from everywhere             */
-/* (interrupts included).                                                     */
-/******************************************************************************/
-static void
-ergo_irq_bh(struct work_struct *ugli_api)
-{
-       hysdn_card *card = container_of(ugli_api, hysdn_card, irq_queue);
-       tErgDpram *dpr;
-       int again;
-       unsigned long flags;
-
-       if (card->state != CARD_STATE_RUN)
-               return;         /* invalid call */
-
-       dpr = card->dpram;      /* point to DPRAM */
-
-       spin_lock_irqsave(&card->hysdn_lock, flags);
-       if (card->hw_lock) {
-               spin_unlock_irqrestore(&card->hysdn_lock, flags);       /* hardware currently unavailable */
-               return;
-       }
-       card->hw_lock = 1;      /* we now lock the hardware */
-
-       do {
-               again = 0;      /* assume loop not to be repeated */
-
-               if (!dpr->ToHyFlag) {
-                       /* we are able to send a buffer */
-
-                       if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
-                                          ERG_TO_HY_BUF_SIZE)) {
-                               dpr->ToHyFlag = 1;      /* enable tx */
-                               again = 1;      /* restart loop */
-                       }
-               }               /* we are able to send a buffer */
-               if (dpr->ToPcFlag) {
-                       /* a message has arrived for us, handle it */
-
-                       if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
-                               dpr->ToPcFlag = 0;      /* we worked the data */
-                               again = 1;      /* restart loop */
-                       }
-               }               /* a message has arrived for us */
-               if (again) {
-                       dpr->ToHyInt = 1;
-                       dpr->ToPcInt = 1;       /* interrupt to E1 for all cards */
-               } else
-                       card->hw_lock = 0;      /* free hardware again */
-       } while (again);        /* until nothing more to do */
-
-       spin_unlock_irqrestore(&card->hysdn_lock, flags);
-}                              /* ergo_irq_bh */
-
-
-/*********************************************************/
-/* stop the card (hardware reset) and disable interrupts */
-/*********************************************************/
-static void
-ergo_stopcard(hysdn_card *card)
-{
-       unsigned long flags;
-       unsigned char val;
-
-       hysdn_net_release(card);        /* first release the net device if existing */
-#ifdef CONFIG_HYSDN_CAPI
-       hycapi_capi_stop(card);
-#endif /* CONFIG_HYSDN_CAPI */
-       spin_lock_irqsave(&card->hysdn_lock, flags);
-       val = bytein(card->iobase + PCI9050_INTR_REG);  /* get actual value */
-       val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1);        /* mask irq */
-       byteout(card->iobase + PCI9050_INTR_REG, val);
-       card->irq_enabled = 0;
-       byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET);      /* reset E1 processor */
-       card->state = CARD_STATE_UNUSED;
-       card->err_log_state = ERRLOG_STATE_OFF;         /* currently no log active */
-
-       spin_unlock_irqrestore(&card->hysdn_lock, flags);
-}                              /* ergo_stopcard */
-
-/**************************************************************************/
-/* enable or disable the cards error log. The event is queued if possible */
-/**************************************************************************/
-static void
-ergo_set_errlog_state(hysdn_card *card, int on)
-{
-       unsigned long flags;
-
-       if (card->state != CARD_STATE_RUN) {
-               card->err_log_state = ERRLOG_STATE_OFF;         /* must be off */
-               return;
-       }
-       spin_lock_irqsave(&card->hysdn_lock, flags);
-
-       if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
-           ((card->err_log_state == ERRLOG_STATE_ON) && on)) {
-               spin_unlock_irqrestore(&card->hysdn_lock, flags);
-               return;         /* nothing to do */
-       }
-       if (on)
-               card->err_log_state = ERRLOG_STATE_START;       /* request start */
-       else
-               card->err_log_state = ERRLOG_STATE_STOP;        /* request stop */
-
-       spin_unlock_irqrestore(&card->hysdn_lock, flags);
-       schedule_work(&card->irq_queue);
-}                              /* ergo_set_errlog_state */
-
-/******************************************/
-/* test the cards RAM and return 0 if ok. */
-/******************************************/
-static const char TestText[36] = "This Message is filler, why read it";
-
-static int
-ergo_testram(hysdn_card *card)
-{
-       tErgDpram *dpr = card->dpram;
-
-       memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable));      /* clear all Traps */
-       dpr->ToHyInt = 1;       /* E1 INTR state forced */
-
-       memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
-              sizeof(TestText));
-       if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
-                  sizeof(TestText)))
-               return (-1);
-
-       memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
-              sizeof(TestText));
-       if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
-                  sizeof(TestText)))
-               return (-1);
-
-       return (0);
-}                              /* ergo_testram */
-
-/*****************************************************************************/
-/* this function is intended to write stage 1 boot image to the cards buffer */
-/* this is done in two steps. First the 1024 hi-words are written (offs=0),  */
-/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the   */
-/* PCI-write-buffers flushed and the card is taken out of reset.             */
-/* The function then waits for a reaction of the E1 processor or a timeout.  */
-/* Negative return values are interpreted as errors.                         */
-/*****************************************************************************/
-static int
-ergo_writebootimg(struct HYSDN_CARD *card, unsigned char *buf,
-                 unsigned long offs)
-{
-       unsigned char *dst;
-       tErgDpram *dpram;
-       int cnt = (BOOT_IMG_SIZE >> 2);         /* number of words to move and swap (byte order!) */
-
-       if (card->debug_flags & LOG_POF_CARD)
-               hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
-
-       dst = card->dpram;      /* pointer to start of DPRAM */
-       dst += (offs + ERG_DPRAM_FILL_SIZE);    /* offset in the DPRAM */
-       while (cnt--) {
-               *dst++ = *(buf + 1);    /* high byte */
-               *dst++ = *buf;  /* low byte */
-               dst += 2;       /* point to next longword */
-               buf += 2;       /* buffer only filled with words */
-       }
-
-       /* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
-       /* flush the PCI-write-buffer and take the E1 out of reset */
-       if (offs) {
-               memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE);    /* fill the DPRAM still not cleared */
-               dpram = card->dpram;    /* get pointer to dpram structure */
-               dpram->ToHyNoDpramErrLog = 0xFF;        /* write a dpram register */
-               while (!dpram->ToHyNoDpramErrLog);      /* reread volatile register to flush PCI */
-
-               byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN);        /* start E1 processor */
-               /* the interrupts are still masked */
-
-               msleep_interruptible(20);               /* Timeout 20ms */
-
-               if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
-                       if (card->debug_flags & LOG_POF_CARD)
-                               hysdn_addlog(card, "ERGO: write bootldr no answer");
-                       return (-ERR_BOOTIMG_FAIL);
-               }
-       }                       /* start_boot_img */
-       return (0);             /* successful */
-}                              /* ergo_writebootimg */
-
-/********************************************************************************/
-/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
-/* using the boot spool mechanism. If everything works fine 0 is returned. In   */
-/* case of errors a negative error value is returned.                           */
-/********************************************************************************/
-static int
-ergo_writebootseq(struct HYSDN_CARD *card, unsigned char *buf, int len)
-{
-       tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
-       unsigned char *dst;
-       unsigned char buflen;
-       int nr_write;
-       unsigned char tmp_rdptr;
-       unsigned char wr_mirror;
-       int i;
-
-       if (card->debug_flags & LOG_POF_CARD)
-               hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
-
-       dst = sp->Data;         /* point to data in spool structure */
-       buflen = sp->Len;       /* maximum len of spooled data */
-       wr_mirror = sp->WrPtr;  /* only once read */
-
-       /* try until all bytes written or error */
-       i = 0x1000;             /* timeout value */
-       while (len) {
-
-               /* first determine the number of bytes that may be buffered */
-               do {
-                       tmp_rdptr = sp->RdPtr;  /* first read the pointer */
-                       i--;    /* decrement timeout */
-               } while (i && (tmp_rdptr != sp->RdPtr));        /* wait for stable pointer */
-
-               if (!i) {
-                       if (card->debug_flags & LOG_POF_CARD)
-                               hysdn_addlog(card, "ERGO: write boot seq timeout");
-                       return (-ERR_BOOTSEQ_FAIL);     /* value not stable -> timeout */
-               }
-               if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
-                       nr_write += buflen;     /* now we got number of free bytes - 1 in buffer */
-
-               if (!nr_write)
-                       continue;       /* no free bytes in buffer */
-
-               if (nr_write > len)
-                       nr_write = len;         /* limit if last few bytes */
-               i = 0x1000;     /* reset timeout value */
-
-               /* now we know how much bytes we may put in the puffer */
-               len -= nr_write;        /* we savely could adjust len before output */
-               while (nr_write--) {
-                       *(dst + wr_mirror) = *buf++;    /* output one byte */
-                       if (++wr_mirror >= buflen)
-                               wr_mirror = 0;
-                       sp->WrPtr = wr_mirror;  /* announce the next byte to E1 */
-               }               /* while (nr_write) */
-
-       }                       /* while (len) */
-       return (0);
-}                              /* ergo_writebootseq */
-
-/***********************************************************************************/
-/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
-/* boot process. If the process has been successful 0 is returned otherwise a     */
-/* negative error code is returned.                                                */
-/***********************************************************************************/
-static int
-ergo_waitpofready(struct HYSDN_CARD *card)
-{
-       tErgDpram *dpr = card->dpram;   /* pointer to DPRAM structure */
-       int timecnt = 10000 / 50;       /* timeout is 10 secs max. */
-       unsigned long flags;
-       int msg_size;
-       int i;
-
-       if (card->debug_flags & LOG_POF_CARD)
-               hysdn_addlog(card, "ERGO: waiting for pof ready");
-       while (timecnt--) {
-               /* wait until timeout  */
-
-               if (dpr->ToPcFlag) {
-                       /* data has arrived */
-
-                       if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
-                           (dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
-                           (dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
-                           ((*(unsigned long *) dpr->ToPcBuf) != RDY_MAGIC))
-                               break;  /* an error occurred */
-
-                       /* Check for additional data delivered during SysReady */
-                       msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
-                       if (msg_size > 0)
-                               if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
-                                       break;
-
-                       if (card->debug_flags & LOG_POF_RECORD)
-                               hysdn_addlog(card, "ERGO: pof boot success");
-                       spin_lock_irqsave(&card->hysdn_lock, flags);
-
-                       card->state = CARD_STATE_RUN;   /* now card is running */
-                       /* enable the cards interrupt */
-                       byteout(card->iobase + PCI9050_INTR_REG,
-                               bytein(card->iobase + PCI9050_INTR_REG) |
-                               (PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
-                       card->irq_enabled = 1;  /* we are ready to receive interrupts */
-
-                       dpr->ToPcFlag = 0;      /* reset data indicator */
-                       dpr->ToHyInt = 1;
-                       dpr->ToPcInt = 1;       /* interrupt to E1 for all cards */
-
-                       spin_unlock_irqrestore(&card->hysdn_lock, flags);
-                       if ((hynet_enable & (1 << card->myid))
-                           && (i = hysdn_net_create(card)))
-                       {
-                               ergo_stopcard(card);
-                               card->state = CARD_STATE_BOOTERR;
-                               return (i);
-                       }
-#ifdef CONFIG_HYSDN_CAPI
-                       if ((i = hycapi_capi_create(card))) {
-                               printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n");
-                       }
-#endif /* CONFIG_HYSDN_CAPI */
-                       return (0);     /* success */
-               }               /* data has arrived */
-               msleep_interruptible(50);               /* Timeout 50ms */
-       }                       /* wait until timeout */
-
-       if (card->debug_flags & LOG_POF_CARD)
-               hysdn_addlog(card, "ERGO: pof boot ready timeout");
-       return (-ERR_POF_TIMEOUT);
-}                              /* ergo_waitpofready */
-
-
-
-/************************************************************************************/
-/* release the cards hardware. Before releasing do a interrupt disable and hardware */
-/* reset. Also unmap dpram.                                                         */
-/* Use only during module release.                                                  */
-/************************************************************************************/
-static void
-ergo_releasehardware(hysdn_card *card)
-{
-       ergo_stopcard(card);    /* first stop the card if not already done */
-       free_irq(card->irq, card);      /* release interrupt */
-       release_region(card->iobase + PCI9050_INTR_REG, 1);     /* release all io ports */
-       release_region(card->iobase + PCI9050_USER_IO, 1);
-       iounmap(card->dpram);
-       card->dpram = NULL;     /* release shared mem */
-}                              /* ergo_releasehardware */
-
-
-/*********************************************************************************/
-/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
-/* value is returned.                                                            */
-/* Use only during module init.                                                  */
-/*********************************************************************************/
-int
-ergo_inithardware(hysdn_card *card)
-{
-       if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN"))
-               return (-1);
-       if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) {
-               release_region(card->iobase + PCI9050_INTR_REG, 1);
-               return (-1);    /* ports already in use */
-       }
-       card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
-       if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) {
-               release_region(card->iobase + PCI9050_INTR_REG, 1);
-               release_region(card->iobase + PCI9050_USER_IO, 1);
-               return (-1);
-       }
-
-       ergo_stopcard(card);    /* disable interrupts */
-       if (request_irq(card->irq, ergo_interrupt, IRQF_SHARED, "HYSDN", card)) {
-               ergo_releasehardware(card); /* return the acquired hardware */
-               return (-1);
-       }
-       /* success, now setup the function pointers */
-       card->stopcard = ergo_stopcard;
-       card->releasehardware = ergo_releasehardware;
-       card->testram = ergo_testram;
-       card->writebootimg = ergo_writebootimg;
-       card->writebootseq = ergo_writebootseq;
-       card->waitpofready = ergo_waitpofready;
-       card->set_errlog_state = ergo_set_errlog_state;
-       INIT_WORK(&card->irq_queue, ergo_irq_bh);
-       spin_lock_init(&card->hysdn_lock);
-
-       return (0);
-}                              /* ergo_inithardware */
diff --git a/drivers/isdn/hysdn/boardergo.h b/drivers/isdn/hysdn/boardergo.h
deleted file mode 100644 (file)
index e99bd81..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/* $Id: boardergo.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-
-/************************************************/
-/* defines for the dual port memory of the card */
-/************************************************/
-#define ERG_DPRAM_PAGE_SIZE 0x2000     /* DPRAM occupies a 8K page */
-#define BOOT_IMG_SIZE 4096
-#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
-
-#define ERG_TO_HY_BUF_SIZE  0x0E00     /* 3072 bytes buffer size to card */
-#define ERG_TO_PC_BUF_SIZE  0x0E00     /* 3072 bytes to PC, too */
-
-/* following DPRAM layout copied from OS2-driver boarderg.h */
-typedef struct ErgDpram_tag {
-       /*0000 */ unsigned char ToHyBuf[ERG_TO_HY_BUF_SIZE];
-       /*0E00 */ unsigned char ToPcBuf[ERG_TO_PC_BUF_SIZE];
-
-       /*1C00 */ unsigned char bSoftUart[SIZE_RSV_SOFT_UART];
-       /* size 0x1B0 */
-
-       /*1DB0 *//* tErrLogEntry */ unsigned char volatile ErrLogMsg[64];
-       /* size 64 bytes */
-       /*1DB0  unsigned long ulErrType;               */
-       /*1DB4  unsigned long ulErrSubtype;            */
-       /*1DB8  unsigned long ucTextSize;              */
-       /*1DB9  unsigned long ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
-       /*1DF0 */
-
-       /*1DF0 */ unsigned short volatile ToHyChannel;
-       /*1DF2 */ unsigned short volatile ToHySize;
-       /*1DF4 */ unsigned char volatile ToHyFlag;
-       /* !=0: msg for Hy waiting */
-       /*1DF5 */ unsigned char volatile ToPcFlag;
-       /* !=0: msg for PC waiting */
-       /*1DF6 */ unsigned short volatile ToPcChannel;
-       /*1DF8 */ unsigned short volatile ToPcSize;
-       /*1DFA */ unsigned char bRes1DBA[0x1E00 - 0x1DFA];
-       /* 6 bytes */
-
-       /*1E00 */ unsigned char bRestOfEntryTbl[0x1F00 - 0x1E00];
-       /*1F00 */ unsigned long TrapTable[62];
-       /*1FF8 */ unsigned char bRes1FF8[0x1FFB - 0x1FF8];
-       /* low part of reset vetor */
-       /*1FFB */ unsigned char ToPcIntMetro;
-       /* notes:
-        * - metro has 32-bit boot ram - accessing
-        *   ToPcInt and ToHyInt would be the same;
-        *   so we moved ToPcInt to 1FFB.
-        *   Because on the PC side both vars are
-        *   readonly (reseting on int from E1 to PC),
-        *   we can read both vars on both cards
-        *   without destroying anything.
-        * - 1FFB is the high byte of the reset vector,
-        *   so E1 side should NOT change this byte
-        *   when writing!
-        */
-       /*1FFC */ unsigned char volatile ToHyNoDpramErrLog;
-       /* note: ToHyNoDpramErrLog is used to inform
-        *       boot loader, not to use DPRAM based
-        *       ErrLog; when DOS driver is rewritten
-        *       this becomes obsolete
-        */
-       /*1FFD */ unsigned char bRes1FFD;
-       /*1FFE */ unsigned char ToPcInt;
-       /* E1_intclear; on CHAMP2: E1_intset   */
-       /*1FFF */ unsigned char ToHyInt;
-       /* E1_intset;   on CHAMP2: E1_intclear */
-} tErgDpram;
-
-/**********************************************/
-/* PCI9050 controller local register offsets: */
-/* copied from boarderg.c                     */
-/**********************************************/
-#define PCI9050_INTR_REG    0x4C       /* Interrupt register */
-#define PCI9050_USER_IO     0x51       /* User I/O  register */
-
-/* bitmask for PCI9050_INTR_REG: */
-#define PCI9050_INTR_REG_EN1    0x01   /* 1= enable (def.), 0= disable */
-#define PCI9050_INTR_REG_POL1   0x02   /* 1= active high (def.), 0= active low */
-#define PCI9050_INTR_REG_STAT1  0x04   /* 1= intr. active, 0= intr. not active (def.) */
-#define PCI9050_INTR_REG_ENPCI  0x40   /* 1= PCI interrupts enable (def.) */
-
-/* bitmask for PCI9050_USER_IO: */
-#define PCI9050_USER_IO_EN3     0x02   /* 1= disable      , 0= enable (def.) */
-#define PCI9050_USER_IO_DIR3    0x04   /* 1= output (def.), 0= input         */
-#define PCI9050_USER_IO_DAT3    0x08   /* 1= high (def.)  , 0= low           */
-
-#define PCI9050_E1_RESET    (PCI9050_USER_IO_DIR3)             /* 0x04 */
-#define PCI9050_E1_RUN      (PCI9050_USER_IO_DAT3 | PCI9050_USER_IO_DIR3)              /* 0x0C */
diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c
deleted file mode 100644 (file)
index a2c15cd..0000000
+++ /dev/null
@@ -1,785 +0,0 @@
-/* $Id: hycapi.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards, CAPI2.0-Interface.
- *
- * Author    Ulrich Albrecht <u.albrecht@hypercope.de> for Hypercope GmbH
- * Copyright 2000 by Hypercope GmbH
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/signal.h>
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/slab.h>
-
-#define        VER_DRIVER      0
-#define        VER_CARDTYPE    1
-#define        VER_HWID        2
-#define        VER_SERIAL      3
-#define        VER_OPTION      4
-#define        VER_PROTO       5
-#define        VER_PROFILE     6
-#define        VER_CAPI        7
-
-#include "hysdn_defs.h"
-#include <linux/kernelcapi.h>
-
-static char hycapi_revision[] = "$Revision: 1.8.6.4 $";
-
-unsigned int hycapi_enable = 0xffffffff;
-module_param(hycapi_enable, uint, 0);
-
-typedef struct _hycapi_appl {
-       unsigned int ctrl_mask;
-       capi_register_params rp;
-       struct sk_buff *listen_req[CAPI_MAXCONTR];
-} hycapi_appl;
-
-static hycapi_appl hycapi_applications[CAPI_MAXAPPL];
-
-static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
-
-static inline int _hycapi_appCheck(int app_id, int ctrl_no)
-{
-       if ((ctrl_no <= 0) || (ctrl_no > CAPI_MAXCONTR) || (app_id <= 0) ||
-          (app_id > CAPI_MAXAPPL))
-       {
-               printk(KERN_ERR "HYCAPI: Invalid request app_id %d for controller %d", app_id, ctrl_no);
-               return -1;
-       }
-       return ((hycapi_applications[app_id - 1].ctrl_mask & (1 << (ctrl_no-1))) != 0);
-}
-
-/******************************
-Kernel-Capi callback reset_ctr
-******************************/
-
-static void
-hycapi_reset_ctr(struct capi_ctr *ctrl)
-{
-       hycapictrl_info *cinfo = ctrl->driverdata;
-
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "HYCAPI hycapi_reset_ctr\n");
-#endif
-       capilib_release(&cinfo->ncci_head);
-       capi_ctr_down(ctrl);
-}
-
-/******************************
-Kernel-Capi callback remove_ctr
-******************************/
-
-static void
-hycapi_remove_ctr(struct capi_ctr *ctrl)
-{
-       int i;
-       hycapictrl_info *cinfo = NULL;
-       hysdn_card *card = NULL;
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "HYCAPI hycapi_remove_ctr\n");
-#endif
-       cinfo = (hycapictrl_info *)(ctrl->driverdata);
-       if (!cinfo) {
-               printk(KERN_ERR "No hycapictrl_info set!");
-               return;
-       }
-       card = cinfo->card;
-       capi_ctr_suspend_output(ctrl);
-       for (i = 0; i < CAPI_MAXAPPL; i++) {
-               if (hycapi_applications[i].listen_req[ctrl->cnr - 1]) {
-                       kfree_skb(hycapi_applications[i].listen_req[ctrl->cnr - 1]);
-                       hycapi_applications[i].listen_req[ctrl->cnr - 1] = NULL;
-               }
-       }
-       detach_capi_ctr(ctrl);
-       ctrl->driverdata = NULL;
-       kfree(card->hyctrlinfo);
-
-
-       card->hyctrlinfo = NULL;
-}
-
-/***********************************************************
-
-Queue a CAPI-message to the controller.
-
-***********************************************************/
-
-static void
-hycapi_sendmsg_internal(struct capi_ctr *ctrl, struct sk_buff *skb)
-{
-       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
-       hysdn_card *card = cinfo->card;
-
-       spin_lock_irq(&cinfo->lock);
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_send_message\n");
-#endif
-       cinfo->skbs[cinfo->in_idx++] = skb;     /* add to buffer list */
-       if (cinfo->in_idx >= HYSDN_MAX_CAPI_SKB)
-               cinfo->in_idx = 0;      /* wrap around */
-       cinfo->sk_count++;              /* adjust counter */
-       if (cinfo->sk_count >= HYSDN_MAX_CAPI_SKB) {
-               /* inform upper layers we're full */
-               printk(KERN_ERR "HYSDN Card%d: CAPI-buffer overrun!\n",
-                      card->myid);
-               capi_ctr_suspend_output(ctrl);
-       }
-       cinfo->tx_skb = skb;
-       spin_unlock_irq(&cinfo->lock);
-       schedule_work(&card->irq_queue);
-}
-
-/***********************************************************
-hycapi_register_internal
-
-Send down the CAPI_REGISTER-Command to the controller.
-This functions will also be used if the adapter has been rebooted to
-re-register any applications in the private list.
-
-************************************************************/
-
-static void
-hycapi_register_internal(struct capi_ctr *ctrl, __u16 appl,
-                        capi_register_params *rp)
-{
-       char ExtFeatureDefaults[] = "49  /0/0/0/0,*/1,*/2,*/3,*/4,*/5,*/6,*/7,*/8,*/9,*";
-       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
-       hysdn_card *card = cinfo->card;
-       struct sk_buff *skb;
-       __u16 len;
-       __u8 _command = 0xa0, _subcommand = 0x80;
-       __u16 MessageNumber = 0x0000;
-       __u16 MessageBufferSize = 0;
-       int slen = strlen(ExtFeatureDefaults);
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_register_appl\n");
-#endif
-       MessageBufferSize = rp->level3cnt * rp->datablkcnt * rp->datablklen;
-
-       len = CAPI_MSG_BASELEN + 8 + slen + 1;
-       if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
-               printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
-                      card->myid);
-               return;
-       }
-       skb_put_data(skb, &len, sizeof(__u16));
-       skb_put_data(skb, &appl, sizeof(__u16));
-       skb_put_data(skb, &_command, sizeof(__u8));
-       skb_put_data(skb, &_subcommand, sizeof(__u8));
-       skb_put_data(skb, &MessageNumber, sizeof(__u16));
-       skb_put_data(skb, &MessageBufferSize, sizeof(__u16));
-       skb_put_data(skb, &(rp->level3cnt), sizeof(__u16));
-       skb_put_data(skb, &(rp->datablkcnt), sizeof(__u16));
-       skb_put_data(skb, &(rp->datablklen), sizeof(__u16));
-       skb_put_data(skb, ExtFeatureDefaults, slen);
-       hycapi_applications[appl - 1].ctrl_mask |= (1 << (ctrl->cnr - 1));
-       hycapi_send_message(ctrl, skb);
-}
-
-/************************************************************
-hycapi_restart_internal
-
-After an adapter has been rebootet, re-register all applications and
-send a LISTEN_REQ (if there has been such a thing )
-
-*************************************************************/
-
-static void hycapi_restart_internal(struct capi_ctr *ctrl)
-{
-       int i;
-       struct sk_buff *skb;
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_WARNING "HYSDN: hycapi_restart_internal");
-#endif
-       for (i = 0; i < CAPI_MAXAPPL; i++) {
-               if (_hycapi_appCheck(i + 1, ctrl->cnr) == 1) {
-                       hycapi_register_internal(ctrl, i + 1,
-                                                &hycapi_applications[i].rp);
-                       if (hycapi_applications[i].listen_req[ctrl->cnr - 1]) {
-                               skb = skb_copy(hycapi_applications[i].listen_req[ctrl->cnr - 1], GFP_ATOMIC);
-                               hycapi_sendmsg_internal(ctrl, skb);
-                       }
-               }
-       }
-}
-
-/*************************************************************
-Register an application.
-Error-checking is done for CAPI-compliance.
-
-The application is recorded in the internal list.
-*************************************************************/
-
-static void
-hycapi_register_appl(struct capi_ctr *ctrl, __u16 appl,
-                    capi_register_params *rp)
-{
-       int MaxLogicalConnections = 0, MaxBDataBlocks = 0, MaxBDataLen = 0;
-       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
-       hysdn_card *card = cinfo->card;
-       int chk = _hycapi_appCheck(appl, ctrl->cnr);
-       if (chk < 0) {
-               return;
-       }
-       if (chk == 1) {
-               printk(KERN_INFO "HYSDN: apl %d already registered\n", appl);
-               return;
-       }
-       MaxBDataBlocks = rp->datablkcnt > CAPI_MAXDATAWINDOW ? CAPI_MAXDATAWINDOW : rp->datablkcnt;
-       rp->datablkcnt = MaxBDataBlocks;
-       MaxBDataLen = rp->datablklen < 1024 ? 1024 : rp->datablklen;
-       rp->datablklen = MaxBDataLen;
-
-       MaxLogicalConnections = rp->level3cnt;
-       if (MaxLogicalConnections < 0) {
-               MaxLogicalConnections = card->bchans * -MaxLogicalConnections;
-       }
-       if (MaxLogicalConnections == 0) {
-               MaxLogicalConnections = card->bchans;
-       }
-
-       rp->level3cnt = MaxLogicalConnections;
-       memcpy(&hycapi_applications[appl - 1].rp,
-              rp, sizeof(capi_register_params));
-}
-
-/*********************************************************************
-
-hycapi_release_internal
-
-Send down a CAPI_RELEASE to the controller.
-*********************************************************************/
-
-static void hycapi_release_internal(struct capi_ctr *ctrl, __u16 appl)
-{
-       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
-       hysdn_card *card = cinfo->card;
-       struct sk_buff *skb;
-       __u16 len;
-       __u8 _command = 0xa1, _subcommand = 0x80;
-       __u16 MessageNumber = 0x0000;
-
-       capilib_release_appl(&cinfo->ncci_head, appl);
-
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_release_appl\n");
-#endif
-       len = CAPI_MSG_BASELEN;
-       if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
-               printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
-                      card->myid);
-               return;
-       }
-       skb_put_data(skb, &len, sizeof(__u16));
-       skb_put_data(skb, &appl, sizeof(__u16));
-       skb_put_data(skb, &_command, sizeof(__u8));
-       skb_put_data(skb, &_subcommand, sizeof(__u8));
-       skb_put_data(skb, &MessageNumber, sizeof(__u16));
-       hycapi_send_message(ctrl, skb);
-       hycapi_applications[appl - 1].ctrl_mask &= ~(1 << (ctrl->cnr - 1));
-}
-
-/******************************************************************
-hycapi_release_appl
-
-Release the application from the internal list an remove it's
-registration at controller-level
-******************************************************************/
-
-static void
-hycapi_release_appl(struct capi_ctr *ctrl, __u16 appl)
-{
-       int chk;
-
-       chk = _hycapi_appCheck(appl, ctrl->cnr);
-       if (chk < 0) {
-               printk(KERN_ERR "HYCAPI: Releasing invalid appl %d on controller %d\n", appl, ctrl->cnr);
-               return;
-       }
-       if (hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1]) {
-               kfree_skb(hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1]);
-               hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1] = NULL;
-       }
-       if (chk == 1)
-       {
-               hycapi_release_internal(ctrl, appl);
-       }
-}
-
-
-/**************************************************************
-Kill a single controller.
-**************************************************************/
-
-int hycapi_capi_release(hysdn_card *card)
-{
-       hycapictrl_info *cinfo = card->hyctrlinfo;
-       struct capi_ctr *ctrl;
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_capi_release\n");
-#endif
-       if (cinfo) {
-               ctrl = &cinfo->capi_ctrl;
-               hycapi_remove_ctr(ctrl);
-       }
-       return 0;
-}
-
-/**************************************************************
-hycapi_capi_stop
-
-Stop CAPI-Output on a card. (e.g. during reboot)
-***************************************************************/
-
-int hycapi_capi_stop(hysdn_card *card)
-{
-       hycapictrl_info *cinfo = card->hyctrlinfo;
-       struct capi_ctr *ctrl;
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_capi_stop\n");
-#endif
-       if (cinfo) {
-               ctrl = &cinfo->capi_ctrl;
-/*             ctrl->suspend_output(ctrl); */
-               capi_ctr_down(ctrl);
-       }
-       return 0;
-}
-
-/***************************************************************
-hycapi_send_message
-
-Send a message to the controller.
-
-Messages are parsed for their Command/Subcommand-type, and appropriate
-action's are performed.
-
-Note that we have to muck around with a 64Bit-DATA_REQ as there are
-firmware-releases that do not check the MsgLen-Indication!
-
-***************************************************************/
-
-static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
-{
-       __u16 appl_id;
-       int _len, _len2;
-       __u8 msghead[64];
-       hycapictrl_info *cinfo = ctrl->driverdata;
-       u16 retval = CAPI_NOERROR;
-
-       appl_id = CAPIMSG_APPID(skb->data);
-       switch (_hycapi_appCheck(appl_id, ctrl->cnr))
-       {
-       case 0:
-/*                     printk(KERN_INFO "Need to register\n"); */
-               hycapi_register_internal(ctrl,
-                                        appl_id,
-                                        &(hycapi_applications[appl_id - 1].rp));
-               break;
-       case 1:
-               break;
-       default:
-               printk(KERN_ERR "HYCAPI: Controller mixup!\n");
-               retval = CAPI_ILLAPPNR;
-               goto out;
-       }
-       switch (CAPIMSG_CMD(skb->data)) {
-       case CAPI_DISCONNECT_B3_RESP:
-               capilib_free_ncci(&cinfo->ncci_head, appl_id,
-                                 CAPIMSG_NCCI(skb->data));
-               break;
-       case CAPI_DATA_B3_REQ:
-               _len = CAPIMSG_LEN(skb->data);
-               if (_len > 22) {
-                       _len2 = _len - 22;
-                       skb_copy_from_linear_data(skb, msghead, 22);
-                       skb_copy_to_linear_data_offset(skb, _len2,
-                                                      msghead, 22);
-                       skb_pull(skb, _len2);
-                       CAPIMSG_SETLEN(skb->data, 22);
-                       retval = capilib_data_b3_req(&cinfo->ncci_head,
-                                                    CAPIMSG_APPID(skb->data),
-                                                    CAPIMSG_NCCI(skb->data),
-                                                    CAPIMSG_MSGID(skb->data));
-               }
-               break;
-       case CAPI_LISTEN_REQ:
-               if (hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1])
-               {
-                       kfree_skb(hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1]);
-                       hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1] = NULL;
-               }
-               if (!(hycapi_applications[appl_id  -1].listen_req[ctrl->cnr - 1] = skb_copy(skb, GFP_ATOMIC)))
-               {
-                       printk(KERN_ERR "HYSDN: memory squeeze in private_listen\n");
-               }
-               break;
-       default:
-               break;
-       }
-out:
-       if (retval == CAPI_NOERROR)
-               hycapi_sendmsg_internal(ctrl, skb);
-       else
-               dev_kfree_skb_any(skb);
-
-       return retval;
-}
-
-static int hycapi_proc_show(struct seq_file *m, void *v)
-{
-       struct capi_ctr *ctrl = m->private;
-       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
-       hysdn_card *card = cinfo->card;
-       char *s;
-
-       seq_printf(m, "%-16s %s\n", "name", cinfo->cardname);
-       seq_printf(m, "%-16s 0x%x\n", "io", card->iobase);
-       seq_printf(m, "%-16s %d\n", "irq", card->irq);
-
-       switch (card->brdtype) {
-       case BD_PCCARD:  s = "HYSDN Hycard"; break;
-       case BD_ERGO: s = "HYSDN Ergo2"; break;
-       case BD_METRO: s = "HYSDN Metro4"; break;
-       case BD_CHAMP2: s = "HYSDN Champ2";     break;
-       case BD_PLEXUS: s = "HYSDN Plexus30"; break;
-       default: s = "???"; break;
-       }
-       seq_printf(m, "%-16s %s\n", "type", s);
-       if ((s = cinfo->version[VER_DRIVER]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_driver", s);
-       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
-       if ((s = cinfo->version[VER_SERIAL]) != NULL)
-               seq_printf(m, "%-16s %s\n", "ver_serial", s);
-
-       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
-
-       return 0;
-}
-
-/**************************************************************
-hycapi_load_firmware
-
-This does NOT load any firmware, but the callback somehow is needed
-on capi-interface registration.
-
-**************************************************************/
-
-static int hycapi_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
-{
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_load_firmware\n");
-#endif
-       return 0;
-}
-
-
-static char *hycapi_procinfo(struct capi_ctr *ctrl)
-{
-       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "%s\n", __func__);
-#endif
-       if (!cinfo)
-               return "";
-       sprintf(cinfo->infobuf, "%s %s 0x%x %d %s",
-               cinfo->cardname[0] ? cinfo->cardname : "-",
-               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
-               cinfo->card ? cinfo->card->iobase : 0x0,
-               cinfo->card ? cinfo->card->irq : 0,
-               hycapi_revision
-               );
-       return cinfo->infobuf;
-}
-
-/******************************************************************
-hycapi_rx_capipkt
-
-Receive a capi-message.
-
-All B3_DATA_IND are converted to 64K-extension compatible format.
-New nccis are created if necessary.
-*******************************************************************/
-
-void
-hycapi_rx_capipkt(hysdn_card *card, unsigned char *buf, unsigned short len)
-{
-       struct sk_buff *skb;
-       hycapictrl_info *cinfo = card->hyctrlinfo;
-       struct capi_ctr *ctrl;
-       __u16 ApplId;
-       __u16 MsgLen, info;
-       __u16 len2, CapiCmd;
-       __u32 CP64[2] = {0, 0};
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_rx_capipkt\n");
-#endif
-       if (!cinfo) {
-               return;
-       }
-       ctrl = &cinfo->capi_ctrl;
-       if (len < CAPI_MSG_BASELEN) {
-               printk(KERN_ERR "HYSDN Card%d: invalid CAPI-message, length %d!\n",
-                      card->myid, len);
-               return;
-       }
-       MsgLen = CAPIMSG_LEN(buf);
-       ApplId = CAPIMSG_APPID(buf);
-       CapiCmd = CAPIMSG_CMD(buf);
-
-       if ((CapiCmd == CAPI_DATA_B3_IND) && (MsgLen < 30)) {
-               len2 = len + (30 - MsgLen);
-               if (!(skb = alloc_skb(len2, GFP_ATOMIC))) {
-                       printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
-                              card->myid);
-                       return;
-               }
-               skb_put_data(skb, buf, MsgLen);
-               skb_put_data(skb, CP64, 2 * sizeof(__u32));
-               skb_put_data(skb, buf + MsgLen, len - MsgLen);
-               CAPIMSG_SETLEN(skb->data, 30);
-       } else {
-               if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
-                       printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
-                              card->myid);
-                       return;
-               }
-               skb_put_data(skb, buf, len);
-       }
-       switch (CAPIMSG_CMD(skb->data))
-       {
-       case CAPI_CONNECT_B3_CONF:
-/* Check info-field for error-indication: */
-               info = CAPIMSG_U16(skb->data, 12);
-               switch (info)
-               {
-               case 0:
-                       capilib_new_ncci(&cinfo->ncci_head, ApplId, CAPIMSG_NCCI(skb->data),
-                                        hycapi_applications[ApplId - 1].rp.datablkcnt);
-
-                       break;
-               case 0x0001:
-                       printk(KERN_ERR "HYSDN Card%d: NCPI not supported by current "
-                              "protocol. NCPI ignored.\n", card->myid);
-                       break;
-               case 0x2001:
-                       printk(KERN_ERR "HYSDN Card%d: Message not supported in"
-                              " current state\n", card->myid);
-                       break;
-               case 0x2002:
-                       printk(KERN_ERR "HYSDN Card%d: invalid PLCI\n", card->myid);
-                       break;
-               case 0x2004:
-                       printk(KERN_ERR "HYSDN Card%d: out of NCCI\n", card->myid);
-                       break;
-               case 0x3008:
-                       printk(KERN_ERR "HYSDN Card%d: NCPI not supported\n",
-                              card->myid);
-                       break;
-               default:
-                       printk(KERN_ERR "HYSDN Card%d: Info in CONNECT_B3_CONF: %d\n",
-                              card->myid, info);
-                       break;
-               }
-               break;
-       case CAPI_CONNECT_B3_IND:
-               capilib_new_ncci(&cinfo->ncci_head, ApplId,
-                                CAPIMSG_NCCI(skb->data),
-                                hycapi_applications[ApplId - 1].rp.datablkcnt);
-               break;
-       case CAPI_DATA_B3_CONF:
-               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
-                                    CAPIMSG_NCCI(skb->data),
-                                    CAPIMSG_MSGID(skb->data));
-               break;
-       default:
-               break;
-       }
-       capi_ctr_handle_message(ctrl, ApplId, skb);
-}
-
-/******************************************************************
-hycapi_tx_capiack
-
-Internally acknowledge a msg sent. This will remove the msg from the
-internal queue.
-
-*******************************************************************/
-
-void hycapi_tx_capiack(hysdn_card *card)
-{
-       hycapictrl_info *cinfo = card->hyctrlinfo;
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_tx_capiack\n");
-#endif
-       if (!cinfo) {
-               return;
-       }
-       spin_lock_irq(&cinfo->lock);
-       kfree_skb(cinfo->skbs[cinfo->out_idx]);         /* free skb */
-       cinfo->skbs[cinfo->out_idx++] = NULL;
-       if (cinfo->out_idx >= HYSDN_MAX_CAPI_SKB)
-               cinfo->out_idx = 0;     /* wrap around */
-
-       if (cinfo->sk_count-- == HYSDN_MAX_CAPI_SKB)    /* dec usage count */
-               capi_ctr_resume_output(&cinfo->capi_ctrl);
-       spin_unlock_irq(&cinfo->lock);
-}
-
-/***************************************************************
-hycapi_tx_capiget(hysdn_card *card)
-
-This is called when polling for messages to SEND.
-
-****************************************************************/
-
-struct sk_buff *
-hycapi_tx_capiget(hysdn_card *card)
-{
-       hycapictrl_info *cinfo = card->hyctrlinfo;
-       if (!cinfo) {
-               return (struct sk_buff *)NULL;
-       }
-       if (!cinfo->sk_count)
-               return (struct sk_buff *)NULL;  /* nothing available */
-
-       return (cinfo->skbs[cinfo->out_idx]);           /* next packet to send */
-}
-
-
-/**********************************************************
-int hycapi_init()
-
-attach the capi-driver to the kernel-capi.
-
-***********************************************************/
-
-int hycapi_init(void)
-{
-       int i;
-       for (i = 0; i < CAPI_MAXAPPL; i++) {
-               memset(&(hycapi_applications[i]), 0, sizeof(hycapi_appl));
-       }
-       return (0);
-}
-
-/**************************************************************
-hycapi_cleanup(void)
-
-detach the capi-driver to the kernel-capi. Actually this should
-free some more ressources. Do that later.
-**************************************************************/
-
-void
-hycapi_cleanup(void)
-{
-}
-
-/********************************************************************
-hycapi_capi_create(hysdn_card *card)
-
-Attach the card with its capi-ctrl.
-*********************************************************************/
-
-static void hycapi_fill_profile(hysdn_card *card)
-{
-       hycapictrl_info *cinfo = NULL;
-       struct capi_ctr *ctrl = NULL;
-       cinfo = card->hyctrlinfo;
-       if (!cinfo) return;
-       ctrl = &cinfo->capi_ctrl;
-       strcpy(ctrl->manu, "Hypercope");
-       ctrl->version.majorversion = 2;
-       ctrl->version.minorversion = 0;
-       ctrl->version.majormanuversion = 3;
-       ctrl->version.minormanuversion = 2;
-       ctrl->profile.ncontroller = card->myid;
-       ctrl->profile.nbchannel = card->bchans;
-       ctrl->profile.goptions = GLOBAL_OPTION_INTERNAL_CONTROLLER |
-               GLOBAL_OPTION_B_CHANNEL_OPERATION;
-       ctrl->profile.support1 =  B1_PROT_64KBIT_HDLC |
-               (card->faxchans ? B1_PROT_T30 : 0) |
-               B1_PROT_64KBIT_TRANSPARENT;
-       ctrl->profile.support2 = B2_PROT_ISO7776 |
-               (card->faxchans ? B2_PROT_T30 : 0) |
-               B2_PROT_TRANSPARENT;
-       ctrl->profile.support3 = B3_PROT_TRANSPARENT |
-               B3_PROT_T90NL |
-               (card->faxchans ? B3_PROT_T30 : 0) |
-               (card->faxchans ? B3_PROT_T30EXT : 0) |
-               B3_PROT_ISO8208;
-}
-
-int
-hycapi_capi_create(hysdn_card *card)
-{
-       hycapictrl_info *cinfo = NULL;
-       struct capi_ctr *ctrl = NULL;
-       int retval;
-#ifdef HYCAPI_PRINTFNAMES
-       printk(KERN_NOTICE "hycapi_capi_create\n");
-#endif
-       if ((hycapi_enable & (1 << card->myid)) == 0) {
-               return 1;
-       }
-       if (!card->hyctrlinfo) {
-               cinfo = kzalloc(sizeof(hycapictrl_info), GFP_ATOMIC);
-               if (!cinfo) {
-                       printk(KERN_WARNING "HYSDN: no memory for capi-ctrl.\n");
-                       return -ENOMEM;
-               }
-               card->hyctrlinfo = cinfo;
-               cinfo->card = card;
-               spin_lock_init(&cinfo->lock);
-               INIT_LIST_HEAD(&cinfo->ncci_head);
-
-               switch (card->brdtype) {
-               case BD_PCCARD:  strcpy(cinfo->cardname, "HYSDN Hycard"); break;
-               case BD_ERGO: strcpy(cinfo->cardname, "HYSDN Ergo2"); break;
-               case BD_METRO: strcpy(cinfo->cardname, "HYSDN Metro4"); break;
-               case BD_CHAMP2: strcpy(cinfo->cardname, "HYSDN Champ2"); break;
-               case BD_PLEXUS: strcpy(cinfo->cardname, "HYSDN Plexus30"); break;
-               default: strcpy(cinfo->cardname, "HYSDN ???"); break;
-               }
-
-               ctrl = &cinfo->capi_ctrl;
-               ctrl->driver_name   = "hycapi";
-               ctrl->driverdata    = cinfo;
-               ctrl->register_appl = hycapi_register_appl;
-               ctrl->release_appl  = hycapi_release_appl;
-               ctrl->send_message  = hycapi_send_message;
-               ctrl->load_firmware = hycapi_load_firmware;
-               ctrl->reset_ctr     = hycapi_reset_ctr;
-               ctrl->procinfo      = hycapi_procinfo;
-               ctrl->proc_show     = hycapi_proc_show;
-               strcpy(ctrl->name, cinfo->cardname);
-               ctrl->owner = THIS_MODULE;
-
-               retval = attach_capi_ctr(ctrl);
-               if (retval) {
-                       printk(KERN_ERR "hycapi: attach controller failed.\n");
-                       return -EBUSY;
-               }
-               /* fill in the blanks: */
-               hycapi_fill_profile(card);
-               capi_ctr_ready(ctrl);
-       } else {
-               /* resume output on stopped ctrl */
-               ctrl = &card->hyctrlinfo->capi_ctrl;
-               hycapi_fill_profile(card);
-               capi_ctr_ready(ctrl);
-               hycapi_restart_internal(ctrl);
-/*             ctrl->resume_output(ctrl); */
-       }
-       return 0;
-}
diff --git a/drivers/isdn/hysdn/hysdn_boot.c b/drivers/isdn/hysdn/hysdn_boot.c
deleted file mode 100644 (file)
index ba177c3..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards
- * specific routines for booting and pof handling
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#include "hysdn_defs.h"
-#include "hysdn_pof.h"
-
-/********************************/
-/* defines for pof read handler */
-/********************************/
-#define POF_READ_FILE_HEAD  0
-#define POF_READ_TAG_HEAD   1
-#define POF_READ_TAG_DATA   2
-
-/************************************************************/
-/* definition of boot specific data area. This data is only */
-/* needed during boot and so allocated dynamically.         */
-/************************************************************/
-struct boot_data {
-       unsigned short Cryptor; /* for use with Decrypt function */
-       unsigned short Nrecs;   /* records remaining in file */
-       unsigned char pof_state;/* actual state of read handler */
-       unsigned char is_crypted;/* card data is crypted */
-       int BufSize;            /* actual number of bytes bufferd */
-       int last_error;         /* last occurred error */
-       unsigned short pof_recid;/* actual pof recid */
-       unsigned long pof_reclen;/* total length of pof record data */
-       unsigned long pof_recoffset;/* actual offset inside pof record */
-       union {
-               unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
-               tPofRecHdr PofRecHdr;   /* header for actual record/chunk */
-               tPofFileHdr PofFileHdr;         /* header from POF file */
-               tPofTimeStamp PofTime;  /* time information */
-       } buf;
-};
-
-/*****************************************************/
-/*  start decryption of successive POF file chuncks.  */
-/*                                                   */
-/*  to be called at start of POF file reading,       */
-/*  before starting any decryption on any POF record. */
-/*****************************************************/
-static void
-StartDecryption(struct boot_data *boot)
-{
-       boot->Cryptor = CRYPT_STARTTERM;
-}                              /* StartDecryption */
-
-
-/***************************************************************/
-/* decrypt complete BootBuf                                    */
-/* NOTE: decryption must be applied to all or none boot tags - */
-/*       to HI and LO boot loader and (all) seq tags, because  */
-/*       global Cryptor is started for whole POF.              */
-/***************************************************************/
-static void
-DecryptBuf(struct boot_data *boot, int cnt)
-{
-       unsigned char *bufp = boot->buf.BootBuf;
-
-       while (cnt--) {
-               boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
-               *bufp++ ^= (unsigned char)boot->Cryptor;
-       }
-}                              /* DecryptBuf */
-
-/********************************************************************************/
-/* pof_handle_data executes the required actions dependent on the active record */
-/* id. If successful 0 is returned, a negative value shows an error.           */
-/********************************************************************************/
-static int
-pof_handle_data(hysdn_card *card, int datlen)
-{
-       struct boot_data *boot = card->boot;    /* pointer to boot specific data */
-       long l;
-       unsigned char *imgp;
-       int img_len;
-
-       /* handle the different record types */
-       switch (boot->pof_recid) {
-
-       case TAG_TIMESTMP:
-               if (card->debug_flags & LOG_POF_RECORD)
-                       hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
-               break;
-
-       case TAG_CBOOTDTA:
-               DecryptBuf(boot, datlen);       /* we need to encrypt the buffer */
-               /* fall through */
-       case TAG_BOOTDTA:
-               if (card->debug_flags & LOG_POF_RECORD)
-                       hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
-                                    (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
-                                    datlen, boot->pof_recoffset);
-
-               if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
-                       boot->last_error = EPOF_BAD_IMG_SIZE;   /* invalid length */
-                       return (boot->last_error);
-               }
-               imgp = boot->buf.BootBuf;       /* start of buffer */
-               img_len = datlen;       /* maximum length to transfer */
-
-               l = POF_BOOT_LOADER_OFF_IN_PAGE -
-                       (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
-               if (l > 0) {
-                       /* buffer needs to be truncated */
-                       imgp += l;      /* advance pointer */
-                       img_len -= l;   /* adjust len */
-               }
-               /* at this point no special handling for data wrapping over buffer */
-               /* is necessary, because the boot image always will be adjusted to */
-               /* match a page boundary inside the buffer.                        */
-               /* The buffer for the boot image on the card is filled in 2 cycles */
-               /* first the 1024 hi-words are put in the buffer, then the low 1024 */
-               /* word are handled in the same way with different offset.         */
-
-               if (img_len > 0) {
-                       /* data available for copy */
-                       if ((boot->last_error =
-                            card->writebootimg(card, imgp,
-                                               (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
-                               return (boot->last_error);
-               }
-               break;  /* end of case boot image hi/lo */
-
-       case TAG_CABSDATA:
-               DecryptBuf(boot, datlen);       /* we need to encrypt the buffer */
-               /* fall through */
-       case TAG_ABSDATA:
-               if (card->debug_flags & LOG_POF_RECORD)
-                       hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
-                                    (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
-                                    datlen, boot->pof_recoffset);
-
-               if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)
-                       return (boot->last_error);      /* error writing data */
-
-               if (boot->pof_recoffset + datlen >= boot->pof_reclen)
-                       return (card->waitpofready(card));      /* data completely spooled, wait for ready */
-
-               break;  /* end of case boot seq data */
-
-       default:
-               if (card->debug_flags & LOG_POF_RECORD)
-                       hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
-                                    datlen, boot->pof_recoffset);
-
-               break;  /* simply skip record */
-       }                       /* switch boot->pof_recid */
-
-       return (0);
-}                              /* pof_handle_data */
-
-
-/******************************************************************************/
-/* pof_write_buffer is called when the buffer has been filled with the needed */
-/* number of data bytes. The number delivered is additionally supplied for    */
-/* verification. The functions handles the data and returns the needed number */
-/* of bytes for the next action. If the returned value is 0 or less an error  */
-/* occurred and booting must be aborted.                                       */
-/******************************************************************************/
-int
-pof_write_buffer(hysdn_card *card, int datlen)
-{
-       struct boot_data *boot = card->boot;    /* pointer to boot specific data */
-
-       if (!boot)
-               return (-EFAULT);       /* invalid call */
-       if (boot->last_error < 0)
-               return (boot->last_error);      /* repeated error */
-
-       if (card->debug_flags & LOG_POF_WRITE)
-               hysdn_addlog(card, "POF write: got %d bytes ", datlen);
-
-       switch (boot->pof_state) {
-       case POF_READ_FILE_HEAD:
-               if (card->debug_flags & LOG_POF_WRITE)
-                       hysdn_addlog(card, "POF write: checking file header");
-
-               if (datlen != sizeof(tPofFileHdr)) {
-                       boot->last_error = -EPOF_INTERNAL;
-                       break;
-               }
-               if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
-                       boot->last_error = -EPOF_BAD_MAGIC;
-                       break;
-               }
-               /* Setup the new state and vars */
-               boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
-               boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
-               boot->last_error = sizeof(tPofRecHdr);  /* new length */
-               break;
-
-       case POF_READ_TAG_HEAD:
-               if (card->debug_flags & LOG_POF_WRITE)
-                       hysdn_addlog(card, "POF write: checking tag header");
-
-               if (datlen != sizeof(tPofRecHdr)) {
-                       boot->last_error = -EPOF_INTERNAL;
-                       break;
-               }
-               boot->pof_recid = boot->buf.PofRecHdr.PofRecId;         /* actual pof recid */
-               boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen;   /* total length */
-               boot->pof_recoffset = 0;        /* no starting offset */
-
-               if (card->debug_flags & LOG_POF_RECORD)
-                       hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
-                                    boot->pof_recid, boot->pof_reclen);
-
-               boot->pof_state = POF_READ_TAG_DATA;    /* now start with tag data */
-               if (boot->pof_reclen < BOOT_BUF_SIZE)
-                       boot->last_error = boot->pof_reclen;    /* limit size */
-               else
-                       boot->last_error = BOOT_BUF_SIZE;       /* maximum */
-
-               if (!boot->last_error) {        /* no data inside record */
-                       boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
-                       boot->last_error = sizeof(tPofRecHdr);  /* new length */
-               }
-               break;
-
-       case POF_READ_TAG_DATA:
-               if (card->debug_flags & LOG_POF_WRITE)
-                       hysdn_addlog(card, "POF write: getting tag data");
-
-               if (datlen != boot->last_error) {
-                       boot->last_error = -EPOF_INTERNAL;
-                       break;
-               }
-               if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
-                       return (boot->last_error);      /* an error occurred */
-               boot->pof_recoffset += datlen;
-               if (boot->pof_recoffset >= boot->pof_reclen) {
-                       boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
-                       boot->last_error = sizeof(tPofRecHdr);  /* new length */
-               } else {
-                       if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
-                               boot->last_error = boot->pof_reclen - boot->pof_recoffset;      /* limit size */
-                       else
-                               boot->last_error = BOOT_BUF_SIZE;       /* maximum */
-               }
-               break;
-
-       default:
-               boot->last_error = -EPOF_INTERNAL;      /* unknown state */
-               break;
-       }                       /* switch (boot->pof_state) */
-
-       return (boot->last_error);
-}                              /* pof_write_buffer */
-
-
-/*******************************************************************************/
-/* pof_write_open is called when an open for boot on the cardlog device occurs. */
-/* The function returns the needed number of bytes for the next operation. If  */
-/* the returned number is less or equal 0 an error specified by this code      */
-/* occurred. Additionally the pointer to the buffer data area is set on success */
-/*******************************************************************************/
-int
-pof_write_open(hysdn_card *card, unsigned char **bufp)
-{
-       struct boot_data *boot; /* pointer to boot specific data */
-
-       if (card->boot) {
-               if (card->debug_flags & LOG_POF_OPEN)
-                       hysdn_addlog(card, "POF open: already opened for boot");
-               return (-ERR_ALREADY_BOOT);     /* boot already active */
-       }
-       /* error no mem available */
-       if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
-               if (card->debug_flags & LOG_MEM_ERR)
-                       hysdn_addlog(card, "POF open: unable to allocate mem");
-               return (-EFAULT);
-       }
-       card->boot = boot;
-       card->state = CARD_STATE_BOOTING;
-
-       card->stopcard(card);   /* first stop the card */
-       if (card->testram(card)) {
-               if (card->debug_flags & LOG_POF_OPEN)
-                       hysdn_addlog(card, "POF open: DPRAM test failure");
-               boot->last_error = -ERR_BOARD_DPRAM;
-               card->state = CARD_STATE_BOOTERR;       /* show boot error */
-               return (boot->last_error);
-       }
-       boot->BufSize = 0;      /* Buffer is empty */
-       boot->pof_state = POF_READ_FILE_HEAD;   /* read file header */
-       StartDecryption(boot);  /* if POF File should be encrypted */
-
-       if (card->debug_flags & LOG_POF_OPEN)
-               hysdn_addlog(card, "POF open: success");
-
-       *bufp = boot->buf.BootBuf;      /* point to buffer */
-       return (sizeof(tPofFileHdr));
-}                              /* pof_write_open */
-
-/********************************************************************************/
-/* pof_write_close is called when an close of boot on the cardlog device occurs. */
-/* The return value must be 0 if everything has happened as desired.            */
-/********************************************************************************/
-int
-pof_write_close(hysdn_card *card)
-{
-       struct boot_data *boot = card->boot;    /* pointer to boot specific data */
-
-       if (!boot)
-               return (-EFAULT);       /* invalid call */
-
-       card->boot = NULL;      /* no boot active */
-       kfree(boot);
-
-       if (card->state == CARD_STATE_RUN)
-               card->set_errlog_state(card, 1);        /* activate error log */
-
-       if (card->debug_flags & LOG_POF_OPEN)
-               hysdn_addlog(card, "POF close: success");
-
-       return (0);
-}                              /* pof_write_close */
-
-/*********************************************************************************/
-/* EvalSysrTokData checks additional records delivered with the Sysready Message */
-/* when POF has been booted. A return value of 0 is used if no error occurred.    */
-/*********************************************************************************/
-int
-EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
-{
-       u_char *p;
-       u_char crc;
-
-       if (card->debug_flags & LOG_POF_RECORD)
-               hysdn_addlog(card, "SysReady Token data length %d", len);
-
-       if (len < 2) {
-               hysdn_addlog(card, "SysReady Token Data to short");
-               return (1);
-       }
-       for (p = cp, crc = 0; p < (cp + len - 2); p++)
-               if ((crc & 0x80))
-                       crc = (((u_char) (crc << 1)) + 1) + *p;
-               else
-                       crc = ((u_char) (crc << 1)) + *p;
-       crc = ~crc;
-       if (crc != *(cp + len - 1)) {
-               hysdn_addlog(card, "SysReady Token Data invalid CRC");
-               return (1);
-       }
-       len--;                  /* don't check CRC byte */
-       while (len > 0) {
-
-               if (*cp == SYSR_TOK_END)
-                       return (0);     /* End of Token stream */
-
-               if (len < (*(cp + 1) + 2)) {
-                       hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
-                       return (1);
-               }
-               switch (*cp) {
-               case SYSR_TOK_B_CHAN:   /* 1 */
-                       if (*(cp + 1) != 1)
-                               return (1);     /* length invalid */
-                       card->bchans = *(cp + 2);
-                       break;
-
-               case SYSR_TOK_FAX_CHAN: /* 2 */
-                       if (*(cp + 1) != 1)
-                               return (1);     /* length invalid */
-                       card->faxchans = *(cp + 2);
-                       break;
-
-               case SYSR_TOK_MAC_ADDR: /* 3 */
-                       if (*(cp + 1) != 6)
-                               return (1);     /* length invalid */
-                       memcpy(card->mac_addr, cp + 2, 6);
-                       break;
-
-               default:
-                       hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
-                       break;
-               }
-               len -= (*(cp + 1) + 2);         /* adjust len */
-               cp += (*(cp + 1) + 2);  /* and pointer */
-       }
-
-       hysdn_addlog(card, "no end token found");
-       return (1);
-}                              /* EvalSysrTokData */
diff --git a/drivers/isdn/hysdn/hysdn_defs.h b/drivers/isdn/hysdn/hysdn_defs.h
deleted file mode 100644 (file)
index cdac46a..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-/* $Id: hysdn_defs.h,v 1.5.6.3 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards
- * global definitions and exported vars and functions.
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#ifndef HYSDN_DEFS_H
-#define HYSDN_DEFS_H
-
-#include <linux/hysdn_if.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/skbuff.h>
-
-#include "ince1pc.h"
-
-#ifdef CONFIG_HYSDN_CAPI
-#include <linux/capi.h>
-#include <linux/isdn/capicmd.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-
-/***************************/
-/*   CAPI-Profile values.  */
-/***************************/
-
-#define GLOBAL_OPTION_INTERNAL_CONTROLLER 0x0001
-#define GLOBAL_OPTION_EXTERNAL_CONTROLLER 0x0002
-#define GLOBAL_OPTION_HANDSET             0x0004
-#define GLOBAL_OPTION_DTMF                0x0008
-#define GLOBAL_OPTION_SUPPL_SERVICES      0x0010
-#define GLOBAL_OPTION_CHANNEL_ALLOCATION  0x0020
-#define GLOBAL_OPTION_B_CHANNEL_OPERATION 0x0040
-
-#define B1_PROT_64KBIT_HDLC        0x0001
-#define B1_PROT_64KBIT_TRANSPARENT 0x0002
-#define B1_PROT_V110_ASYNCH        0x0004
-#define B1_PROT_V110_SYNCH         0x0008
-#define B1_PROT_T30                0x0010
-#define B1_PROT_64KBIT_INV_HDLC    0x0020
-#define B1_PROT_56KBIT_TRANSPARENT 0x0040
-
-#define B2_PROT_ISO7776            0x0001
-#define B2_PROT_TRANSPARENT        0x0002
-#define B2_PROT_SDLC               0x0004
-#define B2_PROT_LAPD               0x0008
-#define B2_PROT_T30                0x0010
-#define B2_PROT_PPP                0x0020
-#define B2_PROT_TRANSPARENT_IGNORE_B1_FRAMING_ERRORS 0x0040
-
-#define B3_PROT_TRANSPARENT        0x0001
-#define B3_PROT_T90NL              0x0002
-#define B3_PROT_ISO8208            0x0004
-#define B3_PROT_X25_DCE            0x0008
-#define B3_PROT_T30                0x0010
-#define B3_PROT_T30EXT             0x0020
-
-#define HYSDN_MAXVERSION               8
-
-/* Number of sendbuffers in CAPI-queue */
-#define HYSDN_MAX_CAPI_SKB             20
-
-#endif /* CONFIG_HYSDN_CAPI*/
-
-/************************************************/
-/* constants and bits for debugging/log outputs */
-/************************************************/
-#define LOG_MAX_LINELEN 120
-#define DEB_OUT_SYSLOG  0x80000000     /* output to syslog instead of proc fs */
-#define LOG_MEM_ERR     0x00000001     /* log memory errors like kmalloc failure */
-#define LOG_POF_OPEN    0x00000010     /* log pof open and close activities */
-#define LOG_POF_RECORD  0x00000020     /* log pof record parser */
-#define LOG_POF_WRITE   0x00000040     /* log detailed pof write operation */
-#define LOG_POF_CARD    0x00000080     /* log pof related card functions */
-#define LOG_CNF_LINE    0x00000100     /* all conf lines are put to procfs */
-#define LOG_CNF_DATA    0x00000200     /* non comment conf lines are shown with channel */
-#define LOG_CNF_MISC    0x00000400     /* additional conf line debug outputs */
-#define LOG_SCHED_ASYN  0x00001000     /* debug schedulers async tx routines */
-#define LOG_PROC_OPEN   0x00100000     /* open and close from procfs are logged */
-#define LOG_PROC_ALL    0x00200000     /* all actions from procfs are logged */
-#define LOG_NET_INIT    0x00010000     /* network init and deinit logging */
-
-#define DEF_DEB_FLAGS   0x7fff000f     /* everything is logged to procfs */
-
-/**********************************/
-/* proc filesystem name constants */
-/**********************************/
-#define PROC_SUBDIR_NAME "hysdn"
-#define PROC_CONF_BASENAME "cardconf"
-#define PROC_LOG_BASENAME "cardlog"
-
-/***********************************/
-/* PCI 32 bit parms for IO and MEM */
-/***********************************/
-#define PCI_REG_PLX_MEM_BASE    0
-#define PCI_REG_PLX_IO_BASE     1
-#define PCI_REG_MEMORY_BASE     3
-
-/**************/
-/* card types */
-/**************/
-#define BD_NONE         0U
-#define BD_PERFORMANCE  1U
-#define BD_VALUE        2U
-#define BD_PCCARD       3U
-#define BD_ERGO         4U
-#define BD_METRO        5U
-#define BD_CHAMP2       6U
-#define BD_PLEXUS       7U
-
-/******************************************************/
-/* defined states for cards shown by reading cardconf */
-/******************************************************/
-#define CARD_STATE_UNUSED   0  /* never been used or booted */
-#define CARD_STATE_BOOTING  1  /* booting is in progress */
-#define CARD_STATE_BOOTERR  2  /* a previous boot was aborted */
-#define CARD_STATE_RUN      3  /* card is active */
-
-/*******************************/
-/* defines for error_log_state */
-/*******************************/
-#define ERRLOG_STATE_OFF   0   /* error log is switched off, nothing to do */
-#define ERRLOG_STATE_ON    1   /* error log is switched on, wait for data */
-#define ERRLOG_STATE_START 2   /* start error logging */
-#define ERRLOG_STATE_STOP  3   /* stop error logging */
-
-/*******************************/
-/* data structure for one card */
-/*******************************/
-typedef struct HYSDN_CARD {
-
-       /* general variables for the cards */
-       int myid;               /* own driver card id */
-       unsigned char bus;      /* pci bus the card is connected to */
-       unsigned char devfn;    /* slot+function bit encoded */
-       unsigned short subsysid;/* PCI subsystem id */
-       unsigned char brdtype;  /* type of card */
-       unsigned int bchans;    /* number of available B-channels */
-       unsigned int faxchans;  /* number of available fax-channels */
-       unsigned char mac_addr[6];/* MAC Address read from card */
-       unsigned int irq;       /* interrupt number */
-       unsigned int iobase;    /* IO-port base address */
-       unsigned long plxbase;  /* PLX memory base */
-       unsigned long membase;  /* DPRAM memory base */
-       unsigned long memend;   /* DPRAM memory end */
-       void *dpram;            /* mapped dpram */
-       int state;              /* actual state of card -> CARD_STATE_** */
-       struct HYSDN_CARD *next;        /* pointer to next card */
-
-       /* data areas for the /proc file system */
-       void *proclog;          /* pointer to proclog filesystem specific data */
-       void *procconf;         /* pointer to procconf filesystem specific data */
-
-       /* debugging and logging */
-       unsigned char err_log_state;/* actual error log state of the card */
-       unsigned long debug_flags;/* tells what should be debugged and where */
-       void (*set_errlog_state) (struct HYSDN_CARD *, int);
-
-       /* interrupt handler + interrupt synchronisation */
-       struct work_struct irq_queue;   /* interrupt task queue */
-       unsigned char volatile irq_enabled;/* interrupt enabled if != 0 */
-       unsigned char volatile hw_lock;/* hardware is currently locked -> no access */
-
-       /* boot process */
-       void *boot;             /* pointer to boot private data */
-       int (*writebootimg) (struct HYSDN_CARD *, unsigned char *, unsigned long);
-       int (*writebootseq) (struct HYSDN_CARD *, unsigned char *, int);
-       int (*waitpofready) (struct HYSDN_CARD *);
-       int (*testram) (struct HYSDN_CARD *);
-
-       /* scheduler for data transfer (only async parts) */
-       unsigned char async_data[256];/* async data to be sent (normally for config) */
-       unsigned short volatile async_len;/* length of data to sent */
-       unsigned short volatile async_channel;/* channel number for async transfer */
-       int volatile async_busy;        /* flag != 0 sending in progress */
-       int volatile net_tx_busy;       /* a network packet tx is in progress */
-
-       /* network interface */
-       void *netif;            /* pointer to network structure */
-
-       /* init and deinit stopcard for booting, too */
-       void (*stopcard) (struct HYSDN_CARD *);
-       void (*releasehardware) (struct HYSDN_CARD *);
-
-       spinlock_t hysdn_lock;
-#ifdef CONFIG_HYSDN_CAPI
-       struct hycapictrl_info {
-               char cardname[32];
-               spinlock_t lock;
-               int versionlen;
-               char versionbuf[1024];
-               char *version[HYSDN_MAXVERSION];
-
-               char infobuf[128];      /* for function procinfo */
-
-               struct HYSDN_CARD  *card;
-               struct capi_ctr capi_ctrl;
-               struct sk_buff *skbs[HYSDN_MAX_CAPI_SKB];
-               int in_idx, out_idx;    /* indexes to buffer ring */
-               int sk_count;           /* number of buffers currently in ring */
-               struct sk_buff *tx_skb; /* buffer for tx operation */
-
-               struct list_head ncci_head;
-       } *hyctrlinfo;
-#endif /* CONFIG_HYSDN_CAPI */
-} hysdn_card;
-
-#ifdef CONFIG_HYSDN_CAPI
-typedef struct hycapictrl_info hycapictrl_info;
-#endif /* CONFIG_HYSDN_CAPI */
-
-
-/*****************/
-/* exported vars */
-/*****************/
-extern hysdn_card *card_root;  /* pointer to first card */
-
-
-
-/*************************/
-/* im/exported functions */
-/*************************/
-
-/* hysdn_procconf.c */
-extern int hysdn_procconf_init(void);  /* init proc config filesys */
-extern void hysdn_procconf_release(void);      /* deinit proc config filesys */
-
-/* hysdn_proclog.c */
-extern int hysdn_proclog_init(hysdn_card *);   /* init proc log entry */
-extern void hysdn_proclog_release(hysdn_card *);       /* deinit proc log entry */
-extern void hysdn_addlog(hysdn_card *, char *, ...);   /* output data to log */
-extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int);      /* output card log */
-
-/* boardergo.c */
-extern int ergo_inithardware(hysdn_card *card);        /* get hardware -> module init */
-
-/* hysdn_boot.c */
-extern int pof_write_close(hysdn_card *);      /* close proc file after writing pof */
-extern int pof_write_open(hysdn_card *, unsigned char **);     /* open proc file for writing pof */
-extern int pof_write_buffer(hysdn_card *, int);                /* write boot data to card */
-extern int EvalSysrTokData(hysdn_card *, unsigned char *, int);                /* Check Sysready Token Data */
-
-/* hysdn_sched.c */
-extern int hysdn_sched_tx(hysdn_card *, unsigned char *,
-                         unsigned short volatile *, unsigned short volatile *,
-                         unsigned short);
-extern int hysdn_sched_rx(hysdn_card *, unsigned char *, unsigned short,
-                         unsigned short);
-extern int hysdn_tx_cfgline(hysdn_card *, unsigned char *,
-                           unsigned short);    /* send one cfg line */
-
-/* hysdn_net.c */
-extern unsigned int hynet_enable;
-extern int hysdn_net_create(hysdn_card *);     /* create a new net device */
-extern int hysdn_net_release(hysdn_card *);    /* delete the device */
-extern char *hysdn_net_getname(hysdn_card *);  /* get name of net interface */
-extern void hysdn_tx_netack(hysdn_card *);     /* acknowledge a packet tx */
-extern struct sk_buff *hysdn_tx_netget(hysdn_card *);  /* get next network packet */
-extern void hysdn_rx_netpkt(hysdn_card *, unsigned char *,
-                           unsigned short);    /* rxed packet from network */
-
-#ifdef CONFIG_HYSDN_CAPI
-extern unsigned int hycapi_enable;
-extern int hycapi_capi_create(hysdn_card *);   /* create a new capi device */
-extern int hycapi_capi_release(hysdn_card *);  /* delete the device */
-extern int hycapi_capi_stop(hysdn_card *card);   /* suspend */
-extern void hycapi_rx_capipkt(hysdn_card *card, unsigned char *buf,
-                             unsigned short len);
-extern void hycapi_tx_capiack(hysdn_card *card);
-extern struct sk_buff *hycapi_tx_capiget(hysdn_card *card);
-extern int hycapi_init(void);
-extern void hycapi_cleanup(void);
-#endif /* CONFIG_HYSDN_CAPI */
-
-#endif /* HYSDN_DEFS_H */
diff --git a/drivers/isdn/hysdn/hysdn_init.c b/drivers/isdn/hysdn/hysdn_init.c
deleted file mode 100644 (file)
index 0db2f75..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/* $Id: hysdn_init.c,v 1.6.6.6 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards, init functions.
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/poll.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-
-#include "hysdn_defs.h"
-
-static struct pci_device_id hysdn_pci_tbl[] = {
-       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
-         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO, 0, 0, BD_METRO },
-       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
-         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, 0, 0, BD_CHAMP2 },
-       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
-         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, 0, 0, BD_ERGO },
-       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
-         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, 0, 0, BD_ERGO },
-
-       { }                             /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(pci, hysdn_pci_tbl);
-MODULE_DESCRIPTION("ISDN4Linux: Driver for HYSDN cards");
-MODULE_AUTHOR("Werner Cornelius");
-MODULE_LICENSE("GPL");
-
-static int cardmax;            /* number of found cards */
-hysdn_card *card_root = NULL;  /* pointer to first card */
-static hysdn_card *card_last = NULL;   /* pointer to first card */
-
-
-/****************************************************************************/
-/* The module startup and shutdown code. Only compiled when used as module. */
-/* Using the driver as module is always advisable, because the booting      */
-/* image becomes smaller and the driver code is only loaded when needed.    */
-/* Additionally newer versions may be activated without rebooting.          */
-/****************************************************************************/
-
-/****************************************************************************/
-/* init_module is called once when the module is loaded to do all necessary */
-/* things like autodetect...                                                */
-/* If the return value of this function is 0 the init has been successful   */
-/* and the module is added to the list in /proc/modules, otherwise an error */
-/* is assumed and the module will not be kept in memory.                    */
-/****************************************************************************/
-
-static int hysdn_pci_init_one(struct pci_dev *akt_pcidev,
-                             const struct pci_device_id *ent)
-{
-       hysdn_card *card;
-       int rc;
-
-       rc = pci_enable_device(akt_pcidev);
-       if (rc)
-               return rc;
-
-       if (!(card = kzalloc(sizeof(hysdn_card), GFP_KERNEL))) {
-               printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
-               rc = -ENOMEM;
-               goto err_out;
-       }
-       card->myid = cardmax;   /* set own id */
-       card->bus = akt_pcidev->bus->number;
-       card->devfn = akt_pcidev->devfn;        /* slot + function */
-       card->subsysid = akt_pcidev->subsystem_device;
-       card->irq = akt_pcidev->irq;
-       card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE);
-       card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE);
-       card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE);
-       card->brdtype = BD_NONE;        /* unknown */
-       card->debug_flags = DEF_DEB_FLAGS;      /* set default debug */
-       card->faxchans = 0;     /* default no fax channels */
-       card->bchans = 2;       /* and 2 b-channels */
-       card->brdtype = ent->driver_data;
-
-       if (ergo_inithardware(card)) {
-               printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
-               rc = -EBUSY;
-               goto err_out_card;
-       }
-
-       cardmax++;
-       card->next = NULL;      /*end of chain */
-       if (card_last)
-               card_last->next = card;         /* pointer to next card */
-       else
-               card_root = card;
-       card_last = card;       /* new chain end */
-
-       pci_set_drvdata(akt_pcidev, card);
-       return 0;
-
-err_out_card:
-       kfree(card);
-err_out:
-       pci_disable_device(akt_pcidev);
-       return rc;
-}
-
-static void hysdn_pci_remove_one(struct pci_dev *akt_pcidev)
-{
-       hysdn_card *card = pci_get_drvdata(akt_pcidev);
-
-       pci_set_drvdata(akt_pcidev, NULL);
-
-       if (card->stopcard)
-               card->stopcard(card);
-
-#ifdef CONFIG_HYSDN_CAPI
-       hycapi_capi_release(card);
-#endif
-
-       if (card->releasehardware)
-               card->releasehardware(card);   /* free all hardware resources */
-
-       if (card == card_root) {
-               card_root = card_root->next;
-               if (!card_root)
-                       card_last = NULL;
-       } else {
-               hysdn_card *tmp = card_root;
-               while (tmp) {
-                       if (tmp->next == card)
-                               tmp->next = card->next;
-                       card_last = tmp;
-                       tmp = tmp->next;
-               }
-       }
-
-       kfree(card);
-       pci_disable_device(akt_pcidev);
-}
-
-static struct pci_driver hysdn_pci_driver = {
-       .name           = "hysdn",
-       .id_table       = hysdn_pci_tbl,
-       .probe          = hysdn_pci_init_one,
-       .remove         = hysdn_pci_remove_one,
-};
-
-static int hysdn_have_procfs;
-
-static int __init
-hysdn_init(void)
-{
-       int rc;
-
-       printk(KERN_NOTICE "HYSDN: module loaded\n");
-
-       rc = pci_register_driver(&hysdn_pci_driver);
-       if (rc)
-               return rc;
-
-       printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
-
-       if (!hysdn_procconf_init())
-               hysdn_have_procfs = 1;
-
-#ifdef CONFIG_HYSDN_CAPI
-       if (cardmax > 0) {
-               if (hycapi_init()) {
-                       printk(KERN_ERR "HYCAPI: init failed\n");
-
-                       if (hysdn_have_procfs)
-                               hysdn_procconf_release();
-
-                       pci_unregister_driver(&hysdn_pci_driver);
-                       return -ESPIPE;
-               }
-       }
-#endif /* CONFIG_HYSDN_CAPI */
-
-       return 0;               /* no error */
-}                              /* init_module */
-
-
-/***********************************************************************/
-/* cleanup_module is called when the module is released by the kernel. */
-/* The routine is only called if init_module has been successful and   */
-/* the module counter has a value of 0. Otherwise this function will   */
-/* not be called. This function must release all resources still allo- */
-/* cated as after the return from this function the module code will   */
-/* be removed from memory.                                             */
-/***********************************************************************/
-static void __exit
-hysdn_exit(void)
-{
-       if (hysdn_have_procfs)
-               hysdn_procconf_release();
-
-       pci_unregister_driver(&hysdn_pci_driver);
-
-#ifdef CONFIG_HYSDN_CAPI
-       hycapi_cleanup();
-#endif /* CONFIG_HYSDN_CAPI */
-
-       printk(KERN_NOTICE "HYSDN: module unloaded\n");
-}                              /* cleanup_module */
-
-module_init(hysdn_init);
-module_exit(hysdn_exit);
diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
deleted file mode 100644 (file)
index 8e9c34f..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-/* $Id: hysdn_net.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards, net (ethernet type) handling routines.
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * This net module has been inspired by the skeleton driver from
- * Donald Becker (becker@CESDIS.gsfc.nasa.gov)
- *
- */
-
-#include <linux/module.h>
-#include <linux/signal.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inetdevice.h>
-
-#include "hysdn_defs.h"
-
-unsigned int hynet_enable = 0xffffffff;
-module_param(hynet_enable, uint, 0);
-
-#define MAX_SKB_BUFFERS 20     /* number of buffers for keeping TX-data */
-
-/****************************************************************************/
-/* structure containing the complete network data. The structure is aligned */
-/* in a way that both, the device and statistics are kept inside it.        */
-/* for proper access, the device structure MUST be the first var/struct     */
-/* inside the definition.                                                   */
-/****************************************************************************/
-struct net_local {
-       /* Tx control lock.  This protects the transmit buffer ring
-        * state along with the "tx full" state of the driver.  This
-        * means all netif_queue flow control actions are protected
-        * by this lock as well.
-        */
-       struct net_device *dev;
-       spinlock_t lock;
-       struct sk_buff *skbs[MAX_SKB_BUFFERS];  /* pointers to tx-skbs */
-       int in_idx, out_idx;    /* indexes to buffer ring */
-       int sk_count;           /* number of buffers currently in ring */
-};                             /* net_local */
-
-
-
-/*********************************************************************/
-/* Open/initialize the board. This is called (in the current kernel) */
-/* sometime after booting when the 'ifconfig' program is run.        */
-/* This routine should set everything up anew at each open, even     */
-/* registers that "should" only need to be set once at boot, so that */
-/* there is non-reboot way to recover if something goes wrong.       */
-/*********************************************************************/
-static int
-net_open(struct net_device *dev)
-{
-       struct in_device *in_dev;
-       hysdn_card *card = dev->ml_priv;
-       int i;
-
-       netif_start_queue(dev); /* start tx-queueing */
-
-       /* Fill in the MAC-level header (if not already set) */
-       if (!card->mac_addr[0]) {
-               for (i = 0; i < ETH_ALEN; i++)
-                       dev->dev_addr[i] = 0xfc;
-               if ((in_dev = dev->ip_ptr) != NULL) {
-                       struct in_ifaddr *ifa = in_dev->ifa_list;
-                       if (ifa != NULL)
-                               memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ifa->ifa_local)), &ifa->ifa_local, sizeof(ifa->ifa_local));
-               }
-       } else
-               memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
-
-       return (0);
-}                              /* net_open */
-
-/*******************************************/
-/* flush the currently occupied tx-buffers */
-/* must only be called when device closed  */
-/*******************************************/
-static void
-flush_tx_buffers(struct net_local *nl)
-{
-
-       while (nl->sk_count) {
-               dev_kfree_skb(nl->skbs[nl->out_idx++]);         /* free skb */
-               if (nl->out_idx >= MAX_SKB_BUFFERS)
-                       nl->out_idx = 0;        /* wrap around */
-               nl->sk_count--;
-       }
-}                              /* flush_tx_buffers */
-
-
-/*********************************************************************/
-/* close/decativate the device. The device is not removed, but only  */
-/* deactivated.                                                      */
-/*********************************************************************/
-static int
-net_close(struct net_device *dev)
-{
-
-       netif_stop_queue(dev);  /* disable queueing */
-
-       flush_tx_buffers((struct net_local *) dev);
-
-       return (0);             /* success */
-}                              /* net_close */
-
-/************************************/
-/* send a packet on this interface. */
-/* new style for kernel >= 2.3.33   */
-/************************************/
-static netdev_tx_t
-net_send_packet(struct sk_buff *skb, struct net_device *dev)
-{
-       struct net_local *lp = (struct net_local *) dev;
-
-       spin_lock_irq(&lp->lock);
-
-       lp->skbs[lp->in_idx++] = skb;   /* add to buffer list */
-       if (lp->in_idx >= MAX_SKB_BUFFERS)
-               lp->in_idx = 0; /* wrap around */
-       lp->sk_count++;         /* adjust counter */
-       netif_trans_update(dev);
-
-       /* If we just used up the very last entry in the
-        * TX ring on this device, tell the queueing
-        * layer to send no more.
-        */
-       if (lp->sk_count >= MAX_SKB_BUFFERS)
-               netif_stop_queue(dev);
-
-       /* When the TX completion hw interrupt arrives, this
-        * is when the transmit statistics are updated.
-        */
-
-       spin_unlock_irq(&lp->lock);
-
-       if (lp->sk_count <= 3) {
-               schedule_work(&((hysdn_card *) dev->ml_priv)->irq_queue);
-       }
-       return NETDEV_TX_OK;    /* success */
-}                              /* net_send_packet */
-
-
-
-/***********************************************************************/
-/* acknowlegde a packet send. The network layer will be informed about */
-/* completion                                                          */
-/***********************************************************************/
-void
-hysdn_tx_netack(hysdn_card *card)
-{
-       struct net_local *lp = card->netif;
-
-       if (!lp)
-               return;         /* non existing device */
-
-
-       if (!lp->sk_count)
-               return;         /* error condition */
-
-       lp->dev->stats.tx_packets++;
-       lp->dev->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
-
-       dev_kfree_skb(lp->skbs[lp->out_idx++]);         /* free skb */
-       if (lp->out_idx >= MAX_SKB_BUFFERS)
-               lp->out_idx = 0;        /* wrap around */
-
-       if (lp->sk_count-- == MAX_SKB_BUFFERS)  /* dec usage count */
-               netif_start_queue((struct net_device *) lp);
-}                              /* hysdn_tx_netack */
-
-/*****************************************************/
-/* we got a packet from the network, go and queue it */
-/*****************************************************/
-void
-hysdn_rx_netpkt(hysdn_card *card, unsigned char *buf, unsigned short len)
-{
-       struct net_local *lp = card->netif;
-       struct net_device *dev;
-       struct sk_buff *skb;
-
-       if (!lp)
-               return;         /* non existing device */
-
-       dev = lp->dev;
-       dev->stats.rx_bytes += len;
-
-       skb = dev_alloc_skb(len);
-       if (skb == NULL) {
-               printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
-                      dev->name);
-               dev->stats.rx_dropped++;
-               return;
-       }
-       /* copy the data */
-       skb_put_data(skb, buf, len);
-
-       /* determine the used protocol */
-       skb->protocol = eth_type_trans(skb, dev);
-
-       dev->stats.rx_packets++;        /* adjust packet count */
-
-       netif_rx(skb);
-}                              /* hysdn_rx_netpkt */
-
-/*****************************************************/
-/* return the pointer to a network packet to be send */
-/*****************************************************/
-struct sk_buff *
-hysdn_tx_netget(hysdn_card *card)
-{
-       struct net_local *lp = card->netif;
-
-       if (!lp)
-               return (NULL);  /* non existing device */
-
-       if (!lp->sk_count)
-               return (NULL);  /* nothing available */
-
-       return (lp->skbs[lp->out_idx]);         /* next packet to send */
-}                              /* hysdn_tx_netget */
-
-static const struct net_device_ops hysdn_netdev_ops = {
-       .ndo_open               = net_open,
-       .ndo_stop               = net_close,
-       .ndo_start_xmit         = net_send_packet,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_validate_addr      = eth_validate_addr,
-};
-
-
-/*****************************************************************************/
-/* hysdn_net_create creates a new net device for the given card. If a device */
-/* already exists, it will be deleted and created a new one. The return value */
-/* 0 announces success, else a negative error code will be returned.         */
-/*****************************************************************************/
-int
-hysdn_net_create(hysdn_card *card)
-{
-       struct net_device *dev;
-       int i;
-       struct net_local *lp;
-
-       if (!card) {
-               printk(KERN_WARNING "No card-pt in hysdn_net_create!\n");
-               return (-ENOMEM);
-       }
-       hysdn_net_release(card);        /* release an existing net device */
-
-       dev = alloc_etherdev(sizeof(struct net_local));
-       if (!dev) {
-               printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
-               return (-ENOMEM);
-       }
-
-       lp = netdev_priv(dev);
-       lp->dev = dev;
-
-       dev->netdev_ops = &hysdn_netdev_ops;
-       spin_lock_init(&((struct net_local *) dev)->lock);
-
-       /* initialise necessary or informing fields */
-       dev->base_addr = card->iobase;  /* IO address */
-       dev->irq = card->irq;   /* irq */
-
-       dev->netdev_ops = &hysdn_netdev_ops;
-       if ((i = register_netdev(dev))) {
-               printk(KERN_WARNING "HYSDN: unable to create network device\n");
-               free_netdev(dev);
-               return (i);
-       }
-       dev->ml_priv = card;    /* remember pointer to own data structure */
-       card->netif = dev;      /* setup the local pointer */
-
-       if (card->debug_flags & LOG_NET_INIT)
-               hysdn_addlog(card, "network device created");
-       return (0);             /* and return success */
-}                              /* hysdn_net_create */
-
-/***************************************************************************/
-/* hysdn_net_release deletes the net device for the given card. The return */
-/* value 0 announces success, else a negative error code will be returned. */
-/***************************************************************************/
-int
-hysdn_net_release(hysdn_card *card)
-{
-       struct net_device *dev = card->netif;
-
-       if (!dev)
-               return (0);     /* non existing */
-
-       card->netif = NULL;     /* clear out pointer */
-       net_close(dev);
-
-       flush_tx_buffers((struct net_local *) dev);     /* empty buffers */
-
-       unregister_netdev(dev); /* release the device */
-       free_netdev(dev);       /* release the memory allocated */
-       if (card->debug_flags & LOG_NET_INIT)
-               hysdn_addlog(card, "network device deleted");
-
-       return (0);             /* always successful */
-}                              /* hysdn_net_release */
-
-/*****************************************************************************/
-/* hysdn_net_getname returns a pointer to the name of the network interface. */
-/* if the interface is not existing, a "-" is returned.                      */
-/*****************************************************************************/
-char *
-hysdn_net_getname(hysdn_card *card)
-{
-       struct net_device *dev = card->netif;
-
-       if (!dev)
-               return ("-");   /* non existing */
-
-       return (dev->name);
-}                              /* hysdn_net_getname */
diff --git a/drivers/isdn/hysdn/hysdn_pof.h b/drivers/isdn/hysdn/hysdn_pof.h
deleted file mode 100644 (file)
index f63f5fa..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/* $Id: hysdn_pof.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards, definitions used for handling pof-files.
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-/************************/
-/* POF specific defines */
-/************************/
-#define BOOT_BUF_SIZE   0x1000 /* =4096, maybe moved to other h file */
-#define CRYPT_FEEDTERM  0x8142
-#define CRYPT_STARTTERM 0x81a5
-/*  max. timeout time in seconds
- *  from end of booting to POF is ready
- */
-#define POF_READY_TIME_OUT_SEC  10
-
-/**********************************/
-/* defines for 1.stage boot image */
-/**********************************/
-
-/*  the POF file record containing the boot loader image
- *  has 2 pages a 16KB:
- *  1. page contains the high 16-bit part of the 32-bit E1 words
- *  2. page contains the low  16-bit part of the 32-bit E1 words
- *
- *  In each 16KB page we assume the start of the boot loader code
- *  in the highest 2KB part (at offset 0x3800);
- *  the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
- */
-
-#define POF_BOOT_LOADER_PAGE_SIZE   0x4000     /* =16384U */
-#define POF_BOOT_LOADER_TOTAL_SIZE  (2U * POF_BOOT_LOADER_PAGE_SIZE)
-
-#define POF_BOOT_LOADER_CODE_SIZE   0x0800     /* =2KB =2048U */
-
-/* offset in boot page, where loader code may start */
-/* =0x3800= 14336U */
-#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
-
-
-/*--------------------------------------POF file record structs------------*/
-typedef struct PofFileHdr_tag {        /* Pof file header */
-       /*00 */ unsigned long Magic __attribute__((packed));
-       /*04 */ unsigned long N_PofRecs __attribute__((packed));
-/*08 */
-} tPofFileHdr;
-
-typedef struct PofRecHdr_tag { /* Pof record header */
-       /*00 */ unsigned short PofRecId __attribute__((packed));
-       /*02 */ unsigned long PofRecDataLen __attribute__((packed));
-/*06 */
-} tPofRecHdr;
-
-typedef struct PofTimeStamp_tag {
-       /*00 */ unsigned long UnixTime __attribute__((packed));
-       /*04 */ unsigned char DateTimeText[0x28];
-       /* =40 */
-/*2C */
-} tPofTimeStamp;
-
-/* tPofFileHdr.Magic value: */
-#define TAGFILEMAGIC 0x464F501AUL
-/* tPofRecHdr.PofRecId values: */
-#define TAG_ABSDATA  0x1000    /* abs. data */
-#define TAG_BOOTDTA  0x1001    /* boot data */
-#define TAG_COMMENT  0x0020
-#define TAG_SYSCALL  0x0021
-#define TAG_FLOWCTRL 0x0022
-#define TAG_TIMESTMP 0x0010    /* date/time stamp of version */
-#define TAG_CABSDATA 0x1100    /* crypted abs. data */
-#define TAG_CBOOTDTA 0x1101    /* crypted boot data */
diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c
deleted file mode 100644 (file)
index 7307921..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-/* $Id: hysdn_procconf.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
- *
- * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
- *
- * Copyright 1999  by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/cred.h>
-#include <linux/module.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <net/net_namespace.h>
-
-#include "hysdn_defs.h"
-
-static DEFINE_MUTEX(hysdn_conf_mutex);
-
-#define INFO_OUT_LEN 80                /* length of info line including lf */
-
-/********************************************************/
-/* defines and data structure for conf write operations */
-/********************************************************/
-#define CONF_STATE_DETECT 0    /* waiting for detect */
-#define CONF_STATE_CONF   1    /* writing config data */
-#define CONF_STATE_POF    2    /* writing pof data */
-#define CONF_LINE_LEN   255    /* 255 chars max */
-
-struct conf_writedata {
-       hysdn_card *card;       /* card the device is connected to */
-       int buf_size;           /* actual number of bytes in the buffer */
-       int needed_size;        /* needed size when reading pof */
-       int state;              /* actual interface states from above constants */
-       unsigned char conf_line[CONF_LINE_LEN]; /* buffered conf line */
-       unsigned short channel;         /* active channel number */
-       unsigned char *pof_buffer;      /* buffer when writing pof */
-};
-
-/***********************************************************************/
-/* process_line parses one config line and transfers it to the card if */
-/* necessary.                                                          */
-/* if the return value is negative an error occurred.                   */
-/***********************************************************************/
-static int
-process_line(struct conf_writedata *cnf)
-{
-       unsigned char *cp = cnf->conf_line;
-       int i;
-
-       if (cnf->card->debug_flags & LOG_CNF_LINE)
-               hysdn_addlog(cnf->card, "conf line: %s", cp);
-
-       if (*cp == '-') {       /* option */
-               cp++;           /* point to option char */
-
-               if (*cp++ != 'c')
-                       return (0);     /* option unknown or used */
-               i = 0;          /* start value for channel */
-               while ((*cp <= '9') && (*cp >= '0'))
-                       i = i * 10 + *cp++ - '0';       /* get decimal number */
-               if (i > 65535) {
-                       if (cnf->card->debug_flags & LOG_CNF_MISC)
-                               hysdn_addlog(cnf->card, "conf channel invalid  %d", i);
-                       return (-ERR_INV_CHAN);         /* invalid channel */
-               }
-               cnf->channel = i & 0xFFFF;      /* set new channel number */
-               return (0);     /* success */
-       }                       /* option */
-       if (*cp == '*') {       /* line to send */
-               if (cnf->card->debug_flags & LOG_CNF_DATA)
-                       hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
-               return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
-                                        cnf->channel));        /* send the line without * */
-       }                       /* line to send */
-       return (0);
-}                              /* process_line */
-
-/***********************************/
-/* conf file operations and tables */
-/***********************************/
-
-/****************************************************/
-/* write conf file -> boot or send cfg line to card */
-/****************************************************/
-static ssize_t
-hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
-{
-       struct conf_writedata *cnf;
-       int i;
-       unsigned char ch, *cp;
-
-       if (!count)
-               return (0);     /* nothing to handle */
-
-       if (!(cnf = file->private_data))
-               return (-EFAULT);       /* should never happen */
-
-       if (cnf->state == CONF_STATE_DETECT) {  /* auto detect cnf or pof data */
-               if (copy_from_user(&ch, buf, 1))        /* get first char for detect */
-                       return (-EFAULT);
-
-               if (ch == 0x1A) {
-                       /* we detected a pof file */
-                       if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
-                               return (cnf->needed_size);      /* an error occurred -> exit */
-                       cnf->buf_size = 0;      /* buffer is empty */
-                       cnf->state = CONF_STATE_POF;    /* new state */
-               } else {
-                       /* conf data has been detected */
-                       cnf->buf_size = 0;      /* buffer is empty */
-                       cnf->state = CONF_STATE_CONF;   /* requested conf data write */
-                       if (cnf->card->state != CARD_STATE_RUN)
-                               return (-ERR_NOT_BOOTED);
-                       cnf->conf_line[CONF_LINE_LEN - 1] = 0;  /* limit string length */
-                       cnf->channel = 4098;    /* default channel for output */
-               }
-       }                       /* state was auto detect */
-       if (cnf->state == CONF_STATE_POF) {     /* pof write active */
-               i = cnf->needed_size - cnf->buf_size;   /* bytes still missing for write */
-               if (i <= 0)
-                       return (-EINVAL);       /* size error handling pof */
-
-               if (i < count)
-                       count = i;      /* limit requested number of bytes */
-               if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
-                       return (-EFAULT);       /* error while copying */
-               cnf->buf_size += count;
-
-               if (cnf->needed_size == cnf->buf_size) {
-                       cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size);  /* write data */
-                       if (cnf->needed_size <= 0) {
-                               cnf->card->state = CARD_STATE_BOOTERR;  /* show boot error */
-                               return (cnf->needed_size);      /* an error occurred */
-                       }
-                       cnf->buf_size = 0;      /* buffer is empty again */
-               }
-       }
-       /* pof write active */
-       else {                  /* conf write active */
-
-               if (cnf->card->state != CARD_STATE_RUN) {
-                       if (cnf->card->debug_flags & LOG_CNF_MISC)
-                               hysdn_addlog(cnf->card, "cnf write denied -> not booted");
-                       return (-ERR_NOT_BOOTED);
-               }
-               i = (CONF_LINE_LEN - 1) - cnf->buf_size;        /* bytes available in buffer */
-               if (i > 0) {
-                       /* copy remaining bytes into buffer */
-
-                       if (count > i)
-                               count = i;      /* limit transfer */
-                       if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
-                               return (-EFAULT);       /* error while copying */
-
-                       i = count;      /* number of chars in buffer */
-                       cp = cnf->conf_line + cnf->buf_size;
-                       while (i) {
-                               /* search for end of line */
-                               if ((*cp < ' ') && (*cp != 9))
-                                       break;  /* end of line found */
-                               cp++;
-                               i--;
-                       }       /* search for end of line */
-
-                       if (i) {
-                               /* delimiter found */
-                               *cp++ = 0;      /* string termination */
-                               count -= (i - 1);       /* subtract remaining bytes from count */
-                               while ((i) && (*cp < ' ') && (*cp != 9)) {
-                                       i--;    /* discard next char */
-                                       count++;        /* mark as read */
-                                       cp++;   /* next char */
-                               }
-                               cnf->buf_size = 0;      /* buffer is empty after transfer */
-                               if ((i = process_line(cnf)) < 0)        /* handle the line */
-                                       count = i;      /* return the error */
-                       }
-                       /* delimiter found */
-                       else {
-                               cnf->buf_size += count;         /* add chars to string */
-                               if (cnf->buf_size >= CONF_LINE_LEN - 1) {
-                                       if (cnf->card->debug_flags & LOG_CNF_MISC)
-                                               hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
-                                       return (-ERR_CONF_LONG);
-                               }
-                       }       /* not delimited */
-
-               }
-               /* copy remaining bytes into buffer */
-               else {
-                       if (cnf->card->debug_flags & LOG_CNF_MISC)
-                               hysdn_addlog(cnf->card, "cnf line too long");
-                       return (-ERR_CONF_LONG);
-               }
-       }                       /* conf write active */
-
-       return (count);
-}                              /* hysdn_conf_write */
-
-/*******************************************/
-/* read conf file -> output card info data */
-/*******************************************/
-static ssize_t
-hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t *off)
-{
-       char *cp;
-
-       if (!(file->f_mode & FMODE_READ))
-               return -EPERM;  /* no permission to read */
-
-       if (!(cp = file->private_data))
-               return -EFAULT; /* should never happen */
-
-       return simple_read_from_buffer(buf, count, off, cp, strlen(cp));
-}                              /* hysdn_conf_read */
-
-/******************/
-/* open conf file */
-/******************/
-static int
-hysdn_conf_open(struct inode *ino, struct file *filep)
-{
-       hysdn_card *card;
-       struct conf_writedata *cnf;
-       char *cp, *tmp;
-
-       /* now search the addressed card */
-       mutex_lock(&hysdn_conf_mutex);
-       card = PDE_DATA(ino);
-       if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
-               hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
-                            filep->f_cred->fsuid, filep->f_cred->fsgid,
-                            filep->f_mode);
-
-       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
-               /* write only access -> write boot file or conf line */
-
-               if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
-                       mutex_unlock(&hysdn_conf_mutex);
-                       return (-EFAULT);
-               }
-               cnf->card = card;
-               cnf->buf_size = 0;      /* nothing buffered */
-               cnf->state = CONF_STATE_DETECT;         /* start auto detect */
-               filep->private_data = cnf;
-
-       } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
-               /* read access -> output card info data */
-
-               if (!(tmp = kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
-                       mutex_unlock(&hysdn_conf_mutex);
-                       return (-EFAULT);       /* out of memory */
-               }
-               filep->private_data = tmp;      /* start of string */
-
-               /* first output a headline */
-               sprintf(tmp, "id bus slot type irq iobase dp-mem     b-chans fax-chans state device");
-               cp = tmp;       /* start of string */
-               while (*cp)
-                       cp++;
-               while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
-                       *cp++ = ' ';
-               *cp++ = '\n';
-
-               /* and now the data */
-               sprintf(cp, "%d  %3d %4d %4d %3d 0x%04x 0x%08lx %7d %9d %3d   %s",
-                       card->myid,
-                       card->bus,
-                       PCI_SLOT(card->devfn),
-                       card->brdtype,
-                       card->irq,
-                       card->iobase,
-                       card->membase,
-                       card->bchans,
-                       card->faxchans,
-                       card->state,
-                       hysdn_net_getname(card));
-               while (*cp)
-                       cp++;
-               while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
-                       *cp++ = ' ';
-               *cp++ = '\n';
-               *cp = 0;        /* end of string */
-       } else {                /* simultaneous read/write access forbidden ! */
-               mutex_unlock(&hysdn_conf_mutex);
-               return (-EPERM);        /* no permission this time */
-       }
-       mutex_unlock(&hysdn_conf_mutex);
-       return nonseekable_open(ino, filep);
-}                              /* hysdn_conf_open */
-
-/***************************/
-/* close a config file.    */
-/***************************/
-static int
-hysdn_conf_close(struct inode *ino, struct file *filep)
-{
-       hysdn_card *card;
-       struct conf_writedata *cnf;
-       int retval = 0;
-
-       mutex_lock(&hysdn_conf_mutex);
-       card = PDE_DATA(ino);
-       if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
-               hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
-                            filep->f_cred->fsuid, filep->f_cred->fsgid,
-                            filep->f_mode);
-
-       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
-               /* write only access -> write boot file or conf line */
-               if (filep->private_data) {
-                       cnf = filep->private_data;
-
-                       if (cnf->state == CONF_STATE_POF)
-                               retval = pof_write_close(cnf->card);    /* close the pof write */
-                       kfree(filep->private_data);     /* free allocated memory for buffer */
-
-               }               /* handle write private data */
-       } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
-               /* read access -> output card info data */
-
-               kfree(filep->private_data);     /* release memory */
-       }
-       mutex_unlock(&hysdn_conf_mutex);
-       return (retval);
-}                              /* hysdn_conf_close */
-
-/******************************************************/
-/* table for conf filesystem functions defined above. */
-/******************************************************/
-static const struct file_operations conf_fops =
-{
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .read           = hysdn_conf_read,
-       .write          = hysdn_conf_write,
-       .open           = hysdn_conf_open,
-       .release        = hysdn_conf_close,
-};
-
-/*****************************/
-/* hysdn subdir in /proc/net */
-/*****************************/
-struct proc_dir_entry *hysdn_proc_entry = NULL;
-
-/*******************************************************************************/
-/* hysdn_procconf_init is called when the module is loaded and after the cards */
-/* have been detected. The needed proc dir and card config files are created.  */
-/* The log init is called at last.                                             */
-/*******************************************************************************/
-int
-hysdn_procconf_init(void)
-{
-       hysdn_card *card;
-       unsigned char conf_name[20];
-
-       hysdn_proc_entry = proc_mkdir(PROC_SUBDIR_NAME, init_net.proc_net);
-       if (!hysdn_proc_entry) {
-               printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
-               return (-1);
-       }
-       card = card_root;       /* point to first card */
-       while (card) {
-
-               sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
-               if ((card->procconf = (void *) proc_create_data(conf_name,
-                                                          S_IFREG | S_IRUGO | S_IWUSR,
-                                                          hysdn_proc_entry,
-                                                          &conf_fops,
-                                                          card)) != NULL) {
-                       hysdn_proclog_init(card);       /* init the log file entry */
-               }
-               card = card->next;      /* next entry */
-       }
-
-       printk(KERN_NOTICE "HYSDN: procfs initialised\n");
-       return (0);
-}                              /* hysdn_procconf_init */
-
-/*************************************************************************************/
-/* hysdn_procconf_release is called when the module is unloaded and before the cards */
-/* resources are released. The module counter is assumed to be 0 !                   */
-/*************************************************************************************/
-void
-hysdn_procconf_release(void)
-{
-       hysdn_card *card;
-       unsigned char conf_name[20];
-
-       card = card_root;       /* start with first card */
-       while (card) {
-
-               sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
-               if (card->procconf)
-                       remove_proc_entry(conf_name, hysdn_proc_entry);
-
-               hysdn_proclog_release(card);    /* init the log file entry */
-
-               card = card->next;      /* point to next card */
-       }
-
-       remove_proc_entry(PROC_SUBDIR_NAME, init_net.proc_net);
-}
diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c
deleted file mode 100644 (file)
index 6e898b9..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-/* $Id: hysdn_proclog.c,v 1.9.6.3 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards, /proc/net filesystem log functions.
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/kernel.h>
-
-#include "hysdn_defs.h"
-
-/* the proc subdir for the interface is defined in the procconf module */
-extern struct proc_dir_entry *hysdn_proc_entry;
-
-static DEFINE_MUTEX(hysdn_log_mutex);
-static void put_log_buffer(hysdn_card *card, char *cp);
-
-/*************************************************/
-/* structure keeping ascii log for device output */
-/*************************************************/
-struct log_data {
-       struct log_data *next;
-       unsigned long usage_cnt;/* number of files still to work */
-       void *proc_ctrl;        /* pointer to own control procdata structure */
-       char log_start[2];      /* log string start (final len aligned by size) */
-};
-
-/**********************************************/
-/* structure holding proc entrys for one card */
-/**********************************************/
-struct procdata {
-       struct proc_dir_entry *log;     /* log entry */
-       char log_name[15];      /* log filename */
-       struct log_data *log_head, *log_tail;   /* head and tail for queue */
-       int if_used;            /* open count for interface */
-       unsigned char logtmp[LOG_MAX_LINELEN];
-       wait_queue_head_t rd_queue;
-};
-
-
-/**********************************************/
-/* log function for cards error log interface */
-/**********************************************/
-void
-hysdn_card_errlog(hysdn_card *card, tErrLogEntry *logp, int maxsize)
-{
-       char buf[ERRLOG_TEXT_SIZE + 40];
-
-       sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
-       put_log_buffer(card, buf);      /* output the string */
-}                              /* hysdn_card_errlog */
-
-/***************************************************/
-/* Log function using format specifiers for output */
-/***************************************************/
-void
-hysdn_addlog(hysdn_card *card, char *fmt, ...)
-{
-       struct procdata *pd = card->proclog;
-       char *cp;
-       va_list args;
-
-       if (!pd)
-               return;         /* log structure non existent */
-
-       cp = pd->logtmp;
-       cp += sprintf(cp, "HYSDN: card %d ", card->myid);
-
-       va_start(args, fmt);
-       cp += vsprintf(cp, fmt, args);
-       va_end(args);
-       *cp++ = '\n';
-       *cp = 0;
-
-       if (card->debug_flags & DEB_OUT_SYSLOG)
-               printk(KERN_INFO "%s", pd->logtmp);
-       else
-               put_log_buffer(card, pd->logtmp);
-
-}                              /* hysdn_addlog */
-
-/********************************************/
-/* put an log buffer into the log queue.    */
-/* This buffer will be kept until all files */
-/* opened for read got the contents.        */
-/* Flushes buffers not longer in use.       */
-/********************************************/
-static void
-put_log_buffer(hysdn_card *card, char *cp)
-{
-       struct log_data *ib;
-       struct procdata *pd = card->proclog;
-       unsigned long flags;
-
-       if (!pd)
-               return;
-       if (!cp)
-               return;
-       if (!*cp)
-               return;
-       if (pd->if_used <= 0)
-               return;         /* no open file for read */
-
-       if (!(ib = kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
-               return; /* no memory */
-       strcpy(ib->log_start, cp);      /* set output string */
-       ib->next = NULL;
-       ib->proc_ctrl = pd;     /* point to own control structure */
-       spin_lock_irqsave(&card->hysdn_lock, flags);
-       ib->usage_cnt = pd->if_used;
-       if (!pd->log_head)
-               pd->log_head = ib;      /* new head */
-       else
-               pd->log_tail->next = ib;        /* follows existing messages */
-       pd->log_tail = ib;      /* new tail */
-
-       /* delete old entrys */
-       while (pd->log_head->next) {
-               if ((pd->log_head->usage_cnt <= 0) &&
-                   (pd->log_head->next->usage_cnt <= 0)) {
-                       ib = pd->log_head;
-                       pd->log_head = pd->log_head->next;
-                       kfree(ib);
-               } else {
-                       break;
-               }
-       }               /* pd->log_head->next */
-
-       spin_unlock_irqrestore(&card->hysdn_lock, flags);
-
-       wake_up_interruptible(&(pd->rd_queue));         /* announce new entry */
-}                              /* put_log_buffer */
-
-
-/******************************/
-/* file operations and tables */
-/******************************/
-
-/****************************************/
-/* write log file -> set log level bits */
-/****************************************/
-static ssize_t
-hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
-{
-       int rc;
-       hysdn_card *card = file->private_data;
-
-       rc = kstrtoul_from_user(buf, count, 0, &card->debug_flags);
-       if (rc < 0)
-               return rc;
-       hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
-       return (count);
-}                              /* hysdn_log_write */
-
-/******************/
-/* read log file */
-/******************/
-static ssize_t
-hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t *off)
-{
-       struct log_data *inf;
-       int len;
-       hysdn_card *card = PDE_DATA(file_inode(file));
-
-       if (!(inf = *((struct log_data **) file->private_data))) {
-               struct procdata *pd = card->proclog;
-               if (file->f_flags & O_NONBLOCK)
-                       return (-EAGAIN);
-
-               wait_event_interruptible(pd->rd_queue, (inf =
-                               *((struct log_data **) file->private_data)));
-       }
-       if (!inf)
-               return (0);
-
-       inf->usage_cnt--;       /* new usage count */
-       file->private_data = &inf->next;        /* next structure */
-       if ((len = strlen(inf->log_start)) <= count) {
-               if (copy_to_user(buf, inf->log_start, len))
-                       return -EFAULT;
-               *off += len;
-               return (len);
-       }
-       return (0);
-}                              /* hysdn_log_read */
-
-/******************/
-/* open log file */
-/******************/
-static int
-hysdn_log_open(struct inode *ino, struct file *filep)
-{
-       hysdn_card *card = PDE_DATA(ino);
-
-       mutex_lock(&hysdn_log_mutex);
-       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
-               /* write only access -> write log level only */
-               filep->private_data = card;     /* remember our own card */
-       } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
-               struct procdata *pd = card->proclog;
-               unsigned long flags;
-
-               /* read access -> log/debug read */
-               spin_lock_irqsave(&card->hysdn_lock, flags);
-               pd->if_used++;
-               if (pd->log_head)
-                       filep->private_data = &pd->log_tail->next;
-               else
-                       filep->private_data = &pd->log_head;
-               spin_unlock_irqrestore(&card->hysdn_lock, flags);
-       } else {                /* simultaneous read/write access forbidden ! */
-               mutex_unlock(&hysdn_log_mutex);
-               return (-EPERM);        /* no permission this time */
-       }
-       mutex_unlock(&hysdn_log_mutex);
-       return nonseekable_open(ino, filep);
-}                              /* hysdn_log_open */
-
-/*******************************************************************************/
-/* close a cardlog file. If the file has been opened for exclusive write it is */
-/* assumed as pof data input and the pof loader is noticed about.              */
-/* Otherwise file is handled as log output. In this case the interface usage   */
-/* count is decremented and all buffers are noticed of closing. If this file   */
-/* was the last one to be closed, all buffers are freed.                       */
-/*******************************************************************************/
-static int
-hysdn_log_close(struct inode *ino, struct file *filep)
-{
-       struct log_data *inf;
-       struct procdata *pd;
-       hysdn_card *card;
-       int retval = 0;
-
-       mutex_lock(&hysdn_log_mutex);
-       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
-               /* write only access -> write debug level written */
-               retval = 0;     /* success */
-       } else {
-               /* read access -> log/debug read, mark one further file as closed */
-
-               inf = *((struct log_data **) filep->private_data);      /* get first log entry */
-               if (inf)
-                       pd = (struct procdata *) inf->proc_ctrl;        /* still entries there */
-               else {
-                       /* no info available -> search card */
-                       card = PDE_DATA(file_inode(filep));
-                       pd = card->proclog;     /* pointer to procfs log */
-               }
-               if (pd)
-                       pd->if_used--;  /* decrement interface usage count by one */
-
-               while (inf) {
-                       inf->usage_cnt--;       /* decrement usage count for buffers */
-                       inf = inf->next;
-               }
-
-               if (pd)
-                       if (pd->if_used <= 0)   /* delete buffers if last file closed */
-                               while (pd->log_head) {
-                                       inf = pd->log_head;
-                                       pd->log_head = pd->log_head->next;
-                                       kfree(inf);
-                               }
-       }                       /* read access */
-       mutex_unlock(&hysdn_log_mutex);
-
-       return (retval);
-}                              /* hysdn_log_close */
-
-/*************************************************/
-/* select/poll routine to be able using select() */
-/*************************************************/
-static __poll_t
-hysdn_log_poll(struct file *file, poll_table *wait)
-{
-       __poll_t mask = 0;
-       hysdn_card *card = PDE_DATA(file_inode(file));
-       struct procdata *pd = card->proclog;
-
-       if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
-               return (mask);  /* no polling for write supported */
-
-       poll_wait(file, &(pd->rd_queue), wait);
-
-       if (*((struct log_data **) file->private_data))
-               mask |= EPOLLIN | EPOLLRDNORM;
-
-       return mask;
-}                              /* hysdn_log_poll */
-
-/**************************************************/
-/* table for log filesystem functions defined above. */
-/**************************************************/
-static const struct file_operations log_fops =
-{
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .read           = hysdn_log_read,
-       .write          = hysdn_log_write,
-       .poll           = hysdn_log_poll,
-       .open           = hysdn_log_open,
-       .release        = hysdn_log_close,
-};
-
-
-/***********************************************************************************/
-/* hysdn_proclog_init is called when the module is loaded after creating the cards */
-/* conf files.                                                                     */
-/***********************************************************************************/
-int
-hysdn_proclog_init(hysdn_card *card)
-{
-       struct procdata *pd;
-
-       /* create a cardlog proc entry */
-
-       if ((pd = kzalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
-               sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
-               pd->log = proc_create_data(pd->log_name,
-                                     S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry,
-                                     &log_fops, card);
-
-               init_waitqueue_head(&(pd->rd_queue));
-
-               card->proclog = (void *) pd;    /* remember procfs structure */
-       }
-       return (0);
-}                              /* hysdn_proclog_init */
-
-/************************************************************************************/
-/* hysdn_proclog_release is called when the module is unloaded and before the cards */
-/* conf file is released                                                            */
-/* The module counter is assumed to be 0 !                                          */
-/************************************************************************************/
-void
-hysdn_proclog_release(hysdn_card *card)
-{
-       struct procdata *pd;
-
-       if ((pd = (struct procdata *) card->proclog) != NULL) {
-               if (pd->log)
-                       remove_proc_entry(pd->log_name, hysdn_proc_entry);
-               kfree(pd);      /* release memory */
-               card->proclog = NULL;
-       }
-}                              /* hysdn_proclog_release */
diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
deleted file mode 100644 (file)
index 31d7c14..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $
- *
- * Linux driver for HYSDN cards
- * scheduler routines for handling exchange card <-> pc.
- *
- * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/signal.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <asm/io.h>
-
-#include "hysdn_defs.h"
-
-/*****************************************************************************/
-/* hysdn_sched_rx is called from the cards handler to announce new data is   */
-/* available from the card. The routine has to handle the data and return    */
-/* with a nonzero code if the data could be worked (or even thrown away), if */
-/* no room to buffer the data is available a zero return tells the card      */
-/* to keep the data until later.                                             */
-/*****************************************************************************/
-int
-hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len,
-              unsigned short chan)
-{
-
-       switch (chan) {
-       case CHAN_NDIS_DATA:
-               if (hynet_enable & (1 << card->myid)) {
-                       /* give packet to network handler */
-                       hysdn_rx_netpkt(card, buf, len);
-               }
-               break;
-
-       case CHAN_ERRLOG:
-               hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
-               if (card->err_log_state == ERRLOG_STATE_ON)
-                       card->err_log_state = ERRLOG_STATE_START;       /* start new fetch */
-               break;
-#ifdef CONFIG_HYSDN_CAPI
-       case CHAN_CAPI:
-/* give packet to CAPI handler */
-               if (hycapi_enable & (1 << card->myid)) {
-                       hycapi_rx_capipkt(card, buf, len);
-               }
-               break;
-#endif /* CONFIG_HYSDN_CAPI */
-       default:
-               printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
-               break;
-
-       }                       /* switch rx channel */
-
-       return (1);             /* always handled */
-}                              /* hysdn_sched_rx */
-
-/*****************************************************************************/
-/* hysdn_sched_tx is called from the cards handler to announce that there is */
-/* room in the tx-buffer to the card and data may be sent if needed.         */
-/* If the routine wants to send data it must fill buf, len and chan with the */
-/* appropriate data and return a nonzero value. With a zero return no new    */
-/* data to send is assumed. maxlen specifies the buffer size available for   */
-/* sending.                                                                  */
-/*****************************************************************************/
-int
-hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
-              unsigned short volatile *len, unsigned short volatile *chan,
-              unsigned short maxlen)
-{
-       struct sk_buff *skb;
-
-       if (card->net_tx_busy) {
-               card->net_tx_busy = 0;  /* reset flag */
-               hysdn_tx_netack(card);  /* acknowledge packet send */
-       }                       /* a network packet has completely been transferred */
-       /* first of all async requests are handled */
-       if (card->async_busy) {
-               if (card->async_len <= maxlen) {
-                       memcpy(buf, card->async_data, card->async_len);
-                       *len = card->async_len;
-                       *chan = card->async_channel;
-                       card->async_busy = 0;   /* reset request */
-                       return (1);
-               }
-               card->async_busy = 0;   /* in case of length error */
-       }                       /* async request */
-       if ((card->err_log_state == ERRLOG_STATE_START) &&
-           (maxlen >= ERRLOG_CMD_REQ_SIZE)) {
-               strcpy(buf, ERRLOG_CMD_REQ);    /* copy the command */
-               *len = ERRLOG_CMD_REQ_SIZE;     /* buffer length */
-               *chan = CHAN_ERRLOG;    /* and channel */
-               card->err_log_state = ERRLOG_STATE_ON;  /* new state is on */
-               return (1);     /* tell that data should be send */
-       }                       /* error log start and able to send */
-       if ((card->err_log_state == ERRLOG_STATE_STOP) &&
-           (maxlen >= ERRLOG_CMD_STOP_SIZE)) {
-               strcpy(buf, ERRLOG_CMD_STOP);   /* copy the command */
-               *len = ERRLOG_CMD_STOP_SIZE;    /* buffer length */
-               *chan = CHAN_ERRLOG;    /* and channel */
-               card->err_log_state = ERRLOG_STATE_OFF;         /* new state is off */
-               return (1);     /* tell that data should be send */
-       }                       /* error log start and able to send */
-       /* now handle network interface packets */
-       if ((hynet_enable & (1 << card->myid)) &&
-           (skb = hysdn_tx_netget(card)) != NULL)
-       {
-               if (skb->len <= maxlen) {
-                       /* copy the packet to the buffer */
-                       skb_copy_from_linear_data(skb, buf, skb->len);
-                       *len = skb->len;
-                       *chan = CHAN_NDIS_DATA;
-                       card->net_tx_busy = 1;  /* we are busy sending network data */
-                       return (1);     /* go and send the data */
-               } else
-                       hysdn_tx_netack(card);  /* aknowledge packet -> throw away */
-       }                       /* send a network packet if available */
-#ifdef CONFIG_HYSDN_CAPI
-       if (((hycapi_enable & (1 << card->myid))) &&
-           ((skb = hycapi_tx_capiget(card)) != NULL))
-       {
-               if (skb->len <= maxlen) {
-                       skb_copy_from_linear_data(skb, buf, skb->len);
-                       *len = skb->len;
-                       *chan = CHAN_CAPI;
-                       hycapi_tx_capiack(card);
-                       return (1);     /* go and send the data */
-               }
-       }
-#endif /* CONFIG_HYSDN_CAPI */
-       return (0);             /* nothing to send */
-}                              /* hysdn_sched_tx */
-
-
-/*****************************************************************************/
-/* send one config line to the card and return 0 if successful, otherwise a */
-/* negative error code.                                                      */
-/* The function works with timeouts perhaps not giving the greatest speed    */
-/* sending the line, but this should be meaningless because only some lines  */
-/* are to be sent and this happens very seldom.                              */
-/*****************************************************************************/
-int
-hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan)
-{
-       int cnt = 50;           /* timeout intervalls */
-       unsigned long flags;
-
-       if (card->debug_flags & LOG_SCHED_ASYN)
-               hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
-
-       while (card->async_busy) {
-
-               if (card->debug_flags & LOG_SCHED_ASYN)
-                       hysdn_addlog(card, "async tx-cfg delayed");
-
-               msleep_interruptible(20);               /* Timeout 20ms */
-               if (!--cnt)
-                       return (-ERR_ASYNC_TIME);       /* timed out */
-       }                       /* wait for buffer to become free */
-
-       spin_lock_irqsave(&card->hysdn_lock, flags);
-       strcpy(card->async_data, line);
-       card->async_len = strlen(line) + 1;
-       card->async_channel = chan;
-       card->async_busy = 1;   /* request transfer */
-
-       /* now queue the task */
-       schedule_work(&card->irq_queue);
-       spin_unlock_irqrestore(&card->hysdn_lock, flags);
-
-       if (card->debug_flags & LOG_SCHED_ASYN)
-               hysdn_addlog(card, "async tx-cfg data queued");
-
-       cnt++;                  /* short delay */
-
-       while (card->async_busy) {
-
-               if (card->debug_flags & LOG_SCHED_ASYN)
-                       hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
-
-               msleep_interruptible(20);               /* Timeout 20ms */
-               if (!--cnt)
-                       return (-ERR_ASYNC_TIME);       /* timed out */
-       }                       /* wait for buffer to become free again */
-
-       if (card->debug_flags & LOG_SCHED_ASYN)
-               hysdn_addlog(card, "async tx-cfg data send");
-
-       return (0);             /* line send correctly */
-}                              /* hysdn_tx_cfgline */
diff --git a/drivers/isdn/hysdn/ince1pc.h b/drivers/isdn/hysdn/ince1pc.h
deleted file mode 100644 (file)
index cab6836..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Linux driver for HYSDN cards
- * common definitions for both sides of the bus:
- * - conventions both spoolers must know
- * - channel numbers agreed upon
- *
- * Author    M. Steinkopf
- * Copyright 1999 by M. Steinkopf
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#ifndef __INCE1PC_H__
-#define __INCE1PC_H__
-
-/*  basic scalar definitions have same meanning,
- *  but their declaration location depends on environment
- */
-
-/*--------------------------------------channel numbers---------------------*/
-#define CHAN_SYSTEM     0x0001      /* system channel (spooler to spooler) */
-#define CHAN_ERRLOG     0x0005      /* error logger */
-#define CHAN_CAPI       0x0064      /* CAPI interface */
-#define CHAN_NDIS_DATA  0x1001      /* NDIS data transfer */
-
-/*--------------------------------------POF ready msg-----------------------*/
-/* NOTE: after booting POF sends system ready message to PC: */
-#define RDY_MAGIC       0x52535953UL    /* 'SYSR' reversed */
-#define RDY_MAGIC_SIZE  4               /* size in bytes */
-
-#define MAX_N_TOK_BYTES 255
-
-#define MIN_RDY_MSG_SIZE    RDY_MAGIC_SIZE
-#define MAX_RDY_MSG_SIZE    (RDY_MAGIC_SIZE + MAX_N_TOK_BYTES)
-
-#define SYSR_TOK_END            0
-#define SYSR_TOK_B_CHAN         1   /* nr. of B-Channels;   DataLen=1; def: 2 */
-#define SYSR_TOK_FAX_CHAN       2   /* nr. of FAX Channels; DataLen=1; def: 0 */
-#define SYSR_TOK_MAC_ADDR       3   /* MAC-Address; DataLen=6; def: auto */
-#define SYSR_TOK_ESC            255 /* undefined data size yet */
-/* default values, if not corrected by token: */
-#define SYSR_TOK_B_CHAN_DEF     2   /* assume 2 B-Channels */
-#define SYSR_TOK_FAX_CHAN_DEF   1   /* assume 1 FAX Channel */
-
-/*  syntax of new SYSR token stream:
- *  channel: CHAN_SYSTEM
- *  msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
- *           RDY_MAGIC_SIZE   <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
- *  msg    : 0 1 2 3 {4 5 6 ..}
- *           S Y S R  MAX_N_TOK_BYTES bytes of TokenStream
- *
- *  TokenStream     :=   empty
- *                     | {NonEndTokenChunk} EndToken RotlCRC
- *  NonEndTokenChunk:= NonEndTokenId DataLen [Data]
- *  NonEndTokenId   := 0x01 .. 0xFE                 1 BYTE
- *  DataLen         := 0x00 .. 0xFF                 1 BYTE
- *  Data            := DataLen bytes
- *  EndToken        := 0x00
- *  RotlCRC         := special 1 byte CRC over all NonEndTokenChunk bytes
- *                     s. RotlCRC algorithm
- *
- *  RotlCRC algorithm:
- *      ucSum= 0                        1 unsigned char
- *      for all NonEndTokenChunk bytes:
- *          ROTL(ucSum,1)               rotate left by 1
- *          ucSum += Char;              add current byte with swap around
- *      RotlCRC= ~ucSum;                invert all bits for result
- *
- *  note:
- *  - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
- */
-
-/*--------------------------------------error logger------------------------*/
-/* note: pof needs final 0 ! */
-#define ERRLOG_CMD_REQ          "ERRLOG ON"
-#define ERRLOG_CMD_REQ_SIZE     10              /* with final 0 byte ! */
-#define ERRLOG_CMD_STOP         "ERRLOG OFF"
-#define ERRLOG_CMD_STOP_SIZE    11              /* with final 0 byte ! */
-
-#define ERRLOG_ENTRY_SIZE       64      /* sizeof(tErrLogEntry) */
-                                       /* remaining text size = 55 */
-#define ERRLOG_TEXT_SIZE    (ERRLOG_ENTRY_SIZE - 2 * 4 - 1)
-
-typedef struct ErrLogEntry_tag {
-
-       /*00 */ unsigned long ulErrType;
-
-       /*04 */ unsigned long ulErrSubtype;
-
-       /*08 */ unsigned char ucTextSize;
-
-       /*09 */ unsigned char ucText[ERRLOG_TEXT_SIZE];
-       /* ASCIIZ of len ucTextSize-1 */
-
-/*40 */
-} tErrLogEntry;
-
-
-#if defined(__TURBOC__)
-#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
-#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
-#endif                         /*  */
-#endif                         /*  */
-
-/*--------------------------------------DPRAM boot spooler------------------*/
-/*  this is the struture used between pc and
- *  hyperstone to exchange boot data
- */
-#define DPRAM_SPOOLER_DATA_SIZE 0x20
-typedef struct DpramBootSpooler_tag {
-
-       /*00 */ unsigned char Len;
-
-       /*01 */ volatile unsigned char RdPtr;
-
-       /*02 */ unsigned char WrPtr;
-
-       /*03 */ unsigned char Data[DPRAM_SPOOLER_DATA_SIZE];
-
-/*23 */
-} tDpramBootSpooler;
-
-
-#define DPRAM_SPOOLER_MIN_SIZE  5       /* Len+RdPtr+Wrptr+2*data */
-#define DPRAM_SPOOLER_DEF_SIZE  0x23    /* current default size   */
-
-/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/
-/* at DPRAM offset 0x1C00: */
-#define SIZE_RSV_SOFT_UART  0x1B0   /* 432 bytes reserved for SoftUart */
-
-
-#endif /* __INCE1PC_H__ */
index d5f771f..7c96a01 100644 (file)
@@ -118,4 +118,6 @@ source "drivers/staging/fieldbus/Kconfig"
 
 source "drivers/staging/kpc2000/Kconfig"
 
+source "drivers/staging/isdn/Kconfig"
+
 endif # STAGING
index 0da0d3f..fcaac96 100644 (file)
@@ -49,3 +49,4 @@ obj-$(CONFIG_XIL_AXIS_FIFO)   += axis-fifo/
 obj-$(CONFIG_EROFS_FS)         += erofs/
 obj-$(CONFIG_FIELDBUS_DEV)     += fieldbus/
 obj-$(CONFIG_KPC2000)          += kpc2000/
+obj-$(CONFIG_ISDN_CAPI)                += isdn/
diff --git a/drivers/staging/isdn/Kconfig b/drivers/staging/isdn/Kconfig
new file mode 100644 (file)
index 0000000..faaf638
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "ISDN CAPI drivers"
+       depends on ISDN_CAPI
+
+source "drivers/staging/isdn/avm/Kconfig"
+
+source "drivers/staging/isdn/gigaset/Kconfig"
+
+source "drivers/staging/isdn/hysdn/Kconfig"
+
+endmenu
+
diff --git a/drivers/staging/isdn/Makefile b/drivers/staging/isdn/Makefile
new file mode 100644 (file)
index 0000000..025504b
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for the kernel ISDN subsystem and device drivers.
+
+# Object files in subdirectories
+
+obj-$(CONFIG_CAPI_AVM)                 += avm/
+obj-$(CONFIG_HYSDN)                    += hysdn/
+obj-$(CONFIG_ISDN_DRV_GIGASET)         += gigaset/
diff --git a/drivers/staging/isdn/TODO b/drivers/staging/isdn/TODO
new file mode 100644 (file)
index 0000000..9210d11
--- /dev/null
@@ -0,0 +1,22 @@
+TODO: Remove in late 2019 unless there are users
+
+
+I tried to find any indication of whether the capi drivers are
+still in use, and have not found anything  from a long time ago.
+
+With public ISDN networks almost completely shut down over the past 12
+months, there is very little you can actually do with this hardware. The
+main remaining use case would be to connect ISDN voice phones to an
+in-house installation with Asterisk or LCR, but anyone trying this in
+turn seems to be using either the mISDN driver stack, or out-of-tree
+drivers from the hardware vendors.
+
+I may of course have missed something, so I would suggest moving
+these into drivers/staging/ just in case someone still uses one
+of the three remaining in-kernel drivers (avm, hysdn, gigaset).
+
+If nobody complains, we can remove them entirely in six months,
+or otherwise move the core code and any drivers that are still
+needed back into drivers/isdn.
+
+  Arnd Bergmann <arnd@arndb.de>
diff --git a/drivers/staging/isdn/avm/Kconfig b/drivers/staging/isdn/avm/Kconfig
new file mode 100644 (file)
index 0000000..81483db
--- /dev/null
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ISDN AVM drivers
+#
+
+menuconfig CAPI_AVM
+       bool "Active AVM cards"
+       help
+         Enable support for AVM active ISDN cards.
+
+if CAPI_AVM
+
+config ISDN_DRV_AVMB1_B1ISA
+       tristate "AVM B1 ISA support"
+       depends on ISA
+       help
+         Enable support for the ISA version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_B1PCI
+       tristate "AVM B1 PCI support"
+       depends on PCI
+       help
+         Enable support for the PCI version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_B1PCIV4
+       bool "AVM B1 PCI V4 support"
+       depends on ISDN_DRV_AVMB1_B1PCI
+       help
+         Enable support for the V4 version of AVM B1 PCI card.
+
+config ISDN_DRV_AVMB1_T1ISA
+       tristate "AVM T1/T1-B ISA support"
+       depends on ISA
+       help
+         Enable support for the AVM T1 T1B card.
+         Note: This is a PRI card and handle 30 B-channels.
+
+config ISDN_DRV_AVMB1_B1PCMCIA
+       tristate "AVM B1/M1/M2 PCMCIA support"
+       depends on PCMCIA
+       help
+         Enable support for the PCMCIA version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_AVM_CS
+       tristate "AVM B1/M1/M2 PCMCIA cs module"
+       depends on ISDN_DRV_AVMB1_B1PCMCIA
+       help
+         Enable the PCMCIA client driver for the AVM B1/M1/M2
+         PCMCIA cards.
+
+config ISDN_DRV_AVMB1_T1PCI
+       tristate "AVM T1/T1-B PCI support"
+       depends on PCI
+       help
+         Enable support for the AVM T1 T1B card.
+         Note: This is a PRI card and handle 30 B-channels.
+
+config ISDN_DRV_AVMB1_C4
+       tristate "AVM C4/C2 support"
+       depends on PCI
+       help
+         Enable support for the AVM C4/C2 PCI cards.
+         These cards handle 4/2 BRI ISDN lines (8/4 channels).
+
+endif # CAPI_AVM
diff --git a/drivers/staging/isdn/avm/Makefile b/drivers/staging/isdn/avm/Makefile
new file mode 100644 (file)
index 0000000..3830a05
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for the AVM ISDN device drivers
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1ISA)     += b1isa.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCI)     += b1pci.o b1.o b1dma.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCMCIA)  += b1pcmcia.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_AVM_CS)    += avm_cs.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_T1ISA)     += t1isa.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_T1PCI)     += t1pci.o b1.o b1dma.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_C4)                += c4.o b1.o
diff --git a/drivers/staging/isdn/avm/avm_cs.c b/drivers/staging/isdn/avm/avm_cs.c
new file mode 100644 (file)
index 0000000..62b8030
--- /dev/null
@@ -0,0 +1,166 @@
+/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
+ *
+ * A PCMCIA client driver for AVM B1/M1/M2
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <asm/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <linux/skbuff.h>
+#include <linux/capi.h>
+#include <linux/b1lli.h>
+#include <linux/b1pcmcia.h>
+
+/*====================================================================*/
+
+MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/*====================================================================*/
+
+static int avmcs_config(struct pcmcia_device *link);
+static void avmcs_release(struct pcmcia_device *link);
+static void avmcs_detach(struct pcmcia_device *p_dev);
+
+static int avmcs_probe(struct pcmcia_device *p_dev)
+{
+       /* General socket configuration */
+       p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+       p_dev->config_index = 1;
+       p_dev->config_regs = PRESENT_OPTION;
+
+       return avmcs_config(p_dev);
+} /* avmcs_attach */
+
+
+static void avmcs_detach(struct pcmcia_device *link)
+{
+       avmcs_release(link);
+} /* avmcs_detach */
+
+static int avmcs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+       p_dev->resource[0]->end = 16;
+       p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+       p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+       return pcmcia_request_io(p_dev);
+}
+
+static int avmcs_config(struct pcmcia_device *link)
+{
+       int i = -1;
+       char devname[128];
+       int cardtype;
+       int (*addcard)(unsigned int port, unsigned irq);
+
+       devname[0] = 0;
+       if (link->prod_id[1])
+               strlcpy(devname, link->prod_id[1], sizeof(devname));
+
+       /*
+        * find IO port
+        */
+       if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
+               return -ENODEV;
+
+       do {
+               if (!link->irq) {
+                       /* undo */
+                       pcmcia_disable_device(link);
+                       break;
+               }
+
+               /*
+                * configure the PCMCIA socket
+                */
+               i = pcmcia_enable_device(link);
+               if (i != 0) {
+                       pcmcia_disable_device(link);
+                       break;
+               }
+
+       } while (0);
+
+       if (devname[0]) {
+               char *s = strrchr(devname, ' ');
+               if (!s)
+                       s = devname;
+               else s++;
+               if (strcmp("M1", s) == 0) {
+                       cardtype = AVM_CARDTYPE_M1;
+               } else if (strcmp("M2", s) == 0) {
+                       cardtype = AVM_CARDTYPE_M2;
+               } else {
+                       cardtype = AVM_CARDTYPE_B1;
+               }
+       } else
+               cardtype = AVM_CARDTYPE_B1;
+
+       /* If any step failed, release any partially configured state */
+       if (i != 0) {
+               avmcs_release(link);
+               return -ENODEV;
+       }
+
+
+       switch (cardtype) {
+       case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
+       case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
+       default:
+       case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
+       }
+       if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
+               dev_err(&link->dev,
+                       "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
+                       (unsigned int) link->resource[0]->start, link->irq);
+               avmcs_release(link);
+               return -ENODEV;
+       }
+       return 0;
+
+} /* avmcs_config */
+
+
+static void avmcs_release(struct pcmcia_device *link)
+{
+       b1pcmcia_delcard(link->resource[0]->start, link->irq);
+       pcmcia_disable_device(link);
+} /* avmcs_release */
+
+
+static const struct pcmcia_device_id avmcs_ids[] = {
+       PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
+       PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
+       PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
+       PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
+
+static struct pcmcia_driver avmcs_driver = {
+       .owner  = THIS_MODULE,
+       .name           = "avm_cs",
+       .probe = avmcs_probe,
+       .remove = avmcs_detach,
+       .id_table = avmcs_ids,
+};
+module_pcmcia_driver(avmcs_driver);
diff --git a/drivers/staging/isdn/avm/avmcard.h b/drivers/staging/isdn/avm/avmcard.h
new file mode 100644 (file)
index 0000000..cdfa89c
--- /dev/null
@@ -0,0 +1,581 @@
+/* $Id: avmcard.h,v 1.1.4.1.2.1 2001/12/21 15:00:17 kai Exp $
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _AVMCARD_H_
+#define _AVMCARD_H_
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#define        AVMB1_PORTLEN           0x1f
+#define AVM_MAXVERSION         8
+#define AVM_NCCI_PER_CHANNEL   4
+
+/*
+ * Versions
+ */
+
+#define        VER_DRIVER      0
+#define        VER_CARDTYPE    1
+#define        VER_HWID        2
+#define        VER_SERIAL      3
+#define        VER_OPTION      4
+#define        VER_PROTO       5
+#define        VER_PROFILE     6
+#define        VER_CAPI        7
+
+enum avmcardtype {
+       avm_b1isa,
+       avm_b1pci,
+       avm_b1pcmcia,
+       avm_m1,
+       avm_m2,
+       avm_t1isa,
+       avm_t1pci,
+       avm_c4,
+       avm_c2
+};
+
+typedef struct avmcard_dmabuf {
+       long        size;
+       u8       *dmabuf;
+       dma_addr_t  dmaaddr;
+} avmcard_dmabuf;
+
+typedef struct avmcard_dmainfo {
+       u32                recvlen;
+       avmcard_dmabuf       recvbuf;
+
+       avmcard_dmabuf       sendbuf;
+       struct sk_buff_head  send_queue;
+
+       struct pci_dev      *pcidev;
+} avmcard_dmainfo;
+
+typedef        struct avmctrl_info {
+       char cardname[32];
+
+       int versionlen;
+       char versionbuf[1024];
+       char *version[AVM_MAXVERSION];
+
+       char infobuf[128];      /* for function procinfo */
+
+       struct avmcard  *card;
+       struct capi_ctr  capi_ctrl;
+
+       struct list_head ncci_head;
+} avmctrl_info;
+
+typedef struct avmcard {
+       char name[32];
+
+       spinlock_t lock;
+       unsigned int port;
+       unsigned irq;
+       unsigned long membase;
+       enum avmcardtype cardtype;
+       unsigned char revision;
+       unsigned char class;
+       int cardnr; /* for t1isa */
+
+       char msgbuf[128];       /* capimsg msg part */
+       char databuf[2048];     /* capimsg data part */
+
+       void __iomem *mbase;
+       volatile u32 csr;
+       avmcard_dmainfo *dma;
+
+       struct avmctrl_info *ctrlinfo;
+
+       u_int nr_controllers;
+       u_int nlogcontr;
+       struct list_head list;
+} avmcard;
+
+extern int b1_irq_table[16];
+
+/*
+ * LLI Messages to the ISDN-ControllerISDN Controller
+ */
+
+#define        SEND_POLL               0x72    /*
+                                        * after load <- RECEIVE_POLL
+                                        */
+#define SEND_INIT              0x11    /*
+                                        * first message <- RECEIVE_INIT
+                                        * int32 NumApplications  int32
+                                        * NumNCCIs int32 BoardNumber
+                                        */
+#define SEND_REGISTER          0x12    /*
+                                        * register an application int32
+                                        * ApplIDId int32 NumMessages
+                                        * int32 NumB3Connections int32
+                                        * NumB3Blocks int32 B3Size
+                                        *
+                                        * AnzB3Connection != 0 &&
+                                        * AnzB3Blocks >= 1 && B3Size >= 1
+                                        */
+#define SEND_RELEASE           0x14    /*
+                                        * deregister an application int32
+                                        * ApplID
+                                        */
+#define SEND_MESSAGE           0x15    /*
+                                        * send capi-message int32 length
+                                        * capi-data ...
+                                        */
+#define SEND_DATA_B3_REQ       0x13    /*
+                                        * send capi-data-message int32
+                                        * MsgLength capi-data ... int32
+                                        * B3Length data ....
+                                        */
+
+#define SEND_CONFIG            0x21    /*
+                                        */
+
+#define SEND_POLLACK           0x73    /* T1 Watchdog */
+
+/*
+ * LLI Messages from the ISDN-ControllerISDN Controller
+ */
+
+#define RECEIVE_POLL           0x32    /*
+                                        * <- after SEND_POLL
+                                        */
+#define RECEIVE_INIT           0x27    /*
+                                        * <- after SEND_INIT int32 length
+                                        * byte total length b1struct board
+                                        * driver revision b1struct card
+                                        * type b1struct reserved b1struct
+                                        * serial number b1struct driver
+                                        * capability b1struct d-channel
+                                        * protocol b1struct CAPI-2.0
+                                        * profile b1struct capi version
+                                        */
+#define RECEIVE_MESSAGE                0x21    /*
+                                        * <- after SEND_MESSAGE int32
+                                        * AppllID int32 Length capi-data
+                                        * ....
+                                        */
+#define RECEIVE_DATA_B3_IND    0x22    /*
+                                        * received data int32 AppllID
+                                        * int32 Length capi-data ...
+                                        * int32 B3Length data ...
+                                        */
+#define RECEIVE_START          0x23    /*
+                                        * Handshake
+                                        */
+#define RECEIVE_STOP           0x24    /*
+                                        * Handshake
+                                        */
+#define RECEIVE_NEW_NCCI       0x25    /*
+                                        * int32 AppllID int32 NCCI int32
+                                        * WindowSize
+                                        */
+#define RECEIVE_FREE_NCCI      0x26    /*
+                                        * int32 AppllID int32 NCCI
+                                        */
+#define RECEIVE_RELEASE                0x26    /*
+                                        * int32 AppllID int32 0xffffffff
+                                        */
+#define RECEIVE_TASK_READY     0x31    /*
+                                        * int32 tasknr
+                                        * int32 Length Taskname ...
+                                        */
+#define RECEIVE_DEBUGMSG       0x71    /*
+                                        * int32 Length message
+                                        *
+                                        */
+#define RECEIVE_POLLDWORD      0x75    /* t1pci in dword mode */
+
+#define WRITE_REGISTER         0x00
+#define READ_REGISTER          0x01
+
+/*
+ * port offsets
+ */
+
+#define B1_READ                        0x00
+#define B1_WRITE               0x01
+#define B1_INSTAT              0x02
+#define B1_OUTSTAT             0x03
+#define B1_ANALYSE             0x04
+#define B1_REVISION            0x05
+#define B1_RESET               0x10
+
+
+#define B1_STAT0(cardtype)  ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l)
+#define B1_STAT1(cardtype)  (0x80E00000l)
+
+/* ---------------------------------------------------------------- */
+
+static inline unsigned char b1outp(unsigned int base,
+                                  unsigned short offset,
+                                  unsigned char value)
+{
+       outb(value, base + offset);
+       return inb(base + B1_ANALYSE);
+}
+
+
+static inline int b1_rx_full(unsigned int base)
+{
+       return inb(base + B1_INSTAT) & 0x1;
+}
+
+static inline unsigned char b1_get_byte(unsigned int base)
+{
+       unsigned long stop = jiffies + 1 * HZ;  /* maximum wait time 1 sec */
+       while (!b1_rx_full(base) && time_before(jiffies, stop));
+       if (b1_rx_full(base))
+               return inb(base + B1_READ);
+       printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
+       return 0;
+}
+
+static inline unsigned int b1_get_word(unsigned int base)
+{
+       unsigned int val = 0;
+       val |= b1_get_byte(base);
+       val |= (b1_get_byte(base) << 8);
+       val |= (b1_get_byte(base) << 16);
+       val |= (b1_get_byte(base) << 24);
+       return val;
+}
+
+static inline int b1_tx_empty(unsigned int base)
+{
+       return inb(base + B1_OUTSTAT) & 0x1;
+}
+
+static inline void b1_put_byte(unsigned int base, unsigned char val)
+{
+       while (!b1_tx_empty(base));
+       b1outp(base, B1_WRITE, val);
+}
+
+static inline int b1_save_put_byte(unsigned int base, unsigned char val)
+{
+       unsigned long stop = jiffies + 2 * HZ;
+       while (!b1_tx_empty(base) && time_before(jiffies, stop));
+       if (!b1_tx_empty(base)) return -1;
+       b1outp(base, B1_WRITE, val);
+       return 0;
+}
+
+static inline void b1_put_word(unsigned int base, unsigned int val)
+{
+       b1_put_byte(base, val & 0xff);
+       b1_put_byte(base, (val >> 8) & 0xff);
+       b1_put_byte(base, (val >> 16) & 0xff);
+       b1_put_byte(base, (val >> 24) & 0xff);
+}
+
+static inline unsigned int b1_get_slice(unsigned int base,
+                                       unsigned char *dp)
+{
+       unsigned int len, i;
+
+       len = i = b1_get_word(base);
+       while (i-- > 0) *dp++ = b1_get_byte(base);
+       return len;
+}
+
+static inline void b1_put_slice(unsigned int base,
+                               unsigned char *dp, unsigned int len)
+{
+       unsigned i = len;
+       b1_put_word(base, i);
+       while (i-- > 0)
+               b1_put_byte(base, *dp++);
+}
+
+static void b1_wr_reg(unsigned int base,
+                     unsigned int reg,
+                     unsigned int value)
+{
+       b1_put_byte(base, WRITE_REGISTER);
+       b1_put_word(base, reg);
+       b1_put_word(base, value);
+}
+
+static inline unsigned int b1_rd_reg(unsigned int base,
+                                    unsigned int reg)
+{
+       b1_put_byte(base, READ_REGISTER);
+       b1_put_word(base, reg);
+       return b1_get_word(base);
+
+}
+
+static inline void b1_reset(unsigned int base)
+{
+       b1outp(base, B1_RESET, 0);
+       mdelay(55 * 2); /* 2 TIC's */
+
+       b1outp(base, B1_RESET, 1);
+       mdelay(55 * 2); /* 2 TIC's */
+
+       b1outp(base, B1_RESET, 0);
+       mdelay(55 * 2); /* 2 TIC's */
+}
+
+static inline unsigned char b1_disable_irq(unsigned int base)
+{
+       return b1outp(base, B1_INSTAT, 0x00);
+}
+
+/* ---------------------------------------------------------------- */
+
+static inline void b1_set_test_bit(unsigned int base,
+                                  enum avmcardtype cardtype,
+                                  int onoff)
+{
+       b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
+}
+
+static inline int b1_get_test_bit(unsigned int base,
+                                 enum avmcardtype cardtype)
+{
+       return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+#define T1_FASTLINK            0x00
+#define T1_SLOWLINK            0x08
+
+#define T1_READ                        B1_READ
+#define T1_WRITE               B1_WRITE
+#define T1_INSTAT              B1_INSTAT
+#define T1_OUTSTAT             B1_OUTSTAT
+#define T1_IRQENABLE           0x05
+#define T1_FIFOSTAT            0x06
+#define T1_RESETLINK           0x10
+#define T1_ANALYSE             0x11
+#define T1_IRQMASTER           0x12
+#define T1_IDENT               0x17
+#define T1_RESETBOARD          0x1f
+
+#define        T1F_IREADY              0x01
+#define        T1F_IHALF               0x02
+#define        T1F_IFULL               0x04
+#define        T1F_IEMPTY              0x08
+#define        T1F_IFLAGS              0xF0
+
+#define        T1F_OREADY              0x10
+#define        T1F_OHALF               0x20
+#define        T1F_OEMPTY              0x40
+#define        T1F_OFULL               0x80
+#define        T1F_OFLAGS              0xF0
+
+/* there are HEMA cards with 1k and 4k FIFO out */
+#define FIFO_OUTBSIZE          256
+#define FIFO_INPBSIZE          512
+
+#define HEMA_VERSION_ID                0
+#define HEMA_PAL_ID            0
+
+static inline void t1outp(unsigned int base,
+                         unsigned short offset,
+                         unsigned char value)
+{
+       outb(value, base + offset);
+}
+
+static inline unsigned char t1inp(unsigned int base,
+                                 unsigned short offset)
+{
+       return inb(base + offset);
+}
+
+static inline int t1_isfastlink(unsigned int base)
+{
+       return (inb(base + T1_IDENT) & ~0x82) == 1;
+}
+
+static inline unsigned char t1_fifostatus(unsigned int base)
+{
+       return inb(base + T1_FIFOSTAT);
+}
+
+static inline unsigned int t1_get_slice(unsigned int base,
+                                       unsigned char *dp)
+{
+       unsigned int len, i;
+#ifdef FASTLINK_DEBUG
+       unsigned wcnt = 0, bcnt = 0;
+#endif
+
+       len = i = b1_get_word(base);
+       if (t1_isfastlink(base)) {
+               int status;
+               while (i > 0) {
+                       status = t1_fifostatus(base) & (T1F_IREADY | T1F_IHALF);
+                       if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
+
+                       switch (status) {
+                       case T1F_IREADY | T1F_IHALF | T1F_IFULL:
+                               insb(base + B1_READ, dp, FIFO_INPBSIZE);
+                               dp += FIFO_INPBSIZE;
+                               i -= FIFO_INPBSIZE;
+#ifdef FASTLINK_DEBUG
+                               wcnt += FIFO_INPBSIZE;
+#endif
+                               break;
+                       case T1F_IREADY | T1F_IHALF:
+                               insb(base + B1_READ, dp, i);
+#ifdef FASTLINK_DEBUG
+                               wcnt += i;
+#endif
+                               dp += i;
+                               i = 0;
+                               break;
+                       default:
+                               *dp++ = b1_get_byte(base);
+                               i--;
+#ifdef FASTLINK_DEBUG
+                               bcnt++;
+#endif
+                               break;
+                       }
+               }
+#ifdef FASTLINK_DEBUG
+               if (wcnt)
+                       printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
+                              base, len, wcnt, bcnt);
+#endif
+       } else {
+               while (i-- > 0)
+                       *dp++ = b1_get_byte(base);
+       }
+       return len;
+}
+
+static inline void t1_put_slice(unsigned int base,
+                               unsigned char *dp, unsigned int len)
+{
+       unsigned i = len;
+       b1_put_word(base, i);
+       if (t1_isfastlink(base)) {
+               int status;
+               while (i > 0) {
+                       status = t1_fifostatus(base) & (T1F_OREADY | T1F_OHALF);
+                       if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
+                       switch (status) {
+                       case T1F_OREADY | T1F_OHALF | T1F_OEMPTY:
+                               outsb(base + B1_WRITE, dp, FIFO_OUTBSIZE);
+                               dp += FIFO_OUTBSIZE;
+                               i -= FIFO_OUTBSIZE;
+                               break;
+                       case T1F_OREADY | T1F_OHALF:
+                               outsb(base + B1_WRITE, dp, i);
+                               dp += i;
+                               i = 0;
+                               break;
+                       default:
+                               b1_put_byte(base, *dp++);
+                               i--;
+                               break;
+                       }
+               }
+       } else {
+               while (i-- > 0)
+                       b1_put_byte(base, *dp++);
+       }
+}
+
+static inline void t1_disable_irq(unsigned int base)
+{
+       t1outp(base, T1_IRQMASTER, 0x00);
+}
+
+static inline void t1_reset(unsigned int base)
+{
+       /* reset T1 Controller */
+       b1_reset(base);
+       /* disable irq on HEMA */
+       t1outp(base, B1_INSTAT, 0x00);
+       t1outp(base, B1_OUTSTAT, 0x00);
+       t1outp(base, T1_IRQMASTER, 0x00);
+       /* reset HEMA board configuration */
+       t1outp(base, T1_RESETBOARD, 0xf);
+}
+
+static inline void b1_setinterrupt(unsigned int base, unsigned irq,
+                                  enum avmcardtype cardtype)
+{
+       switch (cardtype) {
+       case avm_t1isa:
+               t1outp(base, B1_INSTAT, 0x00);
+               t1outp(base, B1_INSTAT, 0x02);
+               t1outp(base, T1_IRQMASTER, 0x08);
+               break;
+       case avm_b1isa:
+               b1outp(base, B1_INSTAT, 0x00);
+               b1outp(base, B1_RESET, b1_irq_table[irq]);
+               b1outp(base, B1_INSTAT, 0x02);
+               break;
+       default:
+       case avm_m1:
+       case avm_m2:
+       case avm_b1pci:
+               b1outp(base, B1_INSTAT, 0x00);
+               b1outp(base, B1_RESET, 0xf0);
+               b1outp(base, B1_INSTAT, 0x02);
+               break;
+       case avm_c4:
+       case avm_t1pci:
+               b1outp(base, B1_RESET, 0xf0);
+               break;
+       }
+}
+
+/* b1.c */
+avmcard *b1_alloc_card(int nr_controllers);
+void b1_free_card(avmcard *card);
+int b1_detect(unsigned int base, enum avmcardtype cardtype);
+void b1_getrevision(avmcard *card);
+int b1_load_t4file(avmcard *card, capiloaddatapart *t4file);
+int b1_load_config(avmcard *card, capiloaddatapart *config);
+int b1_loaded(avmcard *card);
+
+int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
+void b1_reset_ctr(struct capi_ctr *ctrl);
+void b1_register_appl(struct capi_ctr *ctrl, u16 appl,
+                     capi_register_params *rp);
+void b1_release_appl(struct capi_ctr *ctrl, u16 appl);
+u16  b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+void b1_parse_version(avmctrl_info *card);
+irqreturn_t b1_interrupt(int interrupt, void *devptr);
+
+int b1_proc_show(struct seq_file *m, void *v);
+
+avmcard_dmainfo *avmcard_dma_alloc(char *name, struct pci_dev *,
+                                  long rsize, long ssize);
+void avmcard_dma_free(avmcard_dmainfo *);
+
+/* b1dma.c */
+int b1pciv4_detect(avmcard *card);
+int t1pci_detect(avmcard *card);
+void b1dma_reset(avmcard *card);
+irqreturn_t b1dma_interrupt(int interrupt, void *devptr);
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
+void b1dma_reset_ctr(struct capi_ctr *ctrl);
+void b1dma_remove_ctr(struct capi_ctr *ctrl);
+void b1dma_register_appl(struct capi_ctr *ctrl,
+                        u16 appl,
+                        capi_register_params *rp);
+void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl);
+u16  b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+int b1dma_proc_show(struct seq_file *m, void *v);
+
+#endif /* _AVMCARD_H_ */
diff --git a/drivers/staging/isdn/avm/b1.c b/drivers/staging/isdn/avm/b1.c
new file mode 100644 (file)
index 0000000..40ca1e8
--- /dev/null
@@ -0,0 +1,804 @@
+/* $Id: b1.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Common module for AVM B1 cards.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+int b1_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 192,                          /* irq 3 */
+ 32,                           /* irq 4 */
+ 160,                          /* irq 5 */
+ 96,                           /* irq 6 */
+ 224,                          /* irq 7 */
+ 0,
+ 64,                           /* irq 9 */
+ 80,                           /* irq 10 */
+ 208,                          /* irq 11 */
+ 48,                           /* irq 12 */
+ 0,
+ 0,
+ 112,                          /* irq 15 */
+};
+
+/* ------------------------------------------------------------- */
+
+avmcard *b1_alloc_card(int nr_controllers)
+{
+       avmcard *card;
+       avmctrl_info *cinfo;
+       int i;
+
+       card = kzalloc(sizeof(*card), GFP_KERNEL);
+       if (!card)
+               return NULL;
+
+       cinfo = kcalloc(nr_controllers, sizeof(*cinfo), GFP_KERNEL);
+       if (!cinfo) {
+               kfree(card);
+               return NULL;
+       }
+
+       card->ctrlinfo = cinfo;
+       for (i = 0; i < nr_controllers; i++) {
+               INIT_LIST_HEAD(&cinfo[i].ncci_head);
+               cinfo[i].card = card;
+       }
+       spin_lock_init(&card->lock);
+       card->nr_controllers = nr_controllers;
+
+       return card;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1_free_card(avmcard *card)
+{
+       kfree(card->ctrlinfo);
+       kfree(card);
+}
+
+/* ------------------------------------------------------------- */
+
+int b1_detect(unsigned int base, enum avmcardtype cardtype)
+{
+       int onoff, i;
+
+       /*
+        * Statusregister 0000 00xx
+        */
+       if ((inb(base + B1_INSTAT) & 0xfc)
+           || (inb(base + B1_OUTSTAT) & 0xfc))
+               return 1;
+       /*
+        * Statusregister 0000 001x
+        */
+       b1outp(base, B1_INSTAT, 0x2);   /* enable irq */
+       /* b1outp(base, B1_OUTSTAT, 0x2); */
+       if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
+           /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
+               return 2;
+       /*
+        * Statusregister 0000 000x
+        */
+       b1outp(base, B1_INSTAT, 0x0);   /* disable irq */
+       b1outp(base, B1_OUTSTAT, 0x0);
+       if ((inb(base + B1_INSTAT) & 0xfe)
+           || (inb(base + B1_OUTSTAT) & 0xfe))
+               return 3;
+
+       for (onoff = !0, i = 0; i < 10; i++) {
+               b1_set_test_bit(base, cardtype, onoff);
+               if (b1_get_test_bit(base, cardtype) != onoff)
+                       return 4;
+               onoff = !onoff;
+       }
+
+       if (cardtype == avm_m1)
+               return 0;
+
+       if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
+               return 5;
+
+       return 0;
+}
+
+void b1_getrevision(avmcard *card)
+{
+       card->class = inb(card->port + B1_ANALYSE);
+       card->revision = inb(card->port + B1_REVISION);
+}
+
+#define FWBUF_SIZE     256
+int b1_load_t4file(avmcard *card, capiloaddatapart *t4file)
+{
+       unsigned char buf[FWBUF_SIZE];
+       unsigned char *dp;
+       int i, left;
+       unsigned int base = card->port;
+
+       dp = t4file->data;
+       left = t4file->len;
+       while (left > FWBUF_SIZE) {
+               if (t4file->user) {
+                       if (copy_from_user(buf, dp, FWBUF_SIZE))
+                               return -EFAULT;
+               } else {
+                       memcpy(buf, dp, FWBUF_SIZE);
+               }
+               for (i = 0; i < FWBUF_SIZE; i++)
+                       if (b1_save_put_byte(base, buf[i]) < 0) {
+                               printk(KERN_ERR "%s: corrupted firmware file ?\n",
+                                      card->name);
+                               return -EIO;
+                       }
+               left -= FWBUF_SIZE;
+               dp += FWBUF_SIZE;
+       }
+       if (left) {
+               if (t4file->user) {
+                       if (copy_from_user(buf, dp, left))
+                               return -EFAULT;
+               } else {
+                       memcpy(buf, dp, left);
+               }
+               for (i = 0; i < left; i++)
+                       if (b1_save_put_byte(base, buf[i]) < 0) {
+                               printk(KERN_ERR "%s: corrupted firmware file ?\n",
+                                      card->name);
+                               return -EIO;
+                       }
+       }
+       return 0;
+}
+
+int b1_load_config(avmcard *card, capiloaddatapart *config)
+{
+       unsigned char buf[FWBUF_SIZE];
+       unsigned char *dp;
+       unsigned int base = card->port;
+       int i, j, left;
+
+       dp = config->data;
+       left = config->len;
+       if (left) {
+               b1_put_byte(base, SEND_CONFIG);
+               b1_put_word(base, 1);
+               b1_put_byte(base, SEND_CONFIG);
+               b1_put_word(base, left);
+       }
+       while (left > FWBUF_SIZE) {
+               if (config->user) {
+                       if (copy_from_user(buf, dp, FWBUF_SIZE))
+                               return -EFAULT;
+               } else {
+                       memcpy(buf, dp, FWBUF_SIZE);
+               }
+               for (i = 0; i < FWBUF_SIZE; ) {
+                       b1_put_byte(base, SEND_CONFIG);
+                       for (j = 0; j < 4; j++) {
+                               b1_put_byte(base, buf[i++]);
+                       }
+               }
+               left -= FWBUF_SIZE;
+               dp += FWBUF_SIZE;
+       }
+       if (left) {
+               if (config->user) {
+                       if (copy_from_user(buf, dp, left))
+                               return -EFAULT;
+               } else {
+                       memcpy(buf, dp, left);
+               }
+               for (i = 0; i < left; ) {
+                       b1_put_byte(base, SEND_CONFIG);
+                       for (j = 0; j < 4; j++) {
+                               if (i < left)
+                                       b1_put_byte(base, buf[i++]);
+                               else
+                                       b1_put_byte(base, 0);
+                       }
+               }
+       }
+       return 0;
+}
+
+int b1_loaded(avmcard *card)
+{
+       unsigned int base = card->port;
+       unsigned long stop;
+       unsigned char ans;
+       unsigned long tout = 2;
+
+       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+               if (b1_tx_empty(base))
+                       break;
+       }
+       if (!b1_tx_empty(base)) {
+               printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n",
+                      card->name);
+               return 0;
+       }
+       b1_put_byte(base, SEND_POLL);
+       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+               if (b1_rx_full(base)) {
+                       if ((ans = b1_get_byte(base)) == RECEIVE_POLL) {
+                               return 1;
+                       }
+                       printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n",
+                              card->name, ans);
+                       return 0;
+               }
+       }
+       printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name);
+       return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+       int retval;
+
+       b1_reset(port);
+
+       if ((retval = b1_load_t4file(card, &data->firmware))) {
+               b1_reset(port);
+               printk(KERN_ERR "%s: failed to load t4file!!\n",
+                      card->name);
+               return retval;
+       }
+
+       b1_disable_irq(port);
+
+       if (data->configuration.len > 0 && data->configuration.data) {
+               if ((retval = b1_load_config(card, &data->configuration))) {
+                       b1_reset(port);
+                       printk(KERN_ERR "%s: failed to load config!!\n",
+                              card->name);
+                       return retval;
+               }
+       }
+
+       if (!b1_loaded(card)) {
+               printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+               return -EIO;
+       }
+
+       spin_lock_irqsave(&card->lock, flags);
+       b1_setinterrupt(port, card->irq, card->cardtype);
+       b1_put_byte(port, SEND_INIT);
+       b1_put_word(port, CAPI_MAXAPPL);
+       b1_put_word(port, AVM_NCCI_PER_CHANNEL * 2);
+       b1_put_word(port, ctrl->cnr - 1);
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       return 0;
+}
+
+void b1_reset_ctr(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+
+       b1_reset(port);
+       b1_reset(port);
+
+       memset(cinfo->version, 0, sizeof(cinfo->version));
+       spin_lock_irqsave(&card->lock, flags);
+       capilib_release(&cinfo->ncci_head);
+       spin_unlock_irqrestore(&card->lock, flags);
+       capi_ctr_down(ctrl);
+}
+
+void b1_register_appl(struct capi_ctr *ctrl,
+                     u16 appl,
+                     capi_register_params *rp)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+       int nconn, want = rp->level3cnt;
+
+       if (want > 0) nconn = want;
+       else nconn = ctrl->profile.nbchannel * -want;
+       if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+       spin_lock_irqsave(&card->lock, flags);
+       b1_put_byte(port, SEND_REGISTER);
+       b1_put_word(port, appl);
+       b1_put_word(port, 1024 * (nconn + 1));
+       b1_put_word(port, nconn);
+       b1_put_word(port, rp->datablkcnt);
+       b1_put_word(port, rp->datablklen);
+       spin_unlock_irqrestore(&card->lock, flags);
+}
+
+void b1_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+       capilib_release_appl(&cinfo->ncci_head, appl);
+       b1_put_byte(port, SEND_RELEASE);
+       b1_put_word(port, appl);
+       spin_unlock_irqrestore(&card->lock, flags);
+}
+
+u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+       u16 len = CAPIMSG_LEN(skb->data);
+       u8 cmd = CAPIMSG_COMMAND(skb->data);
+       u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+       u16 dlen, retval;
+
+       spin_lock_irqsave(&card->lock, flags);
+       if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+               retval = capilib_data_b3_req(&cinfo->ncci_head,
+                                            CAPIMSG_APPID(skb->data),
+                                            CAPIMSG_NCCI(skb->data),
+                                            CAPIMSG_MSGID(skb->data));
+               if (retval != CAPI_NOERROR) {
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       return retval;
+               }
+
+               dlen = CAPIMSG_DATALEN(skb->data);
+
+               b1_put_byte(port, SEND_DATA_B3_REQ);
+               b1_put_slice(port, skb->data, len);
+               b1_put_slice(port, skb->data + len, dlen);
+       } else {
+               b1_put_byte(port, SEND_MESSAGE);
+               b1_put_slice(port, skb->data, len);
+       }
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       dev_kfree_skb_any(skb);
+       return CAPI_NOERROR;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1_parse_version(avmctrl_info *cinfo)
+{
+       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+       avmcard *card = cinfo->card;
+       capi_profile *profp;
+       u8 *dversion;
+       u8 flag;
+       int i, j;
+
+       for (j = 0; j < AVM_MAXVERSION; j++)
+               cinfo->version[j] = "";
+       for (i = 0, j = 0;
+            j < AVM_MAXVERSION && i < cinfo->versionlen;
+            j++, i += cinfo->versionbuf[i] + 1)
+               cinfo->version[j] = &cinfo->versionbuf[i + 1];
+
+       strlcpy(ctrl->serial, cinfo->version[VER_SERIAL], sizeof(ctrl->serial));
+       memcpy(&ctrl->profile, cinfo->version[VER_PROFILE], sizeof(capi_profile));
+       strlcpy(ctrl->manu, "AVM GmbH", sizeof(ctrl->manu));
+       dversion = cinfo->version[VER_DRIVER];
+       ctrl->version.majorversion = 2;
+       ctrl->version.minorversion = 0;
+       ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
+       ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf);
+       ctrl->version.minormanuversion = (dversion[3] - '0') << 4;
+       ctrl->version.minormanuversion |=
+               (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
+
+       profp = &ctrl->profile;
+
+       flag = ((u8 *)(profp->manu))[1];
+       switch (flag) {
+       case 0: if (cinfo->version[VER_CARDTYPE])
+                       strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]);
+               else strcpy(cinfo->cardname, "B1");
+               break;
+       case 3: strcpy(cinfo->cardname, "PCMCIA B"); break;
+       case 4: strcpy(cinfo->cardname, "PCMCIA M1"); break;
+       case 5: strcpy(cinfo->cardname, "PCMCIA M2"); break;
+       case 6: strcpy(cinfo->cardname, "B1 V3.0"); break;
+       case 7: strcpy(cinfo->cardname, "B1 PCI"); break;
+       default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break;
+       }
+       printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n",
+              card->name, ctrl->cnr, cinfo->cardname);
+
+       flag = ((u8 *)(profp->manu))[3];
+       if (flag)
+               printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n",
+                      card->name,
+                      ctrl->cnr,
+                      (flag & 0x01) ? " DSS1" : "",
+                      (flag & 0x02) ? " CT1" : "",
+                      (flag & 0x04) ? " VN3" : "",
+                      (flag & 0x08) ? " NI1" : "",
+                      (flag & 0x10) ? " AUSTEL" : "",
+                      (flag & 0x20) ? " ESS" : "",
+                      (flag & 0x40) ? " 1TR6" : ""
+                       );
+
+       flag = ((u8 *)(profp->manu))[5];
+       if (flag)
+               printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n",
+                      card->name,
+                      ctrl->cnr,
+                      (flag & 0x01) ? " point to point" : "",
+                      (flag & 0x02) ? " point to multipoint" : "",
+                      (flag & 0x08) ? " leased line without D-channel" : "",
+                      (flag & 0x04) ? " leased line with D-channel" : ""
+                       );
+}
+
+/* ------------------------------------------------------------- */
+
+irqreturn_t b1_interrupt(int interrupt, void *devptr)
+{
+       avmcard *card = devptr;
+       avmctrl_info *cinfo = &card->ctrlinfo[0];
+       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+       unsigned char b1cmd;
+       struct sk_buff *skb;
+
+       unsigned ApplId;
+       unsigned MsgLen;
+       unsigned DataB3Len;
+       unsigned NCCI;
+       unsigned WindowSize;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       if (!b1_rx_full(card->port)) {
+               spin_unlock_irqrestore(&card->lock, flags);
+               return IRQ_NONE;
+       }
+
+       b1cmd = b1_get_byte(card->port);
+
+       switch (b1cmd) {
+
+       case RECEIVE_DATA_B3_IND:
+
+               ApplId = (unsigned) b1_get_word(card->port);
+               MsgLen = b1_get_slice(card->port, card->msgbuf);
+               DataB3Len = b1_get_slice(card->port, card->databuf);
+               spin_unlock_irqrestore(&card->lock, flags);
+
+               if (MsgLen < 30) { /* not CAPI 64Bit */
+                       memset(card->msgbuf + MsgLen, 0, 30-MsgLen);
+                       MsgLen = 30;
+                       CAPIMSG_SETLEN(card->msgbuf, 30);
+               }
+               if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
+                       printk(KERN_ERR "%s: incoming packet dropped\n",
+                              card->name);
+               } else {
+                       skb_put_data(skb, card->msgbuf, MsgLen);
+                       skb_put_data(skb, card->databuf, DataB3Len);
+                       capi_ctr_handle_message(ctrl, ApplId, skb);
+               }
+               break;
+
+       case RECEIVE_MESSAGE:
+
+               ApplId = (unsigned) b1_get_word(card->port);
+               MsgLen = b1_get_slice(card->port, card->msgbuf);
+               if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+                       printk(KERN_ERR "%s: incoming packet dropped\n",
+                              card->name);
+                       spin_unlock_irqrestore(&card->lock, flags);
+               } else {
+                       skb_put_data(skb, card->msgbuf, MsgLen);
+                       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+                               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+                                                    CAPIMSG_NCCI(skb->data),
+                                                    CAPIMSG_MSGID(skb->data));
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       capi_ctr_handle_message(ctrl, ApplId, skb);
+               }
+               break;
+
+       case RECEIVE_NEW_NCCI:
+
+               ApplId = b1_get_word(card->port);
+               NCCI = b1_get_word(card->port);
+               WindowSize = b1_get_word(card->port);
+               capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+               spin_unlock_irqrestore(&card->lock, flags);
+               break;
+
+       case RECEIVE_FREE_NCCI:
+
+               ApplId = b1_get_word(card->port);
+               NCCI = b1_get_word(card->port);
+               if (NCCI != 0xffffffff)
+                       capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+               spin_unlock_irqrestore(&card->lock, flags);
+               break;
+
+       case RECEIVE_START:
+               /* b1_put_byte(card->port, SEND_POLLACK); */
+               spin_unlock_irqrestore(&card->lock, flags);
+               capi_ctr_resume_output(ctrl);
+               break;
+
+       case RECEIVE_STOP:
+               spin_unlock_irqrestore(&card->lock, flags);
+               capi_ctr_suspend_output(ctrl);
+               break;
+
+       case RECEIVE_INIT:
+
+               cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf);
+               spin_unlock_irqrestore(&card->lock, flags);
+               b1_parse_version(cinfo);
+               printk(KERN_INFO "%s: %s-card (%s) now active\n",
+                      card->name,
+                      cinfo->version[VER_CARDTYPE],
+                      cinfo->version[VER_DRIVER]);
+               capi_ctr_ready(ctrl);
+               break;
+
+       case RECEIVE_TASK_READY:
+               ApplId = (unsigned) b1_get_word(card->port);
+               MsgLen = b1_get_slice(card->port, card->msgbuf);
+               spin_unlock_irqrestore(&card->lock, flags);
+               card->msgbuf[MsgLen] = 0;
+               while (MsgLen > 0
+                      && (card->msgbuf[MsgLen - 1] == '\n'
+                          || card->msgbuf[MsgLen - 1] == '\r')) {
+                       card->msgbuf[MsgLen - 1] = 0;
+                       MsgLen--;
+               }
+               printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+                      card->name, ApplId, card->msgbuf);
+               break;
+
+       case RECEIVE_DEBUGMSG:
+               MsgLen = b1_get_slice(card->port, card->msgbuf);
+               spin_unlock_irqrestore(&card->lock, flags);
+               card->msgbuf[MsgLen] = 0;
+               while (MsgLen > 0
+                      && (card->msgbuf[MsgLen - 1] == '\n'
+                          || card->msgbuf[MsgLen - 1] == '\r')) {
+                       card->msgbuf[MsgLen - 1] = 0;
+                       MsgLen--;
+               }
+               printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+               break;
+
+       case 0xff:
+               spin_unlock_irqrestore(&card->lock, flags);
+               printk(KERN_ERR "%s: card removed ?\n", card->name);
+               return IRQ_NONE;
+       default:
+               spin_unlock_irqrestore(&card->lock, flags);
+               printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
+                      card->name, b1cmd);
+               return IRQ_HANDLED;
+       }
+       return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+int b1_proc_show(struct seq_file *m, void *v)
+{
+       struct capi_ctr *ctrl = m->private;
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       u8 flag;
+       char *s;
+
+       seq_printf(m, "%-16s %s\n", "name", card->name);
+       seq_printf(m, "%-16s 0x%x\n", "io", card->port);
+       seq_printf(m, "%-16s %d\n", "irq", card->irq);
+       switch (card->cardtype) {
+       case avm_b1isa: s = "B1 ISA"; break;
+       case avm_b1pci: s = "B1 PCI"; break;
+       case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+       case avm_m1: s = "M1"; break;
+       case avm_m2: s = "M2"; break;
+       case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+       case avm_t1pci: s = "T1 PCI"; break;
+       case avm_c4: s = "C4"; break;
+       case avm_c2: s = "C2"; break;
+       default: s = "???"; break;
+       }
+       seq_printf(m, "%-16s %s\n", "type", s);
+       if (card->cardtype == avm_t1isa)
+               seq_printf(m, "%-16s %d\n", "cardnr", card->cardnr);
+       if ((s = cinfo->version[VER_DRIVER]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_driver", s);
+       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
+       if ((s = cinfo->version[VER_SERIAL]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_serial", s);
+
+       if (card->cardtype != avm_m1) {
+               flag = ((u8 *)(ctrl->profile.manu))[3];
+               if (flag)
+                       seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
+                                  "protocol",
+                                  (flag & 0x01) ? " DSS1" : "",
+                                  (flag & 0x02) ? " CT1" : "",
+                                  (flag & 0x04) ? " VN3" : "",
+                                  (flag & 0x08) ? " NI1" : "",
+                                  (flag & 0x10) ? " AUSTEL" : "",
+                                  (flag & 0x20) ? " ESS" : "",
+                                  (flag & 0x40) ? " 1TR6" : ""
+                               );
+       }
+       if (card->cardtype != avm_m1) {
+               flag = ((u8 *)(ctrl->profile.manu))[5];
+               if (flag)
+                       seq_printf(m, "%-16s%s%s%s%s\n",
+                                  "linetype",
+                                  (flag & 0x01) ? " point to point" : "",
+                                  (flag & 0x02) ? " point to multipoint" : "",
+                                  (flag & 0x08) ? " leased line without D-channel" : "",
+                                  (flag & 0x04) ? " leased line with D-channel" : ""
+                               );
+       }
+       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
+
+       return 0;
+}
+EXPORT_SYMBOL(b1_proc_show);
+
+/* ------------------------------------------------------------- */
+
+#ifdef CONFIG_PCI
+
+avmcard_dmainfo *
+avmcard_dma_alloc(char *name, struct pci_dev *pdev, long rsize, long ssize)
+{
+       avmcard_dmainfo *p;
+       void *buf;
+
+       p = kzalloc(sizeof(avmcard_dmainfo), GFP_KERNEL);
+       if (!p) {
+               printk(KERN_WARNING "%s: no memory.\n", name);
+               goto err;
+       }
+
+       p->recvbuf.size = rsize;
+       buf = pci_alloc_consistent(pdev, rsize, &p->recvbuf.dmaaddr);
+       if (!buf) {
+               printk(KERN_WARNING "%s: allocation of receive dma buffer failed.\n", name);
+               goto err_kfree;
+       }
+       p->recvbuf.dmabuf = buf;
+
+       p->sendbuf.size = ssize;
+       buf = pci_alloc_consistent(pdev, ssize, &p->sendbuf.dmaaddr);
+       if (!buf) {
+               printk(KERN_WARNING "%s: allocation of send dma buffer failed.\n", name);
+               goto err_free_consistent;
+       }
+
+       p->sendbuf.dmabuf = buf;
+       skb_queue_head_init(&p->send_queue);
+
+       return p;
+
+err_free_consistent:
+       pci_free_consistent(p->pcidev, p->recvbuf.size,
+                           p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
+err_kfree:
+       kfree(p);
+err:
+       return NULL;
+}
+
+void avmcard_dma_free(avmcard_dmainfo *p)
+{
+       pci_free_consistent(p->pcidev, p->recvbuf.size,
+                           p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
+       pci_free_consistent(p->pcidev, p->sendbuf.size,
+                           p->sendbuf.dmabuf, p->sendbuf.dmaaddr);
+       skb_queue_purge(&p->send_queue);
+       kfree(p);
+}
+
+EXPORT_SYMBOL(avmcard_dma_alloc);
+EXPORT_SYMBOL(avmcard_dma_free);
+
+#endif
+
+EXPORT_SYMBOL(b1_irq_table);
+
+EXPORT_SYMBOL(b1_alloc_card);
+EXPORT_SYMBOL(b1_free_card);
+EXPORT_SYMBOL(b1_detect);
+EXPORT_SYMBOL(b1_getrevision);
+EXPORT_SYMBOL(b1_load_t4file);
+EXPORT_SYMBOL(b1_load_config);
+EXPORT_SYMBOL(b1_loaded);
+EXPORT_SYMBOL(b1_load_firmware);
+EXPORT_SYMBOL(b1_reset_ctr);
+EXPORT_SYMBOL(b1_register_appl);
+EXPORT_SYMBOL(b1_release_appl);
+EXPORT_SYMBOL(b1_send_message);
+
+EXPORT_SYMBOL(b1_parse_version);
+EXPORT_SYMBOL(b1_interrupt);
+
+static int __init b1_init(void)
+{
+       char *p;
+       char rev[32];
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, 32);
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+       printk(KERN_INFO "b1: revision %s\n", rev);
+
+       return 0;
+}
+
+static void __exit b1_exit(void)
+{
+}
+
+module_init(b1_init);
+module_exit(b1_exit);
diff --git a/drivers/staging/isdn/avm/b1dma.c b/drivers/staging/isdn/avm/b1dma.c
new file mode 100644 (file)
index 0000000..6a3dc99
--- /dev/null
@@ -0,0 +1,981 @@
+/* $Id: b1dma.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ *
+ * Common module for AVM B1 cards that support dma with AMCC
+ *
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/gfp.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+#undef AVM_B1DMA_DEBUG
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: DMA support for active AVM cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+static bool suppress_pollack = 0;
+module_param(suppress_pollack, bool, 0);
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+/* S5933 */
+
+#define        AMCC_RXPTR      0x24
+#define        AMCC_RXLEN      0x28
+#define        AMCC_TXPTR      0x2c
+#define        AMCC_TXLEN      0x30
+
+#define        AMCC_INTCSR     0x38
+#      define EN_READ_TC_INT           0x00008000L
+#      define EN_WRITE_TC_INT          0x00004000L
+#      define EN_TX_TC_INT             EN_READ_TC_INT
+#      define EN_RX_TC_INT             EN_WRITE_TC_INT
+#      define AVM_FLAG                 0x30000000L
+
+#      define ANY_S5933_INT            0x00800000L
+#      define READ_TC_INT              0x00080000L
+#      define WRITE_TC_INT             0x00040000L
+#      define  TX_TC_INT               READ_TC_INT
+#      define  RX_TC_INT               WRITE_TC_INT
+#      define MASTER_ABORT_INT         0x00100000L
+#      define TARGET_ABORT_INT         0x00200000L
+#      define BUS_MASTER_INT           0x00200000L
+#      define ALL_INT                  0x000C0000L
+
+#define        AMCC_MCSR       0x3c
+#      define A2P_HI_PRIORITY          0x00000100L
+#      define EN_A2P_TRANSFERS         0x00000400L
+#      define P2A_HI_PRIORITY          0x00001000L
+#      define EN_P2A_TRANSFERS         0x00004000L
+#      define RESET_A2P_FLAGS          0x04000000L
+#      define RESET_P2A_FLAGS          0x02000000L
+
+/* ------------------------------------------------------------- */
+
+static inline void b1dma_writel(avmcard *card, u32 value, int off)
+{
+       writel(value, card->mbase + off);
+}
+
+static inline u32 b1dma_readl(avmcard *card, int off)
+{
+       return readl(card->mbase + off);
+}
+
+/* ------------------------------------------------------------- */
+
+static inline int b1dma_tx_empty(unsigned int port)
+{
+       return inb(port + 0x03) & 0x1;
+}
+
+static inline int b1dma_rx_full(unsigned int port)
+{
+       return inb(port + 0x02) & 0x1;
+}
+
+static int b1dma_tolink(avmcard *card, void *buf, unsigned int len)
+{
+       unsigned long stop = jiffies + 1 * HZ;  /* maximum wait time 1 sec */
+       unsigned char *s = (unsigned char *)buf;
+       while (len--) {
+               while (!b1dma_tx_empty(card->port)
+                      && time_before(jiffies, stop));
+               if (!b1dma_tx_empty(card->port))
+                       return -1;
+               t1outp(card->port, 0x01, *s++);
+       }
+       return 0;
+}
+
+static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len)
+{
+       unsigned long stop = jiffies + 1 * HZ;  /* maximum wait time 1 sec */
+       unsigned char *s = (unsigned char *)buf;
+       while (len--) {
+               while (!b1dma_rx_full(card->port)
+                      && time_before(jiffies, stop));
+               if (!b1dma_rx_full(card->port))
+                       return -1;
+               *s++ = t1inp(card->port, 0x00);
+       }
+       return 0;
+}
+
+static int WriteReg(avmcard *card, u32 reg, u8 val)
+{
+       u8 cmd = 0x00;
+       if (b1dma_tolink(card, &cmd, 1) == 0
+           && b1dma_tolink(card, &reg, 4) == 0) {
+               u32 tmp = val;
+               return b1dma_tolink(card, &tmp, 4);
+       }
+       return -1;
+}
+
+static u8 ReadReg(avmcard *card, u32 reg)
+{
+       u8 cmd = 0x01;
+       if (b1dma_tolink(card, &cmd, 1) == 0
+           && b1dma_tolink(card, &reg, 4) == 0) {
+               u32 tmp;
+               if (b1dma_fromlink(card, &tmp, 4) == 0)
+                       return (u8)tmp;
+       }
+       return 0xff;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, u8 val)
+{
+       u8 *s = *pp;
+       *s++ = val;
+       *pp = s;
+}
+
+static inline void _put_word(void **pp, u32 val)
+{
+       u8 *s = *pp;
+       *s++ = val & 0xff;
+       *s++ = (val >> 8) & 0xff;
+       *s++ = (val >> 16) & 0xff;
+       *s++ = (val >> 24) & 0xff;
+       *pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+       unsigned i = len;
+       _put_word(pp, i);
+       while (i-- > 0)
+               _put_byte(pp, *dp++);
+}
+
+static inline u8 _get_byte(void **pp)
+{
+       u8 *s = *pp;
+       u8 val;
+       val = *s++;
+       *pp = s;
+       return val;
+}
+
+static inline u32 _get_word(void **pp)
+{
+       u8 *s = *pp;
+       u32 val;
+       val = *s++;
+       val |= (*s++ << 8);
+       val |= (*s++ << 16);
+       val |= (*s++ << 24);
+       *pp = s;
+       return val;
+}
+
+static inline u32 _get_slice(void **pp, unsigned char *dp)
+{
+       unsigned int len, i;
+
+       len = i = _get_word(pp);
+       while (i-- > 0) *dp++ = _get_byte(pp);
+       return len;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_reset(avmcard *card)
+{
+       card->csr = 0x0;
+       b1dma_writel(card, card->csr, AMCC_INTCSR);
+       b1dma_writel(card, 0, AMCC_MCSR);
+       b1dma_writel(card, 0, AMCC_RXLEN);
+       b1dma_writel(card, 0, AMCC_TXLEN);
+
+       t1outp(card->port, 0x10, 0x00);
+       t1outp(card->port, 0x07, 0x00);
+
+       b1dma_writel(card, 0, AMCC_MCSR);
+       mdelay(10);
+       b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
+       mdelay(10);
+       b1dma_writel(card, 0, AMCC_MCSR);
+       if (card->cardtype == avm_t1pci)
+               mdelay(42);
+       else
+               mdelay(10);
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1dma_detect(avmcard *card)
+{
+       b1dma_writel(card, 0, AMCC_MCSR);
+       mdelay(10);
+       b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
+       mdelay(10);
+       b1dma_writel(card, 0, AMCC_MCSR);
+       mdelay(42);
+
+       b1dma_writel(card, 0, AMCC_RXLEN);
+       b1dma_writel(card, 0, AMCC_TXLEN);
+       card->csr = 0x0;
+       b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+       if (b1dma_readl(card, AMCC_MCSR) != 0x000000E6)
+               return 1;
+
+       b1dma_writel(card, 0xffffffff, AMCC_RXPTR);
+       b1dma_writel(card, 0xffffffff, AMCC_TXPTR);
+       if (b1dma_readl(card, AMCC_RXPTR) != 0xfffffffc
+           || b1dma_readl(card, AMCC_TXPTR) != 0xfffffffc)
+               return 2;
+
+       b1dma_writel(card, 0x0, AMCC_RXPTR);
+       b1dma_writel(card, 0x0, AMCC_TXPTR);
+       if (b1dma_readl(card, AMCC_RXPTR) != 0x0
+           || b1dma_readl(card, AMCC_TXPTR) != 0x0)
+               return 3;
+
+       t1outp(card->port, 0x10, 0x00);
+       t1outp(card->port, 0x07, 0x00);
+
+       t1outp(card->port, 0x02, 0x02);
+       t1outp(card->port, 0x03, 0x02);
+
+       if ((t1inp(card->port, 0x02) & 0xFE) != 0x02
+           || t1inp(card->port, 0x3) != 0x03)
+               return 4;
+
+       t1outp(card->port, 0x02, 0x00);
+       t1outp(card->port, 0x03, 0x00);
+
+       if ((t1inp(card->port, 0x02) & 0xFE) != 0x00
+           || t1inp(card->port, 0x3) != 0x01)
+               return 5;
+
+       return 0;
+}
+
+int t1pci_detect(avmcard *card)
+{
+       int ret;
+
+       if ((ret = b1dma_detect(card)) != 0)
+               return ret;
+
+       /* Transputer test */
+
+       if (WriteReg(card, 0x80001000, 0x11) != 0
+           || WriteReg(card, 0x80101000, 0x22) != 0
+           || WriteReg(card, 0x80201000, 0x33) != 0
+           || WriteReg(card, 0x80301000, 0x44) != 0)
+               return 6;
+
+       if (ReadReg(card, 0x80001000) != 0x11
+           || ReadReg(card, 0x80101000) != 0x22
+           || ReadReg(card, 0x80201000) != 0x33
+           || ReadReg(card, 0x80301000) != 0x44)
+               return 7;
+
+       if (WriteReg(card, 0x80001000, 0x55) != 0
+           || WriteReg(card, 0x80101000, 0x66) != 0
+           || WriteReg(card, 0x80201000, 0x77) != 0
+           || WriteReg(card, 0x80301000, 0x88) != 0)
+               return 8;
+
+       if (ReadReg(card, 0x80001000) != 0x55
+           || ReadReg(card, 0x80101000) != 0x66
+           || ReadReg(card, 0x80201000) != 0x77
+           || ReadReg(card, 0x80301000) != 0x88)
+               return 9;
+
+       return 0;
+}
+
+int b1pciv4_detect(avmcard *card)
+{
+       int ret, i;
+
+       if ((ret = b1dma_detect(card)) != 0)
+               return ret;
+
+       for (i = 0; i < 5; i++) {
+               if (WriteReg(card, 0x80A00000, 0x21) != 0)
+                       return 6;
+               if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01)
+                       return 7;
+       }
+       for (i = 0; i < 5; i++) {
+               if (WriteReg(card, 0x80A00000, 0x20) != 0)
+                       return 8;
+               if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00)
+                       return 9;
+       }
+
+       return 0;
+}
+
+static void b1dma_queue_tx(avmcard *card, struct sk_buff *skb)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       skb_queue_tail(&card->dma->send_queue, skb);
+
+       if (!(card->csr & EN_TX_TC_INT)) {
+               b1dma_dispatch_tx(card);
+               b1dma_writel(card, card->csr, AMCC_INTCSR);
+       }
+
+       spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card)
+{
+       avmcard_dmainfo *dma = card->dma;
+       struct sk_buff *skb;
+       u8 cmd, subcmd;
+       u16 len;
+       u32 txlen;
+       void *p;
+
+       skb = skb_dequeue(&dma->send_queue);
+
+       len = CAPIMSG_LEN(skb->data);
+
+       if (len) {
+               cmd = CAPIMSG_COMMAND(skb->data);
+               subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+               p = dma->sendbuf.dmabuf;
+
+               if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+                       u16 dlen = CAPIMSG_DATALEN(skb->data);
+                       _put_byte(&p, SEND_DATA_B3_REQ);
+                       _put_slice(&p, skb->data, len);
+                       _put_slice(&p, skb->data + len, dlen);
+               } else {
+                       _put_byte(&p, SEND_MESSAGE);
+                       _put_slice(&p, skb->data, len);
+               }
+               txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
+#ifdef AVM_B1DMA_DEBUG
+               printk(KERN_DEBUG "tx: put msg len=%d\n", txlen);
+#endif
+       } else {
+               txlen = skb->len - 2;
+#ifdef AVM_B1DMA_POLLDEBUG
+               if (skb->data[2] == SEND_POLLACK)
+                       printk(KERN_INFO "%s: send ack\n", card->name);
+#endif
+#ifdef AVM_B1DMA_DEBUG
+               printk(KERN_DEBUG "tx: put 0x%x len=%d\n",
+                      skb->data[2], txlen);
+#endif
+               skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+                                                skb->len - 2);
+       }
+       txlen = (txlen + 3) & ~3;
+
+       b1dma_writel(card, dma->sendbuf.dmaaddr, AMCC_TXPTR);
+       b1dma_writel(card, txlen, AMCC_TXLEN);
+
+       card->csr |= EN_TX_TC_INT;
+
+       dev_kfree_skb_any(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+       struct sk_buff *skb;
+       void *p;
+
+       skb = alloc_skb(3, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+                      card->name);
+               return;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_POLLACK);
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_rx(avmcard *card)
+{
+       avmctrl_info *cinfo = &card->ctrlinfo[0];
+       avmcard_dmainfo *dma = card->dma;
+       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+       struct sk_buff *skb;
+       void *p = dma->recvbuf.dmabuf + 4;
+       u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+       u8 b1cmd =  _get_byte(&p);
+
+#ifdef AVM_B1DMA_DEBUG
+       printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
+#endif
+
+       switch (b1cmd) {
+       case RECEIVE_DATA_B3_IND:
+
+               ApplId = (unsigned) _get_word(&p);
+               MsgLen = _get_slice(&p, card->msgbuf);
+               DataB3Len = _get_slice(&p, card->databuf);
+
+               if (MsgLen < 30) { /* not CAPI 64Bit */
+                       memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
+                       MsgLen = 30;
+                       CAPIMSG_SETLEN(card->msgbuf, 30);
+               }
+               if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
+                       printk(KERN_ERR "%s: incoming packet dropped\n",
+                              card->name);
+               } else {
+                       skb_put_data(skb, card->msgbuf, MsgLen);
+                       skb_put_data(skb, card->databuf, DataB3Len);
+                       capi_ctr_handle_message(ctrl, ApplId, skb);
+               }
+               break;
+
+       case RECEIVE_MESSAGE:
+
+               ApplId = (unsigned) _get_word(&p);
+               MsgLen = _get_slice(&p, card->msgbuf);
+               if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+                       printk(KERN_ERR "%s: incoming packet dropped\n",
+                              card->name);
+               } else {
+                       skb_put_data(skb, card->msgbuf, MsgLen);
+                       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) {
+                               spin_lock(&card->lock);
+                               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+                                                    CAPIMSG_NCCI(skb->data),
+                                                    CAPIMSG_MSGID(skb->data));
+                               spin_unlock(&card->lock);
+                       }
+                       capi_ctr_handle_message(ctrl, ApplId, skb);
+               }
+               break;
+
+       case RECEIVE_NEW_NCCI:
+
+               ApplId = _get_word(&p);
+               NCCI = _get_word(&p);
+               WindowSize = _get_word(&p);
+               spin_lock(&card->lock);
+               capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+               spin_unlock(&card->lock);
+               break;
+
+       case RECEIVE_FREE_NCCI:
+
+               ApplId = _get_word(&p);
+               NCCI = _get_word(&p);
+
+               if (NCCI != 0xffffffff) {
+                       spin_lock(&card->lock);
+                       capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+                       spin_unlock(&card->lock);
+               }
+               break;
+
+       case RECEIVE_START:
+#ifdef AVM_B1DMA_POLLDEBUG
+               printk(KERN_INFO "%s: receive poll\n", card->name);
+#endif
+               if (!suppress_pollack)
+                       queue_pollack(card);
+               capi_ctr_resume_output(ctrl);
+               break;
+
+       case RECEIVE_STOP:
+               capi_ctr_suspend_output(ctrl);
+               break;
+
+       case RECEIVE_INIT:
+
+               cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+               b1_parse_version(cinfo);
+               printk(KERN_INFO "%s: %s-card (%s) now active\n",
+                      card->name,
+                      cinfo->version[VER_CARDTYPE],
+                      cinfo->version[VER_DRIVER]);
+               capi_ctr_ready(ctrl);
+               break;
+
+       case RECEIVE_TASK_READY:
+               ApplId = (unsigned) _get_word(&p);
+               MsgLen = _get_slice(&p, card->msgbuf);
+               card->msgbuf[MsgLen] = 0;
+               while (MsgLen > 0
+                      && (card->msgbuf[MsgLen - 1] == '\n'
+                          || card->msgbuf[MsgLen - 1] == '\r')) {
+                       card->msgbuf[MsgLen - 1] = 0;
+                       MsgLen--;
+               }
+               printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+                      card->name, ApplId, card->msgbuf);
+               break;
+
+       case RECEIVE_DEBUGMSG:
+               MsgLen = _get_slice(&p, card->msgbuf);
+               card->msgbuf[MsgLen] = 0;
+               while (MsgLen > 0
+                      && (card->msgbuf[MsgLen - 1] == '\n'
+                          || card->msgbuf[MsgLen - 1] == '\r')) {
+                       card->msgbuf[MsgLen - 1] = 0;
+                       MsgLen--;
+               }
+               printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+               break;
+
+       default:
+               printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n",
+                      card->name, b1cmd);
+               return;
+       }
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_interrupt(avmcard *card)
+{
+       u32 status;
+       u32 newcsr;
+
+       spin_lock(&card->lock);
+
+       status = b1dma_readl(card, AMCC_INTCSR);
+       if ((status & ANY_S5933_INT) == 0) {
+               spin_unlock(&card->lock);
+               return;
+       }
+
+       newcsr = card->csr | (status & ALL_INT);
+       if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
+       if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
+       b1dma_writel(card, newcsr, AMCC_INTCSR);
+
+       if ((status & RX_TC_INT) != 0) {
+               struct avmcard_dmainfo *dma = card->dma;
+               u32 rxlen;
+               if (card->dma->recvlen == 0) {
+                       rxlen = b1dma_readl(card, AMCC_RXLEN);
+                       if (rxlen == 0) {
+                               dma->recvlen = *((u32 *)dma->recvbuf.dmabuf);
+                               rxlen = (dma->recvlen + 3) & ~3;
+                               b1dma_writel(card, dma->recvbuf.dmaaddr + 4, AMCC_RXPTR);
+                               b1dma_writel(card, rxlen, AMCC_RXLEN);
+#ifdef AVM_B1DMA_DEBUG
+                       } else {
+                               printk(KERN_ERR "%s: rx not complete (%d).\n",
+                                      card->name, rxlen);
+#endif
+                       }
+               } else {
+                       spin_unlock(&card->lock);
+                       b1dma_handle_rx(card);
+                       dma->recvlen = 0;
+                       spin_lock(&card->lock);
+                       b1dma_writel(card, dma->recvbuf.dmaaddr, AMCC_RXPTR);
+                       b1dma_writel(card, 4, AMCC_RXLEN);
+               }
+       }
+
+       if ((status & TX_TC_INT) != 0) {
+               if (skb_queue_empty(&card->dma->send_queue))
+                       card->csr &= ~EN_TX_TC_INT;
+               else
+                       b1dma_dispatch_tx(card);
+       }
+       b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+       spin_unlock(&card->lock);
+}
+
+irqreturn_t b1dma_interrupt(int interrupt, void *devptr)
+{
+       avmcard *card = devptr;
+
+       b1dma_handle_interrupt(card);
+       return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1dma_loaded(avmcard *card)
+{
+       unsigned long stop;
+       unsigned char ans;
+       unsigned long tout = 2;
+       unsigned int base = card->port;
+
+       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+               if (b1_tx_empty(base))
+                       break;
+       }
+       if (!b1_tx_empty(base)) {
+               printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n",
+                      card->name);
+               return 0;
+       }
+       b1_put_byte(base, SEND_POLLACK);
+       for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+               if (b1_rx_full(base)) {
+                       if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
+                               return 1;
+                       }
+                       printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
+                       return 0;
+               }
+       }
+       printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name);
+       return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_send_init(avmcard *card)
+{
+       struct sk_buff *skb;
+       void *p;
+
+       skb = alloc_skb(15, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+                      card->name);
+               return;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_INIT);
+       _put_word(&p, CAPI_MAXAPPL);
+       _put_word(&p, AVM_NCCI_PER_CHANNEL * 30);
+       _put_word(&p, card->cardnr - 1);
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       b1dma_queue_tx(card, skb);
+}
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       int retval;
+
+       b1dma_reset(card);
+
+       if ((retval = b1_load_t4file(card, &data->firmware))) {
+               b1dma_reset(card);
+               printk(KERN_ERR "%s: failed to load t4file!!\n",
+                      card->name);
+               return retval;
+       }
+
+       if (data->configuration.len > 0 && data->configuration.data) {
+               if ((retval = b1_load_config(card, &data->configuration))) {
+                       b1dma_reset(card);
+                       printk(KERN_ERR "%s: failed to load config!!\n",
+                              card->name);
+                       return retval;
+               }
+       }
+
+       if (!b1dma_loaded(card)) {
+               b1dma_reset(card);
+               printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+               return -EIO;
+       }
+
+       card->csr = AVM_FLAG;
+       b1dma_writel(card, card->csr, AMCC_INTCSR);
+       b1dma_writel(card, EN_A2P_TRANSFERS | EN_P2A_TRANSFERS | A2P_HI_PRIORITY |
+                    P2A_HI_PRIORITY | RESET_A2P_FLAGS | RESET_P2A_FLAGS,
+                    AMCC_MCSR);
+       t1outp(card->port, 0x07, 0x30);
+       t1outp(card->port, 0x10, 0xF0);
+
+       card->dma->recvlen = 0;
+       b1dma_writel(card, card->dma->recvbuf.dmaaddr, AMCC_RXPTR);
+       b1dma_writel(card, 4, AMCC_RXLEN);
+       card->csr |= EN_RX_TC_INT;
+       b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+       b1dma_send_init(card);
+
+       return 0;
+}
+
+void b1dma_reset_ctr(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+       b1dma_reset(card);
+
+       memset(cinfo->version, 0, sizeof(cinfo->version));
+       capilib_release(&cinfo->ncci_head);
+       spin_unlock_irqrestore(&card->lock, flags);
+       capi_ctr_down(ctrl);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_register_appl(struct capi_ctr *ctrl,
+                        u16 appl,
+                        capi_register_params *rp)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       struct sk_buff *skb;
+       int want = rp->level3cnt;
+       int nconn;
+       void *p;
+
+       if (want > 0) nconn = want;
+       else nconn = ctrl->profile.nbchannel * -want;
+       if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+       skb = alloc_skb(23, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+                      card->name);
+               return;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_REGISTER);
+       _put_word(&p, appl);
+       _put_word(&p, 1024 * (nconn + 1));
+       _put_word(&p, nconn);
+       _put_word(&p, rp->datablkcnt);
+       _put_word(&p, rp->datablklen);
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       struct sk_buff *skb;
+       void *p;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+       capilib_release_appl(&cinfo->ncci_head, appl);
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       skb = alloc_skb(7, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+                      card->name);
+               return;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_RELEASE);
+       _put_word(&p, appl);
+
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       u16 retval = CAPI_NOERROR;
+
+       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+               unsigned long flags;
+               spin_lock_irqsave(&card->lock, flags);
+               retval = capilib_data_b3_req(&cinfo->ncci_head,
+                                            CAPIMSG_APPID(skb->data),
+                                            CAPIMSG_NCCI(skb->data),
+                                            CAPIMSG_MSGID(skb->data));
+               spin_unlock_irqrestore(&card->lock, flags);
+       }
+       if (retval == CAPI_NOERROR)
+               b1dma_queue_tx(card, skb);
+
+       return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1dma_proc_show(struct seq_file *m, void *v)
+{
+       struct capi_ctr *ctrl = m->private;
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       u8 flag;
+       char *s;
+       u32 txoff, txlen, rxoff, rxlen, csr;
+       unsigned long flags;
+
+       seq_printf(m, "%-16s %s\n", "name", card->name);
+       seq_printf(m, "%-16s 0x%x\n", "io", card->port);
+       seq_printf(m, "%-16s %d\n", "irq", card->irq);
+       seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase);
+       switch (card->cardtype) {
+       case avm_b1isa: s = "B1 ISA"; break;
+       case avm_b1pci: s = "B1 PCI"; break;
+       case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+       case avm_m1: s = "M1"; break;
+       case avm_m2: s = "M2"; break;
+       case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+       case avm_t1pci: s = "T1 PCI"; break;
+       case avm_c4: s = "C4"; break;
+       case avm_c2: s = "C2"; break;
+       default: s = "???"; break;
+       }
+       seq_printf(m, "%-16s %s\n", "type", s);
+       if ((s = cinfo->version[VER_DRIVER]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_driver", s);
+       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
+       if ((s = cinfo->version[VER_SERIAL]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_serial", s);
+
+       if (card->cardtype != avm_m1) {
+               flag = ((u8 *)(ctrl->profile.manu))[3];
+               if (flag)
+                       seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
+                                  "protocol",
+                                  (flag & 0x01) ? " DSS1" : "",
+                                  (flag & 0x02) ? " CT1" : "",
+                                  (flag & 0x04) ? " VN3" : "",
+                                  (flag & 0x08) ? " NI1" : "",
+                                  (flag & 0x10) ? " AUSTEL" : "",
+                                  (flag & 0x20) ? " ESS" : "",
+                                  (flag & 0x40) ? " 1TR6" : ""
+                               );
+       }
+       if (card->cardtype != avm_m1) {
+               flag = ((u8 *)(ctrl->profile.manu))[5];
+               if (flag)
+                       seq_printf(m, "%-16s%s%s%s%s\n",
+                                  "linetype",
+                                  (flag & 0x01) ? " point to point" : "",
+                                  (flag & 0x02) ? " point to multipoint" : "",
+                                  (flag & 0x08) ? " leased line without D-channel" : "",
+                                  (flag & 0x04) ? " leased line with D-channel" : ""
+                               );
+       }
+       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
+
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       txoff = (dma_addr_t)b1dma_readl(card, AMCC_TXPTR)-card->dma->sendbuf.dmaaddr;
+       txlen = b1dma_readl(card, AMCC_TXLEN);
+
+       rxoff = (dma_addr_t)b1dma_readl(card, AMCC_RXPTR)-card->dma->recvbuf.dmaaddr;
+       rxlen = b1dma_readl(card, AMCC_RXLEN);
+
+       csr  = b1dma_readl(card, AMCC_INTCSR);
+
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       seq_printf(m, "%-16s 0x%lx\n", "csr (cached)", (unsigned long)card->csr);
+       seq_printf(m, "%-16s 0x%lx\n", "csr", (unsigned long)csr);
+       seq_printf(m, "%-16s %lu\n", "txoff", (unsigned long)txoff);
+       seq_printf(m, "%-16s %lu\n", "txlen", (unsigned long)txlen);
+       seq_printf(m, "%-16s %lu\n", "rxoff", (unsigned long)rxoff);
+       seq_printf(m, "%-16s %lu\n", "rxlen", (unsigned long)rxlen);
+
+       return 0;
+}
+EXPORT_SYMBOL(b1dma_proc_show);
+
+/* ------------------------------------------------------------- */
+
+EXPORT_SYMBOL(b1dma_reset);
+EXPORT_SYMBOL(t1pci_detect);
+EXPORT_SYMBOL(b1pciv4_detect);
+EXPORT_SYMBOL(b1dma_interrupt);
+
+EXPORT_SYMBOL(b1dma_load_firmware);
+EXPORT_SYMBOL(b1dma_reset_ctr);
+EXPORT_SYMBOL(b1dma_register_appl);
+EXPORT_SYMBOL(b1dma_release_appl);
+EXPORT_SYMBOL(b1dma_send_message);
+
+static int __init b1dma_init(void)
+{
+       char *p;
+       char rev[32];
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, sizeof(rev));
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+       printk(KERN_INFO "b1dma: revision %s\n", rev);
+
+       return 0;
+}
+
+static void __exit b1dma_exit(void)
+{
+}
+
+module_init(b1dma_init);
+module_exit(b1dma_exit);
diff --git a/drivers/staging/isdn/avm/b1isa.c b/drivers/staging/isdn/avm/b1isa.c
new file mode 100644 (file)
index 0000000..cdfea72
--- /dev/null
@@ -0,0 +1,243 @@
+/* $Id: b1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ *
+ * Module for AVM B1 ISA-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 ISA card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static void b1isa_remove(struct pci_dev *pdev)
+{
+       avmctrl_info *cinfo = pci_get_drvdata(pdev);
+       avmcard *card;
+
+       if (!cinfo)
+               return;
+
+       card = cinfo->card;
+
+       b1_reset(card->port);
+       b1_reset(card->port);
+
+       detach_capi_ctr(&cinfo->capi_ctrl);
+       free_irq(card->irq, card);
+       release_region(card->port, AVMB1_PORTLEN);
+       b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *b1isa_procinfo(struct capi_ctr *ctrl);
+
+static int b1isa_probe(struct pci_dev *pdev)
+{
+       avmctrl_info *cinfo;
+       avmcard *card;
+       int retval;
+
+       card = b1_alloc_card(1);
+       if (!card) {
+               printk(KERN_WARNING "b1isa: no memory.\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       cinfo = card->ctrlinfo;
+
+       card->port = pci_resource_start(pdev, 0);
+       card->irq = pdev->irq;
+       card->cardtype = avm_b1isa;
+       sprintf(card->name, "b1isa-%x", card->port);
+
+       if (card->port != 0x150 && card->port != 0x250
+           && card->port != 0x300 && card->port != 0x340) {
+               printk(KERN_WARNING "b1isa: invalid port 0x%x.\n", card->port);
+               retval = -EINVAL;
+               goto err_free;
+       }
+       if (b1_irq_table[card->irq & 0xf] == 0) {
+               printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq);
+               retval = -EINVAL;
+               goto err_free;
+       }
+       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+               printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n",
+                      card->port, card->port + AVMB1_PORTLEN);
+               retval = -EBUSY;
+               goto err_free;
+       }
+       retval = request_irq(card->irq, b1_interrupt, 0, card->name, card);
+       if (retval) {
+               printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq);
+               goto err_release_region;
+       }
+       b1_reset(card->port);
+       if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
+               printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n",
+                      card->port, retval);
+               retval = -ENODEV;
+               goto err_free_irq;
+       }
+       b1_reset(card->port);
+       b1_getrevision(card);
+
+       cinfo->capi_ctrl.owner = THIS_MODULE;
+       cinfo->capi_ctrl.driver_name   = "b1isa";
+       cinfo->capi_ctrl.driverdata    = cinfo;
+       cinfo->capi_ctrl.register_appl = b1_register_appl;
+       cinfo->capi_ctrl.release_appl  = b1_release_appl;
+       cinfo->capi_ctrl.send_message  = b1_send_message;
+       cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+       cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
+       cinfo->capi_ctrl.procinfo      = b1isa_procinfo;
+       cinfo->capi_ctrl.proc_show     = b1_proc_show;
+       strcpy(cinfo->capi_ctrl.name, card->name);
+
+       retval = attach_capi_ctr(&cinfo->capi_ctrl);
+       if (retval) {
+               printk(KERN_ERR "b1isa: attach controller failed.\n");
+               goto err_free_irq;
+       }
+
+       printk(KERN_INFO "b1isa: AVM B1 ISA at i/o %#x, irq %d, revision %d\n",
+              card->port, card->irq, card->revision);
+
+       pci_set_drvdata(pdev, cinfo);
+       return 0;
+
+err_free_irq:
+       free_irq(card->irq, card);
+err_release_region:
+       release_region(card->port, AVMB1_PORTLEN);
+err_free:
+       b1_free_card(card);
+err:
+       return retval;
+}
+
+static char *b1isa_procinfo(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->port : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               cinfo->card ? cinfo->card->revision : 0
+               );
+       return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+
+module_param_hw_array(io, int, ioport, NULL, 0);
+module_param_hw_array(irq, int, irq, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+
+static int b1isa_add_card(struct capi_driver *driver, capicardparams *data)
+{
+       int i;
+
+       for (i = 0; i < MAX_CARDS; i++) {
+               if (isa_dev[i].resource[0].start)
+                       continue;
+
+               isa_dev[i].resource[0].start = data->port;
+               isa_dev[i].irq = data->irq;
+
+               if (b1isa_probe(&isa_dev[i]) == 0)
+                       return 0;
+       }
+       return -ENODEV;
+}
+
+static struct capi_driver capi_driver_b1isa = {
+       .name           = "b1isa",
+       .revision       = "1.0",
+       .add_card       = b1isa_add_card,
+};
+
+static int __init b1isa_init(void)
+{
+       char *p;
+       char rev[32];
+       int i;
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, 32);
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+       for (i = 0; i < MAX_CARDS; i++) {
+               if (!io[i])
+                       break;
+
+               isa_dev[i].resource[0].start = io[i];
+               isa_dev[i].irq = irq[i];
+
+               if (b1isa_probe(&isa_dev[i]) != 0)
+                       return -ENODEV;
+       }
+
+       strlcpy(capi_driver_b1isa.revision, rev, 32);
+       register_capi_driver(&capi_driver_b1isa);
+       printk(KERN_INFO "b1isa: revision %s\n", rev);
+
+       return 0;
+}
+
+static void __exit b1isa_exit(void)
+{
+       int i;
+
+       for (i = 0; i < MAX_CARDS; i++) {
+               if (isa_dev[i].resource[0].start)
+                       b1isa_remove(&isa_dev[i]);
+       }
+       unregister_capi_driver(&capi_driver_b1isa);
+}
+
+module_init(b1isa_init);
+module_exit(b1isa_exit);
diff --git a/drivers/staging/isdn/avm/b1pci.c b/drivers/staging/isdn/avm/b1pci.c
new file mode 100644 (file)
index 0000000..b76b57a
--- /dev/null
@@ -0,0 +1,416 @@
+/* $Id: b1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM B1 PCI-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+static struct pci_device_id b1pci_pci_tbl[] = {
+       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, PCI_ANY_ID, PCI_ANY_ID },
+       { }                             /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, b1pci_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 PCI card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static char *b1pci_procinfo(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->port : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               cinfo->card ? cinfo->card->revision : 0
+               );
+       return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1pci_probe(struct capicardparams *p, struct pci_dev *pdev)
+{
+       avmcard *card;
+       avmctrl_info *cinfo;
+       int retval;
+
+       card = b1_alloc_card(1);
+       if (!card) {
+               printk(KERN_WARNING "b1pci: no memory.\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       cinfo = card->ctrlinfo;
+       sprintf(card->name, "b1pci-%x", p->port);
+       card->port = p->port;
+       card->irq = p->irq;
+       card->cardtype = avm_b1pci;
+
+       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+               printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
+                      card->port, card->port + AVMB1_PORTLEN);
+               retval = -EBUSY;
+               goto err_free;
+       }
+       b1_reset(card->port);
+       retval = b1_detect(card->port, card->cardtype);
+       if (retval) {
+               printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
+                      card->port, retval);
+               retval = -ENODEV;
+               goto err_release_region;
+       }
+       b1_reset(card->port);
+       b1_getrevision(card);
+
+       retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
+       if (retval) {
+               printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq);
+               retval = -EBUSY;
+               goto err_release_region;
+       }
+
+       cinfo->capi_ctrl.driver_name   = "b1pci";
+       cinfo->capi_ctrl.driverdata    = cinfo;
+       cinfo->capi_ctrl.register_appl = b1_register_appl;
+       cinfo->capi_ctrl.release_appl  = b1_release_appl;
+       cinfo->capi_ctrl.send_message  = b1_send_message;
+       cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+       cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
+       cinfo->capi_ctrl.procinfo      = b1pci_procinfo;
+       cinfo->capi_ctrl.proc_show     = b1_proc_show;
+       strcpy(cinfo->capi_ctrl.name, card->name);
+       cinfo->capi_ctrl.owner         = THIS_MODULE;
+
+       retval = attach_capi_ctr(&cinfo->capi_ctrl);
+       if (retval) {
+               printk(KERN_ERR "b1pci: attach controller failed.\n");
+               goto err_free_irq;
+       }
+
+       if (card->revision >= 4) {
+               printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n",
+                      card->port, card->irq, card->revision);
+       } else {
+               printk(KERN_INFO "b1pci: AVM B1 PCI at i/o %#x, irq %d, revision %d\n",
+                      card->port, card->irq, card->revision);
+       }
+
+       pci_set_drvdata(pdev, card);
+       return 0;
+
+err_free_irq:
+       free_irq(card->irq, card);
+err_release_region:
+       release_region(card->port, AVMB1_PORTLEN);
+err_free:
+       b1_free_card(card);
+err:
+       return retval;
+}
+
+static void b1pci_remove(struct pci_dev *pdev)
+{
+       avmcard *card = pci_get_drvdata(pdev);
+       avmctrl_info *cinfo = card->ctrlinfo;
+       unsigned int port = card->port;
+
+       b1_reset(port);
+       b1_reset(port);
+
+       detach_capi_ctr(&cinfo->capi_ctrl);
+       free_irq(card->irq, card);
+       release_region(card->port, AVMB1_PORTLEN);
+       b1_free_card(card);
+}
+
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+/* ------------------------------------------------------------- */
+
+static char *b1pciv4_procinfo(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->port : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               cinfo->card ? cinfo->card->membase : 0,
+               cinfo->card ? cinfo->card->revision : 0
+               );
+       return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1pciv4_probe(struct capicardparams *p, struct pci_dev *pdev)
+{
+       avmcard *card;
+       avmctrl_info *cinfo;
+       int retval;
+
+       card = b1_alloc_card(1);
+       if (!card) {
+               printk(KERN_WARNING "b1pci: no memory.\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       card->dma = avmcard_dma_alloc("b1pci", pdev, 2048 + 128, 2048 + 128);
+       if (!card->dma) {
+               printk(KERN_WARNING "b1pci: dma alloc.\n");
+               retval = -ENOMEM;
+               goto err_free;
+       }
+
+       cinfo = card->ctrlinfo;
+       sprintf(card->name, "b1pciv4-%x", p->port);
+       card->port = p->port;
+       card->irq = p->irq;
+       card->membase = p->membase;
+       card->cardtype = avm_b1pci;
+
+       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+               printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
+                      card->port, card->port + AVMB1_PORTLEN);
+               retval = -EBUSY;
+               goto err_free_dma;
+       }
+
+       card->mbase = ioremap(card->membase, 64);
+       if (!card->mbase) {
+               printk(KERN_NOTICE "b1pci: can't remap memory at 0x%lx\n",
+                      card->membase);
+               retval = -ENOMEM;
+               goto err_release_region;
+       }
+
+       b1dma_reset(card);
+
+       retval = b1pciv4_detect(card);
+       if (retval) {
+               printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
+                      card->port, retval);
+               retval = -ENODEV;
+               goto err_unmap;
+       }
+       b1dma_reset(card);
+       b1_getrevision(card);
+
+       retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
+       if (retval) {
+               printk(KERN_ERR "b1pci: unable to get IRQ %d.\n",
+                      card->irq);
+               retval = -EBUSY;
+               goto err_unmap;
+       }
+
+       cinfo->capi_ctrl.owner         = THIS_MODULE;
+       cinfo->capi_ctrl.driver_name   = "b1pciv4";
+       cinfo->capi_ctrl.driverdata    = cinfo;
+       cinfo->capi_ctrl.register_appl = b1dma_register_appl;
+       cinfo->capi_ctrl.release_appl  = b1dma_release_appl;
+       cinfo->capi_ctrl.send_message  = b1dma_send_message;
+       cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
+       cinfo->capi_ctrl.reset_ctr     = b1dma_reset_ctr;
+       cinfo->capi_ctrl.procinfo      = b1pciv4_procinfo;
+       cinfo->capi_ctrl.proc_show     = b1dma_proc_show;
+       strcpy(cinfo->capi_ctrl.name, card->name);
+
+       retval = attach_capi_ctr(&cinfo->capi_ctrl);
+       if (retval) {
+               printk(KERN_ERR "b1pci: attach controller failed.\n");
+               goto err_free_irq;
+       }
+       card->cardnr = cinfo->capi_ctrl.cnr;
+
+       printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n",
+              card->port, card->irq, card->membase, card->revision);
+
+       pci_set_drvdata(pdev, card);
+       return 0;
+
+err_free_irq:
+       free_irq(card->irq, card);
+err_unmap:
+       iounmap(card->mbase);
+err_release_region:
+       release_region(card->port, AVMB1_PORTLEN);
+err_free_dma:
+       avmcard_dma_free(card->dma);
+err_free:
+       b1_free_card(card);
+err:
+       return retval;
+
+}
+
+static void b1pciv4_remove(struct pci_dev *pdev)
+{
+       avmcard *card = pci_get_drvdata(pdev);
+       avmctrl_info *cinfo = card->ctrlinfo;
+
+       b1dma_reset(card);
+
+       detach_capi_ctr(&cinfo->capi_ctrl);
+       free_irq(card->irq, card);
+       iounmap(card->mbase);
+       release_region(card->port, AVMB1_PORTLEN);
+       avmcard_dma_free(card->dma);
+       b1_free_card(card);
+}
+
+#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
+
+static int b1pci_pci_probe(struct pci_dev *pdev,
+                          const struct pci_device_id *ent)
+{
+       struct capicardparams param;
+       int retval;
+
+       if (pci_enable_device(pdev) < 0) {
+               printk(KERN_ERR "b1pci: failed to enable AVM-B1\n");
+               return -ENODEV;
+       }
+       param.irq = pdev->irq;
+
+       if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+               pci_set_master(pdev);
+#endif
+               param.membase = pci_resource_start(pdev, 0);
+               param.port = pci_resource_start(pdev, 2);
+
+               printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
+                      param.port, param.irq, param.membase);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+               retval = b1pciv4_probe(&param, pdev);
+#else
+               retval = b1pci_probe(&param, pdev);
+#endif
+               if (retval != 0) {
+                       printk(KERN_ERR "b1pci: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n",
+                              param.port, param.irq, param.membase);
+               }
+       } else {
+               param.membase = 0;
+               param.port = pci_resource_start(pdev, 1);
+
+               printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
+                      param.port, param.irq);
+               retval = b1pci_probe(&param, pdev);
+               if (retval != 0) {
+                       printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
+                              param.port, param.irq);
+               }
+       }
+       return retval;
+}
+
+static void b1pci_pci_remove(struct pci_dev *pdev)
+{
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+       avmcard *card = pci_get_drvdata(pdev);
+
+       if (card->dma)
+               b1pciv4_remove(pdev);
+       else
+               b1pci_remove(pdev);
+#else
+       b1pci_remove(pdev);
+#endif
+}
+
+static struct pci_driver b1pci_pci_driver = {
+       .name           = "b1pci",
+       .id_table       = b1pci_pci_tbl,
+       .probe          = b1pci_pci_probe,
+       .remove         = b1pci_pci_remove,
+};
+
+static struct capi_driver capi_driver_b1pci = {
+       .name           = "b1pci",
+       .revision       = "1.0",
+};
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+static struct capi_driver capi_driver_b1pciv4 = {
+       .name           = "b1pciv4",
+       .revision       = "1.0",
+};
+#endif
+
+static int __init b1pci_init(void)
+{
+       char *p;
+       char rev[32];
+       int err;
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, 32);
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+
+       err = pci_register_driver(&b1pci_pci_driver);
+       if (!err) {
+               strlcpy(capi_driver_b1pci.revision, rev, 32);
+               register_capi_driver(&capi_driver_b1pci);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+               strlcpy(capi_driver_b1pciv4.revision, rev, 32);
+               register_capi_driver(&capi_driver_b1pciv4);
+#endif
+               printk(KERN_INFO "b1pci: revision %s\n", rev);
+       }
+       return err;
+}
+
+static void __exit b1pci_exit(void)
+{
+       unregister_capi_driver(&capi_driver_b1pci);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+       unregister_capi_driver(&capi_driver_b1pciv4);
+#endif
+       pci_unregister_driver(&b1pci_pci_driver);
+}
+
+module_init(b1pci_init);
+module_exit(b1pci_exit);
diff --git a/drivers/staging/isdn/avm/b1pcmcia.c b/drivers/staging/isdn/avm/b1pcmcia.c
new file mode 100644 (file)
index 0000000..3aca16e
--- /dev/null
@@ -0,0 +1,224 @@
+/* $Id: b1pcmcia.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM B1/M1/M2 PCMCIA-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/capi.h>
+#include <linux/b1pcmcia.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM PCMCIA cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+
+       b1_reset(port);
+       b1_reset(port);
+
+       detach_capi_ctr(ctrl);
+       free_irq(card->irq, card);
+       b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static LIST_HEAD(cards);
+
+static char *b1pcmcia_procinfo(struct capi_ctr *ctrl);
+
+static int b1pcmcia_add_card(unsigned int port, unsigned irq,
+                            enum avmcardtype cardtype)
+{
+       avmctrl_info *cinfo;
+       avmcard *card;
+       char *cardname;
+       int retval;
+
+       card = b1_alloc_card(1);
+       if (!card) {
+               printk(KERN_WARNING "b1pcmcia: no memory.\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+       cinfo = card->ctrlinfo;
+
+       switch (cardtype) {
+       case avm_m1: sprintf(card->name, "m1-%x", port); break;
+       case avm_m2: sprintf(card->name, "m2-%x", port); break;
+       default: sprintf(card->name, "b1pcmcia-%x", port); break;
+       }
+       card->port = port;
+       card->irq = irq;
+       card->cardtype = cardtype;
+
+       retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
+       if (retval) {
+               printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n",
+                      card->irq);
+               retval = -EBUSY;
+               goto err_free;
+       }
+       b1_reset(card->port);
+       if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
+               printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n",
+                      card->port, retval);
+               retval = -ENODEV;
+               goto err_free_irq;
+       }
+       b1_reset(card->port);
+       b1_getrevision(card);
+
+       cinfo->capi_ctrl.owner         = THIS_MODULE;
+       cinfo->capi_ctrl.driver_name   = "b1pcmcia";
+       cinfo->capi_ctrl.driverdata    = cinfo;
+       cinfo->capi_ctrl.register_appl = b1_register_appl;
+       cinfo->capi_ctrl.release_appl  = b1_release_appl;
+       cinfo->capi_ctrl.send_message  = b1_send_message;
+       cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+       cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
+       cinfo->capi_ctrl.procinfo      = b1pcmcia_procinfo;
+       cinfo->capi_ctrl.proc_show     = b1_proc_show;
+       strcpy(cinfo->capi_ctrl.name, card->name);
+
+       retval = attach_capi_ctr(&cinfo->capi_ctrl);
+       if (retval) {
+               printk(KERN_ERR "b1pcmcia: attach controller failed.\n");
+               goto err_free_irq;
+       }
+       switch (cardtype) {
+       case avm_m1: cardname = "M1"; break;
+       case avm_m2: cardname = "M2"; break;
+       default: cardname = "B1 PCMCIA"; break;
+       }
+
+       printk(KERN_INFO "b1pcmcia: AVM %s at i/o %#x, irq %d, revision %d\n",
+              cardname, card->port, card->irq, card->revision);
+
+       list_add(&card->list, &cards);
+       return cinfo->capi_ctrl.cnr;
+
+err_free_irq:
+       free_irq(card->irq, card);
+err_free:
+       b1_free_card(card);
+err:
+       return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static char *b1pcmcia_procinfo(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->port : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               cinfo->card ? cinfo->card->revision : 0
+               );
+       return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1pcmcia_addcard_b1(unsigned int port, unsigned irq)
+{
+       return b1pcmcia_add_card(port, irq, avm_b1pcmcia);
+}
+
+int b1pcmcia_addcard_m1(unsigned int port, unsigned irq)
+{
+       return b1pcmcia_add_card(port, irq, avm_m1);
+}
+
+int b1pcmcia_addcard_m2(unsigned int port, unsigned irq)
+{
+       return b1pcmcia_add_card(port, irq, avm_m2);
+}
+
+int b1pcmcia_delcard(unsigned int port, unsigned irq)
+{
+       struct list_head *l;
+       avmcard *card;
+
+       list_for_each(l, &cards) {
+               card = list_entry(l, avmcard, list);
+               if (card->port == port && card->irq == irq) {
+                       b1pcmcia_remove_ctr(&card->ctrlinfo[0].capi_ctrl);
+                       return 0;
+               }
+       }
+       return -ESRCH;
+}
+
+EXPORT_SYMBOL(b1pcmcia_addcard_b1);
+EXPORT_SYMBOL(b1pcmcia_addcard_m1);
+EXPORT_SYMBOL(b1pcmcia_addcard_m2);
+EXPORT_SYMBOL(b1pcmcia_delcard);
+
+static struct capi_driver capi_driver_b1pcmcia = {
+       .name           = "b1pcmcia",
+       .revision       = "1.0",
+};
+
+static int __init b1pcmcia_init(void)
+{
+       char *p;
+       char rev[32];
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, 32);
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+       strlcpy(capi_driver_b1pcmcia.revision, rev, 32);
+       register_capi_driver(&capi_driver_b1pcmcia);
+       printk(KERN_INFO "b1pci: revision %s\n", rev);
+
+       return 0;
+}
+
+static void __exit b1pcmcia_exit(void)
+{
+       unregister_capi_driver(&capi_driver_b1pcmcia);
+}
+
+module_init(b1pcmcia_init);
+module_exit(b1pcmcia_exit);
diff --git a/drivers/staging/isdn/avm/c4.c b/drivers/staging/isdn/avm/c4.c
new file mode 100644 (file)
index 0000000..ac72cd2
--- /dev/null
@@ -0,0 +1,1317 @@
+/* $Id: c4.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM C4 & C2 card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <asm/io.h>
+#include <linux/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+#undef AVM_C4_DEBUG
+#undef AVM_C4_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+static bool suppress_pollack;
+
+static const struct pci_device_id c4_pci_tbl[] = {
+       { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 },
+       { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 },
+       { }                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, c4_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(suppress_pollack, bool, 0);
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+#define DC21285_DRAM_A0MR      0x40000000
+#define DC21285_DRAM_A1MR      0x40004000
+#define DC21285_DRAM_A2MR      0x40008000
+#define DC21285_DRAM_A3MR      0x4000C000
+
+#define        CAS_OFFSET      0x88
+
+#define DC21285_ARMCSR_BASE    0x42000000
+
+#define        PCI_OUT_INT_STATUS      0x30
+#define        PCI_OUT_INT_MASK        0x34
+#define        MAILBOX_0               0x50
+#define        MAILBOX_1               0x54
+#define        MAILBOX_2               0x58
+#define        MAILBOX_3               0x5C
+#define        DOORBELL                0x60
+#define        DOORBELL_SETUP          0x64
+
+#define CHAN_1_CONTROL         0x90
+#define CHAN_2_CONTROL         0xB0
+#define DRAM_TIMING            0x10C
+#define DRAM_ADDR_SIZE_0       0x110
+#define DRAM_ADDR_SIZE_1       0x114
+#define DRAM_ADDR_SIZE_2       0x118
+#define DRAM_ADDR_SIZE_3       0x11C
+#define        SA_CONTROL              0x13C
+#define        XBUS_CYCLE              0x148
+#define        XBUS_STROBE             0x14C
+#define        DBELL_PCI_MASK          0x150
+#define DBELL_SA_MASK          0x154
+
+#define SDRAM_SIZE             0x1000000
+
+/* ------------------------------------------------------------- */
+
+#define        MBOX_PEEK_POKE          MAILBOX_0
+
+#define DBELL_ADDR             0x01
+#define DBELL_DATA             0x02
+#define DBELL_RNWR             0x40
+#define DBELL_INIT             0x80
+
+/* ------------------------------------------------------------- */
+
+#define        MBOX_UP_ADDR            MAILBOX_0
+#define        MBOX_UP_LEN             MAILBOX_1
+#define        MBOX_DOWN_ADDR          MAILBOX_2
+#define        MBOX_DOWN_LEN           MAILBOX_3
+
+#define        DBELL_UP_HOST           0x00000100
+#define        DBELL_UP_ARM            0x00000200
+#define        DBELL_DOWN_HOST         0x00000400
+#define        DBELL_DOWN_ARM          0x00000800
+#define        DBELL_RESET_HOST        0x40000000
+#define        DBELL_RESET_ARM         0x80000000
+
+/* ------------------------------------------------------------- */
+
+#define        DRAM_TIMING_DEF         0x001A01A5
+#define DRAM_AD_SZ_DEF0                0x00000045
+#define DRAM_AD_SZ_NULL                0x00000000
+
+#define SA_CTL_ALLRIGHT                0x64AA0271
+
+#define        INIT_XBUS_CYCLE         0x100016DB
+#define        INIT_XBUS_STROBE        0xF1F1F1F1
+
+/* ------------------------------------------------------------- */
+
+#define        RESET_TIMEOUT           (15 * HZ)       /* 15 sec */
+#define        PEEK_POKE_TIMEOUT       (HZ / 10)       /* 0.1 sec */
+
+/* ------------------------------------------------------------- */
+
+#define c4outmeml(addr, value) writel(value, addr)
+#define c4inmeml(addr) readl(addr)
+#define c4outmemw(addr, value) writew(value, addr)
+#define c4inmemw(addr) readw(addr)
+#define c4outmemb(addr, value) writeb(value, addr)
+#define c4inmemb(addr) readb(addr)
+
+/* ------------------------------------------------------------- */
+
+static inline int wait_for_doorbell(avmcard *card, unsigned long t)
+{
+       unsigned long stop;
+
+       stop = jiffies + t;
+       while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) {
+               if (!time_before(jiffies, stop))
+                       return -1;
+               mb();
+       }
+       return 0;
+}
+
+static int c4_poke(avmcard *card,  unsigned long off, unsigned long value)
+{
+
+       if (wait_for_doorbell(card, HZ / 10) < 0)
+               return -1;
+
+       c4outmeml(card->mbase + MBOX_PEEK_POKE, off);
+       c4outmeml(card->mbase + DOORBELL, DBELL_ADDR);
+
+       if (wait_for_doorbell(card, HZ / 10) < 0)
+               return -1;
+
+       c4outmeml(card->mbase + MBOX_PEEK_POKE, value);
+       c4outmeml(card->mbase + DOORBELL, DBELL_DATA | DBELL_ADDR);
+
+       return 0;
+}
+
+static int c4_peek(avmcard *card,  unsigned long off, unsigned long *valuep)
+{
+       if (wait_for_doorbell(card, HZ / 10) < 0)
+               return -1;
+
+       c4outmeml(card->mbase + MBOX_PEEK_POKE, off);
+       c4outmeml(card->mbase + DOORBELL, DBELL_RNWR | DBELL_ADDR);
+
+       if (wait_for_doorbell(card, HZ / 10) < 0)
+               return -1;
+
+       *valuep = c4inmeml(card->mbase + MBOX_PEEK_POKE);
+
+       return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_load_t4file(avmcard *card, capiloaddatapart *t4file)
+{
+       u32 val;
+       unsigned char *dp;
+       u_int left;
+       u32 loadoff = 0;
+
+       dp = t4file->data;
+       left = t4file->len;
+       while (left >= sizeof(u32)) {
+               if (t4file->user) {
+                       if (copy_from_user(&val, dp, sizeof(val)))
+                               return -EFAULT;
+               } else {
+                       memcpy(&val, dp, sizeof(val));
+               }
+               if (c4_poke(card, loadoff, val)) {
+                       printk(KERN_ERR "%s: corrupted firmware file ?\n",
+                              card->name);
+                       return -EIO;
+               }
+               left -= sizeof(u32);
+               dp += sizeof(u32);
+               loadoff += sizeof(u32);
+       }
+       if (left) {
+               val = 0;
+               if (t4file->user) {
+                       if (copy_from_user(&val, dp, left))
+                               return -EFAULT;
+               } else {
+                       memcpy(&val, dp, left);
+               }
+               if (c4_poke(card, loadoff, val)) {
+                       printk(KERN_ERR "%s: corrupted firmware file ?\n",
+                              card->name);
+                       return -EIO;
+               }
+       }
+       return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, u8 val)
+{
+       u8 *s = *pp;
+       *s++ = val;
+       *pp = s;
+}
+
+static inline void _put_word(void **pp, u32 val)
+{
+       u8 *s = *pp;
+       *s++ = val & 0xff;
+       *s++ = (val >> 8) & 0xff;
+       *s++ = (val >> 16) & 0xff;
+       *s++ = (val >> 24) & 0xff;
+       *pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+       unsigned i = len;
+       _put_word(pp, i);
+       while (i-- > 0)
+               _put_byte(pp, *dp++);
+}
+
+static inline u8 _get_byte(void **pp)
+{
+       u8 *s = *pp;
+       u8 val;
+       val = *s++;
+       *pp = s;
+       return val;
+}
+
+static inline u32 _get_word(void **pp)
+{
+       u8 *s = *pp;
+       u32 val;
+       val = *s++;
+       val |= (*s++ << 8);
+       val |= (*s++ << 16);
+       val |= (*s++ << 24);
+       *pp = s;
+       return val;
+}
+
+static inline u32 _get_slice(void **pp, unsigned char *dp)
+{
+       unsigned int len, i;
+
+       len = i = _get_word(pp);
+       while (i-- > 0) *dp++ = _get_byte(pp);
+       return len;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_reset(avmcard *card)
+{
+       unsigned long stop;
+
+       c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM);
+
+       stop = jiffies + HZ * 10;
+       while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) {
+               if (!time_before(jiffies, stop))
+                       return;
+               c4outmeml(card->mbase + DOORBELL, DBELL_ADDR);
+               mb();
+       }
+
+       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_detect(avmcard *card)
+{
+       unsigned long stop, dummy;
+
+       c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c);
+       if (c4inmeml(card->mbase + PCI_OUT_INT_MASK) != 0x0c)
+               return  1;
+
+       c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM);
+
+       stop = jiffies + HZ * 10;
+       while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) {
+               if (!time_before(jiffies, stop))
+                       return 2;
+               c4outmeml(card->mbase + DOORBELL, DBELL_ADDR);
+               mb();
+       }
+
+       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+       c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+
+       c4outmeml(card->mbase + MAILBOX_0, 0x55aa55aa);
+       if (c4inmeml(card->mbase + MAILBOX_0) != 0x55aa55aa) return 3;
+
+       c4outmeml(card->mbase + MAILBOX_0, 0xaa55aa55);
+       if (c4inmeml(card->mbase + MAILBOX_0) != 0xaa55aa55) return 4;
+
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_SA_MASK, 0)) return 5;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_PCI_MASK, 0)) return 6;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + SA_CONTROL, SA_CTL_ALLRIGHT))
+               return 7;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_CYCLE, INIT_XBUS_CYCLE))
+               return 8;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_STROBE, INIT_XBUS_STROBE))
+               return 8;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, 0)) return 9;
+
+       mdelay(1);
+
+       if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10;
+       if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11;
+       if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12;
+       if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13;
+
+       if (c4_poke(card, DC21285_DRAM_A0MR + CAS_OFFSET, 0)) return 14;
+       if (c4_poke(card, DC21285_DRAM_A1MR + CAS_OFFSET, 0)) return 15;
+       if (c4_poke(card, DC21285_DRAM_A2MR + CAS_OFFSET, 0)) return 16;
+       if (c4_poke(card, DC21285_DRAM_A3MR + CAS_OFFSET, 0)) return 17;
+
+       mdelay(1);
+
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, DRAM_TIMING_DEF))
+               return 18;
+
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_0, DRAM_AD_SZ_DEF0))
+               return 19;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_1, DRAM_AD_SZ_NULL))
+               return 20;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_2, DRAM_AD_SZ_NULL))
+               return 21;
+       if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_3, DRAM_AD_SZ_NULL))
+               return 22;
+
+       /* Transputer test */
+
+       if (c4_poke(card, 0x000000, 0x11111111)
+           || c4_poke(card, 0x400000, 0x22222222)
+              || c4_poke(card, 0x800000, 0x33333333)
+              || c4_poke(card, 0xC00000, 0x44444444))
+               return 23;
+
+       if (c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111
+           || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222
+              || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333
+              || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444)
+               return 24;
+
+       if (c4_poke(card, 0x000000, 0x55555555)
+           || c4_poke(card, 0x400000, 0x66666666)
+              || c4_poke(card, 0x800000, 0x77777777)
+              || c4_poke(card, 0xC00000, 0x88888888))
+               return 25;
+
+       if (c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555
+           || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666
+              || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777
+              || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888)
+               return 26;
+
+       return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card)
+{
+       avmcard_dmainfo *dma = card->dma;
+       struct sk_buff *skb;
+       u8 cmd, subcmd;
+       u16 len;
+       u32 txlen;
+       void *p;
+
+
+       if (card->csr & DBELL_DOWN_ARM) { /* tx busy */
+               return;
+       }
+
+       skb = skb_dequeue(&dma->send_queue);
+       if (!skb) {
+#ifdef AVM_C4_DEBUG
+               printk(KERN_DEBUG "%s: tx underrun\n", card->name);
+#endif
+               return;
+       }
+
+       len = CAPIMSG_LEN(skb->data);
+
+       if (len) {
+               cmd = CAPIMSG_COMMAND(skb->data);
+               subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+               p = dma->sendbuf.dmabuf;
+
+               if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+                       u16 dlen = CAPIMSG_DATALEN(skb->data);
+                       _put_byte(&p, SEND_DATA_B3_REQ);
+                       _put_slice(&p, skb->data, len);
+                       _put_slice(&p, skb->data + len, dlen);
+               } else {
+                       _put_byte(&p, SEND_MESSAGE);
+                       _put_slice(&p, skb->data, len);
+               }
+               txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
+#ifdef AVM_C4_DEBUG
+               printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen);
+#endif
+       } else {
+               txlen = skb->len - 2;
+#ifdef AVM_C4_POLLDEBUG
+               if (skb->data[2] == SEND_POLLACK)
+                       printk(KERN_INFO "%s: ack to c4\n", card->name);
+#endif
+#ifdef AVM_C4_DEBUG
+               printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n",
+                      card->name, skb->data[2], txlen);
+#endif
+               skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+                                                skb->len - 2);
+       }
+       txlen = (txlen + 3) & ~3;
+
+       c4outmeml(card->mbase + MBOX_DOWN_ADDR, dma->sendbuf.dmaaddr);
+       c4outmeml(card->mbase + MBOX_DOWN_LEN, txlen);
+
+       card->csr |= DBELL_DOWN_ARM;
+
+       c4outmeml(card->mbase + DOORBELL, DBELL_DOWN_ARM);
+
+       dev_kfree_skb_any(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+       struct sk_buff *skb;
+       void *p;
+
+       skb = alloc_skb(3, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+                      card->name);
+               return;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_POLLACK);
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       skb_queue_tail(&card->dma->send_queue, skb);
+       c4_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_handle_rx(avmcard *card)
+{
+       avmcard_dmainfo *dma = card->dma;
+       struct capi_ctr *ctrl;
+       avmctrl_info *cinfo;
+       struct sk_buff *skb;
+       void *p = dma->recvbuf.dmabuf;
+       u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+       u8 b1cmd =  _get_byte(&p);
+       u32 cidx;
+
+
+#ifdef AVM_C4_DEBUG
+       printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name,
+              b1cmd, (unsigned long)dma->recvlen);
+#endif
+
+       switch (b1cmd) {
+       case RECEIVE_DATA_B3_IND:
+
+               ApplId = (unsigned) _get_word(&p);
+               MsgLen = _get_slice(&p, card->msgbuf);
+               DataB3Len = _get_slice(&p, card->databuf);
+               cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+               if (cidx >= card->nlogcontr) cidx = 0;
+               ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+
+               if (MsgLen < 30) { /* not CAPI 64Bit */
+                       memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
+                       MsgLen = 30;
+                       CAPIMSG_SETLEN(card->msgbuf, 30);
+               }
+               if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
+                       printk(KERN_ERR "%s: incoming packet dropped\n",
+                              card->name);
+               } else {
+                       skb_put_data(skb, card->msgbuf, MsgLen);
+                       skb_put_data(skb, card->databuf, DataB3Len);
+                       capi_ctr_handle_message(ctrl, ApplId, skb);
+               }
+               break;
+
+       case RECEIVE_MESSAGE:
+
+               ApplId = (unsigned) _get_word(&p);
+               MsgLen = _get_slice(&p, card->msgbuf);
+               cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+               if (cidx >= card->nlogcontr) cidx = 0;
+               cinfo = &card->ctrlinfo[cidx];
+               ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+
+               if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+                       printk(KERN_ERR "%s: incoming packet dropped\n",
+                              card->name);
+               } else {
+                       skb_put_data(skb, card->msgbuf, MsgLen);
+                       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+                               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+                                                    CAPIMSG_NCCI(skb->data),
+                                                    CAPIMSG_MSGID(skb->data));
+
+                       capi_ctr_handle_message(ctrl, ApplId, skb);
+               }
+               break;
+
+       case RECEIVE_NEW_NCCI:
+
+               ApplId = _get_word(&p);
+               NCCI = _get_word(&p);
+               WindowSize = _get_word(&p);
+               cidx = (NCCI & 0x7f) - card->cardnr;
+               if (cidx >= card->nlogcontr) cidx = 0;
+
+               capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize);
+
+               break;
+
+       case RECEIVE_FREE_NCCI:
+
+               ApplId = _get_word(&p);
+               NCCI = _get_word(&p);
+
+               if (NCCI != 0xffffffff) {
+                       cidx = (NCCI & 0x7f) - card->cardnr;
+                       if (cidx >= card->nlogcontr) cidx = 0;
+                       capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI);
+               }
+               break;
+
+       case RECEIVE_START:
+#ifdef AVM_C4_POLLDEBUG
+               printk(KERN_INFO "%s: poll from c4\n", card->name);
+#endif
+               if (!suppress_pollack)
+                       queue_pollack(card);
+               for (cidx = 0; cidx < card->nr_controllers; cidx++) {
+                       ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+                       capi_ctr_resume_output(ctrl);
+               }
+               break;
+
+       case RECEIVE_STOP:
+               for (cidx = 0; cidx < card->nr_controllers; cidx++) {
+                       ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+                       capi_ctr_suspend_output(ctrl);
+               }
+               break;
+
+       case RECEIVE_INIT:
+
+               cidx = card->nlogcontr;
+               if (cidx >= card->nr_controllers) {
+                       printk(KERN_ERR "%s: card with %d controllers ??\n",
+                              card->name, cidx + 1);
+                       break;
+               }
+               card->nlogcontr++;
+               cinfo = &card->ctrlinfo[cidx];
+               ctrl = &cinfo->capi_ctrl;
+               cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+               b1_parse_version(cinfo);
+               printk(KERN_INFO "%s: %s-card (%s) now active\n",
+                      card->name,
+                      cinfo->version[VER_CARDTYPE],
+                      cinfo->version[VER_DRIVER]);
+               capi_ctr_ready(&cinfo->capi_ctrl);
+               break;
+
+       case RECEIVE_TASK_READY:
+               ApplId = (unsigned) _get_word(&p);
+               MsgLen = _get_slice(&p, card->msgbuf);
+               card->msgbuf[MsgLen] = 0;
+               while (MsgLen > 0
+                      && (card->msgbuf[MsgLen - 1] == '\n'
+                          || card->msgbuf[MsgLen - 1] == '\r')) {
+                       card->msgbuf[MsgLen - 1] = 0;
+                       MsgLen--;
+               }
+               printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+                      card->name, ApplId, card->msgbuf);
+               break;
+
+       case RECEIVE_DEBUGMSG:
+               MsgLen = _get_slice(&p, card->msgbuf);
+               card->msgbuf[MsgLen] = 0;
+               while (MsgLen > 0
+                      && (card->msgbuf[MsgLen - 1] == '\n'
+                          || card->msgbuf[MsgLen - 1] == '\r')) {
+                       card->msgbuf[MsgLen - 1] = 0;
+                       MsgLen--;
+               }
+               printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+               break;
+
+       default:
+               printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n",
+                      card->name, b1cmd);
+               return;
+       }
+}
+
+/* ------------------------------------------------------------- */
+
+static irqreturn_t c4_handle_interrupt(avmcard *card)
+{
+       unsigned long flags;
+       u32 status;
+
+       spin_lock_irqsave(&card->lock, flags);
+       status = c4inmeml(card->mbase + DOORBELL);
+
+       if (status & DBELL_RESET_HOST) {
+               u_int i;
+               c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c);
+               spin_unlock_irqrestore(&card->lock, flags);
+               if (card->nlogcontr == 0)
+                       return IRQ_HANDLED;
+               printk(KERN_ERR "%s: unexpected reset\n", card->name);
+               for (i = 0; i < card->nr_controllers; i++) {
+                       avmctrl_info *cinfo = &card->ctrlinfo[i];
+                       memset(cinfo->version, 0, sizeof(cinfo->version));
+                       spin_lock_irqsave(&card->lock, flags);
+                       capilib_release(&cinfo->ncci_head);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       capi_ctr_down(&cinfo->capi_ctrl);
+               }
+               card->nlogcontr = 0;
+               return IRQ_HANDLED;
+       }
+
+       status &= (DBELL_UP_HOST | DBELL_DOWN_HOST);
+       if (!status) {
+               spin_unlock_irqrestore(&card->lock, flags);
+               return IRQ_HANDLED;
+       }
+       c4outmeml(card->mbase + DOORBELL, status);
+
+       if ((status & DBELL_UP_HOST) != 0) {
+               card->dma->recvlen = c4inmeml(card->mbase + MBOX_UP_LEN);
+               c4outmeml(card->mbase + MBOX_UP_LEN, 0);
+               c4_handle_rx(card);
+               card->dma->recvlen = 0;
+               c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size);
+               c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM);
+       }
+
+       if ((status & DBELL_DOWN_HOST) != 0) {
+               card->csr &= ~DBELL_DOWN_ARM;
+               c4_dispatch_tx(card);
+       } else if (card->csr & DBELL_DOWN_HOST) {
+               if (c4inmeml(card->mbase + MBOX_DOWN_LEN) == 0) {
+                       card->csr &= ~DBELL_DOWN_ARM;
+                       c4_dispatch_tx(card);
+               }
+       }
+       spin_unlock_irqrestore(&card->lock, flags);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t c4_interrupt(int interrupt, void *devptr)
+{
+       avmcard *card = devptr;
+
+       return c4_handle_interrupt(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_send_init(avmcard *card)
+{
+       struct sk_buff *skb;
+       void *p;
+       unsigned long flags;
+
+       skb = alloc_skb(15, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+                      card->name);
+               return;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_INIT);
+       _put_word(&p, CAPI_MAXAPPL);
+       _put_word(&p, AVM_NCCI_PER_CHANNEL * 30);
+       _put_word(&p, card->cardnr - 1);
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       skb_queue_tail(&card->dma->send_queue, skb);
+       spin_lock_irqsave(&card->lock, flags);
+       c4_dispatch_tx(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static int queue_sendconfigword(avmcard *card, u32 val)
+{
+       struct sk_buff *skb;
+       unsigned long flags;
+       void *p;
+
+       skb = alloc_skb(3 + 4, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, send config\n",
+                      card->name);
+               return -ENOMEM;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_CONFIG);
+       _put_word(&p, val);
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       skb_queue_tail(&card->dma->send_queue, skb);
+       spin_lock_irqsave(&card->lock, flags);
+       c4_dispatch_tx(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+       return 0;
+}
+
+static int queue_sendconfig(avmcard *card, char cval[4])
+{
+       struct sk_buff *skb;
+       unsigned long flags;
+       void *p;
+
+       skb = alloc_skb(3 + 4, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_CRIT "%s: no memory, send config\n",
+                      card->name);
+               return -ENOMEM;
+       }
+       p = skb->data;
+       _put_byte(&p, 0);
+       _put_byte(&p, 0);
+       _put_byte(&p, SEND_CONFIG);
+       _put_byte(&p, cval[0]);
+       _put_byte(&p, cval[1]);
+       _put_byte(&p, cval[2]);
+       _put_byte(&p, cval[3]);
+       skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+       skb_queue_tail(&card->dma->send_queue, skb);
+
+       spin_lock_irqsave(&card->lock, flags);
+       c4_dispatch_tx(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+       return 0;
+}
+
+static int c4_send_config(avmcard *card, capiloaddatapart *config)
+{
+       u8 val[4];
+       unsigned char *dp;
+       u_int left;
+       int retval;
+
+       if ((retval = queue_sendconfigword(card, 1)) != 0)
+               return retval;
+       if ((retval = queue_sendconfigword(card, config->len)) != 0)
+               return retval;
+
+       dp = config->data;
+       left = config->len;
+       while (left >= sizeof(u32)) {
+               if (config->user) {
+                       if (copy_from_user(val, dp, sizeof(val)))
+                               return -EFAULT;
+               } else {
+                       memcpy(val, dp, sizeof(val));
+               }
+               if ((retval = queue_sendconfig(card, val)) != 0)
+                       return retval;
+               left -= sizeof(val);
+               dp += sizeof(val);
+       }
+       if (left) {
+               memset(val, 0, sizeof(val));
+               if (config->user) {
+                       if (copy_from_user(&val, dp, left))
+                               return -EFAULT;
+               } else {
+                       memcpy(&val, dp, left);
+               }
+               if ((retval = queue_sendconfig(card, val)) != 0)
+                       return retval;
+       }
+
+       return 0;
+}
+
+static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       int retval;
+
+       if ((retval = c4_load_t4file(card, &data->firmware))) {
+               printk(KERN_ERR "%s: failed to load t4file!!\n",
+                      card->name);
+               c4_reset(card);
+               return retval;
+       }
+
+       card->csr = 0;
+       c4outmeml(card->mbase + MBOX_UP_LEN, 0);
+       c4outmeml(card->mbase + MBOX_DOWN_LEN, 0);
+       c4outmeml(card->mbase + DOORBELL, DBELL_INIT);
+       mdelay(1);
+       c4outmeml(card->mbase + DOORBELL,
+                 DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST);
+
+       c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x08);
+
+       card->dma->recvlen = 0;
+       c4outmeml(card->mbase + MBOX_UP_ADDR, card->dma->recvbuf.dmaaddr);
+       c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size);
+       c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM);
+
+       if (data->configuration.len > 0 && data->configuration.data) {
+               retval = c4_send_config(card, &data->configuration);
+               if (retval) {
+                       printk(KERN_ERR "%s: failed to set config!!\n",
+                              card->name);
+                       c4_reset(card);
+                       return retval;
+               }
+       }
+
+       c4_send_init(card);
+
+       return 0;
+}
+
+
+static void c4_reset_ctr(struct capi_ctr *ctrl)
+{
+       avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card;
+       avmctrl_info *cinfo;
+       u_int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       c4_reset(card);
+
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       for (i = 0; i < card->nr_controllers; i++) {
+               cinfo = &card->ctrlinfo[i];
+               memset(cinfo->version, 0, sizeof(cinfo->version));
+               capi_ctr_down(&cinfo->capi_ctrl);
+       }
+       card->nlogcontr = 0;
+}
+
+static void c4_remove(struct pci_dev *pdev)
+{
+       avmcard *card = pci_get_drvdata(pdev);
+       avmctrl_info *cinfo;
+       u_int i;
+
+       if (!card)
+               return;
+
+       c4_reset(card);
+
+       for (i = 0; i < card->nr_controllers; i++) {
+               cinfo = &card->ctrlinfo[i];
+               detach_capi_ctr(&cinfo->capi_ctrl);
+       }
+
+       free_irq(card->irq, card);
+       iounmap(card->mbase);
+       release_region(card->port, AVMB1_PORTLEN);
+       avmcard_dma_free(card->dma);
+       pci_set_drvdata(pdev, NULL);
+       b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+
+static void c4_register_appl(struct capi_ctr *ctrl,
+                            u16 appl,
+                            capi_register_params *rp)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       struct sk_buff *skb;
+       int want = rp->level3cnt;
+       unsigned long flags;
+       int nconn;
+       void *p;
+
+       if (ctrl->cnr == card->cardnr) {
+
+               if (want > 0) nconn = want;
+               else nconn = ctrl->profile.nbchannel * 4 * -want;
+               if (nconn == 0) nconn = ctrl->profile.nbchannel * 4;
+
+               skb = alloc_skb(23, GFP_ATOMIC);
+               if (!skb) {
+                       printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+                              card->name);
+                       return;
+               }
+               p = skb->data;
+               _put_byte(&p, 0);
+               _put_byte(&p, 0);
+               _put_byte(&p, SEND_REGISTER);
+               _put_word(&p, appl);
+               _put_word(&p, 1024 * (nconn + 1));
+               _put_word(&p, nconn);
+               _put_word(&p, rp->datablkcnt);
+               _put_word(&p, rp->datablklen);
+               skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+               skb_queue_tail(&card->dma->send_queue, skb);
+
+               spin_lock_irqsave(&card->lock, flags);
+               c4_dispatch_tx(card);
+               spin_unlock_irqrestore(&card->lock, flags);
+       }
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned long flags;
+       struct sk_buff *skb;
+       void *p;
+
+       spin_lock_irqsave(&card->lock, flags);
+       capilib_release_appl(&cinfo->ncci_head, appl);
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       if (ctrl->cnr == card->cardnr) {
+               skb = alloc_skb(7, GFP_ATOMIC);
+               if (!skb) {
+                       printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+                              card->name);
+                       return;
+               }
+               p = skb->data;
+               _put_byte(&p, 0);
+               _put_byte(&p, 0);
+               _put_byte(&p, SEND_RELEASE);
+               _put_word(&p, appl);
+
+               skb_put(skb, (u8 *)p - (u8 *)skb->data);
+               skb_queue_tail(&card->dma->send_queue, skb);
+               spin_lock_irqsave(&card->lock, flags);
+               c4_dispatch_tx(card);
+               spin_unlock_irqrestore(&card->lock, flags);
+       }
+}
+
+/* ------------------------------------------------------------- */
+
+
+static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       u16 retval = CAPI_NOERROR;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+       if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+               retval = capilib_data_b3_req(&cinfo->ncci_head,
+                                            CAPIMSG_APPID(skb->data),
+                                            CAPIMSG_NCCI(skb->data),
+                                            CAPIMSG_MSGID(skb->data));
+       }
+       if (retval == CAPI_NOERROR) {
+               skb_queue_tail(&card->dma->send_queue, skb);
+               c4_dispatch_tx(card);
+       }
+       spin_unlock_irqrestore(&card->lock, flags);
+       return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static char *c4_procinfo(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->port : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               cinfo->card ? cinfo->card->membase : 0
+               );
+       return cinfo->infobuf;
+}
+
+static int c4_proc_show(struct seq_file *m, void *v)
+{
+       struct capi_ctr *ctrl = m->private;
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       u8 flag;
+       char *s;
+
+       seq_printf(m, "%-16s %s\n", "name", card->name);
+       seq_printf(m, "%-16s 0x%x\n", "io", card->port);
+       seq_printf(m, "%-16s %d\n", "irq", card->irq);
+       seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase);
+       switch (card->cardtype) {
+       case avm_b1isa: s = "B1 ISA"; break;
+       case avm_b1pci: s = "B1 PCI"; break;
+       case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+       case avm_m1: s = "M1"; break;
+       case avm_m2: s = "M2"; break;
+       case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+       case avm_t1pci: s = "T1 PCI"; break;
+       case avm_c4: s = "C4"; break;
+       case avm_c2: s = "C2"; break;
+       default: s = "???"; break;
+       }
+       seq_printf(m, "%-16s %s\n", "type", s);
+       if ((s = cinfo->version[VER_DRIVER]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_driver", s);
+       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
+       if ((s = cinfo->version[VER_SERIAL]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_serial", s);
+
+       if (card->cardtype != avm_m1) {
+               flag = ((u8 *)(ctrl->profile.manu))[3];
+               if (flag)
+                       seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
+                                  "protocol",
+                                  (flag & 0x01) ? " DSS1" : "",
+                                  (flag & 0x02) ? " CT1" : "",
+                                  (flag & 0x04) ? " VN3" : "",
+                                  (flag & 0x08) ? " NI1" : "",
+                                  (flag & 0x10) ? " AUSTEL" : "",
+                                  (flag & 0x20) ? " ESS" : "",
+                                  (flag & 0x40) ? " 1TR6" : ""
+                               );
+       }
+       if (card->cardtype != avm_m1) {
+               flag = ((u8 *)(ctrl->profile.manu))[5];
+               if (flag)
+                       seq_printf(m, "%-16s%s%s%s%s\n",
+                                  "linetype",
+                                  (flag & 0x01) ? " point to point" : "",
+                                  (flag & 0x02) ? " point to multipoint" : "",
+                                  (flag & 0x08) ? " leased line without D-channel" : "",
+                                  (flag & 0x04) ? " leased line with D-channel" : ""
+                               );
+       }
+       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
+
+       return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_add_card(struct capicardparams *p, struct pci_dev *dev,
+                      int nr_controllers)
+{
+       avmcard *card;
+       avmctrl_info *cinfo;
+       int retval;
+       int i;
+
+       card = b1_alloc_card(nr_controllers);
+       if (!card) {
+               printk(KERN_WARNING "c4: no memory.\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+       card->dma = avmcard_dma_alloc("c4", dev, 2048 + 128, 2048 + 128);
+       if (!card->dma) {
+               printk(KERN_WARNING "c4: no memory.\n");
+               retval = -ENOMEM;
+               goto err_free;
+       }
+
+       sprintf(card->name, "c%d-%x", nr_controllers, p->port);
+       card->port = p->port;
+       card->irq = p->irq;
+       card->membase = p->membase;
+       card->cardtype = (nr_controllers == 4) ? avm_c4 : avm_c2;
+
+       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+               printk(KERN_WARNING "c4: ports 0x%03x-0x%03x in use.\n",
+                      card->port, card->port + AVMB1_PORTLEN);
+               retval = -EBUSY;
+               goto err_free_dma;
+       }
+
+       card->mbase = ioremap(card->membase, 128);
+       if (card->mbase == NULL) {
+               printk(KERN_NOTICE "c4: can't remap memory at 0x%lx\n",
+                      card->membase);
+               retval = -EIO;
+               goto err_release_region;
+       }
+
+       retval = c4_detect(card);
+       if (retval != 0) {
+               printk(KERN_NOTICE "c4: NO card at 0x%x error(%d)\n",
+                      card->port, retval);
+               retval = -EIO;
+               goto err_unmap;
+       }
+       c4_reset(card);
+
+       retval = request_irq(card->irq, c4_interrupt, IRQF_SHARED, card->name, card);
+       if (retval) {
+               printk(KERN_ERR "c4: unable to get IRQ %d.\n", card->irq);
+               retval = -EBUSY;
+               goto err_unmap;
+       }
+
+       for (i = 0; i < nr_controllers; i++) {
+               cinfo = &card->ctrlinfo[i];
+               cinfo->capi_ctrl.owner = THIS_MODULE;
+               cinfo->capi_ctrl.driver_name   = "c4";
+               cinfo->capi_ctrl.driverdata    = cinfo;
+               cinfo->capi_ctrl.register_appl = c4_register_appl;
+               cinfo->capi_ctrl.release_appl  = c4_release_appl;
+               cinfo->capi_ctrl.send_message  = c4_send_message;
+               cinfo->capi_ctrl.load_firmware = c4_load_firmware;
+               cinfo->capi_ctrl.reset_ctr     = c4_reset_ctr;
+               cinfo->capi_ctrl.procinfo      = c4_procinfo;
+               cinfo->capi_ctrl.proc_show     = c4_proc_show;
+               strcpy(cinfo->capi_ctrl.name, card->name);
+
+               retval = attach_capi_ctr(&cinfo->capi_ctrl);
+               if (retval) {
+                       printk(KERN_ERR "c4: attach controller failed (%d).\n", i);
+                       for (i--; i >= 0; i--) {
+                               cinfo = &card->ctrlinfo[i];
+                               detach_capi_ctr(&cinfo->capi_ctrl);
+                       }
+                       goto err_free_irq;
+               }
+               if (i == 0)
+                       card->cardnr = cinfo->capi_ctrl.cnr;
+       }
+
+       printk(KERN_INFO "c4: AVM C%d at i/o %#x, irq %d, mem %#lx\n",
+              nr_controllers, card->port, card->irq,
+              card->membase);
+       pci_set_drvdata(dev, card);
+       return 0;
+
+err_free_irq:
+       free_irq(card->irq, card);
+err_unmap:
+       iounmap(card->mbase);
+err_release_region:
+       release_region(card->port, AVMB1_PORTLEN);
+err_free_dma:
+       avmcard_dma_free(card->dma);
+err_free:
+       b1_free_card(card);
+err:
+       return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+       int nr = ent->driver_data;
+       int retval = 0;
+       struct capicardparams param;
+
+       if (pci_enable_device(dev) < 0) {
+               printk(KERN_ERR "c4: failed to enable AVM-C%d\n", nr);
+               return -ENODEV;
+       }
+       pci_set_master(dev);
+
+       param.port = pci_resource_start(dev, 1);
+       param.irq = dev->irq;
+       param.membase = pci_resource_start(dev, 0);
+
+       printk(KERN_INFO "c4: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n",
+              nr, param.port, param.irq, param.membase);
+
+       retval = c4_add_card(&param, dev, nr);
+       if (retval != 0) {
+               printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n",
+                      nr, param.port, param.irq, param.membase);
+               pci_disable_device(dev);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static struct pci_driver c4_pci_driver = {
+       .name           = "c4",
+       .id_table       = c4_pci_tbl,
+       .probe          = c4_probe,
+       .remove         = c4_remove,
+};
+
+static struct capi_driver capi_driver_c2 = {
+       .name           = "c2",
+       .revision       = "1.0",
+};
+
+static struct capi_driver capi_driver_c4 = {
+       .name           = "c4",
+       .revision       = "1.0",
+};
+
+static int __init c4_init(void)
+{
+       char *p;
+       char rev[32];
+       int err;
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, 32);
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+       err = pci_register_driver(&c4_pci_driver);
+       if (!err) {
+               strlcpy(capi_driver_c2.revision, rev, 32);
+               register_capi_driver(&capi_driver_c2);
+               strlcpy(capi_driver_c4.revision, rev, 32);
+               register_capi_driver(&capi_driver_c4);
+               printk(KERN_INFO "c4: revision %s\n", rev);
+       }
+       return err;
+}
+
+static void __exit c4_exit(void)
+{
+       unregister_capi_driver(&capi_driver_c2);
+       unregister_capi_driver(&capi_driver_c4);
+       pci_unregister_driver(&c4_pci_driver);
+}
+
+module_init(c4_init);
+module_exit(c4_exit);
diff --git a/drivers/staging/isdn/avm/t1isa.c b/drivers/staging/isdn/avm/t1isa.c
new file mode 100644 (file)
index 0000000..2153619
--- /dev/null
@@ -0,0 +1,594 @@
+/* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ *
+ * Module for AVM T1 HEMA-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/netdevice.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/gfp.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static int hema_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 0x80,                         /* irq 3 */
+ 0,
+ 0x90,                         /* irq 5 */
+ 0,
+ 0xA0,                         /* irq 7 */
+ 0,
+ 0xB0,                         /* irq 9 */
+ 0xC0,                         /* irq 10 */
+ 0xD0,                         /* irq 11 */
+ 0xE0,                         /* irq 12 */
+ 0,
+ 0,
+ 0xF0,                         /* irq 15 */
+};
+
+static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr)
+{
+       unsigned char cregs[8];
+       unsigned char reverse_cardnr;
+       unsigned char dummy;
+       int i;
+
+       reverse_cardnr =   ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
+               | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
+       cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
+       cregs[1] = 0x00; /* fast & slow link connected to CON1 */
+       cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
+       cregs[3] = 0;
+       cregs[4] = 0x11; /* zero wait state */
+       cregs[5] = hema_irq_table[irq & 0xf];
+       cregs[6] = 0;
+       cregs[7] = 0;
+
+       /*
+        * no one else should use the ISA bus in this moment,
+        * but no function there to prevent this :-(
+        * save_flags(flags); cli();
+        */
+
+       /* board reset */
+       t1outp(base, T1_RESETBOARD, 0xf);
+       mdelay(100);
+       dummy = t1inp(base, T1_FASTLINK + T1_OUTSTAT); /* first read */
+
+       /* write config */
+       dummy = (base >> 4) & 0xff;
+       for (i = 1; i <= 0xf; i++) t1outp(base, i, dummy);
+       t1outp(base, HEMA_PAL_ID & 0xf, dummy);
+       t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
+       for (i = 1; i < 7; i++) t1outp(base, 0, cregs[i]);
+       t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
+       /* restore_flags(flags); */
+
+       mdelay(100);
+       t1outp(base, T1_FASTLINK + T1_RESETLINK, 0);
+       t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0);
+       mdelay(10);
+       t1outp(base, T1_FASTLINK + T1_RESETLINK, 1);
+       t1outp(base, T1_SLOWLINK + T1_RESETLINK, 1);
+       mdelay(100);
+       t1outp(base, T1_FASTLINK + T1_RESETLINK, 0);
+       t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0);
+       mdelay(10);
+       t1outp(base, T1_FASTLINK + T1_ANALYSE, 0);
+       mdelay(5);
+       t1outp(base, T1_SLOWLINK + T1_ANALYSE, 0);
+
+       if (t1inp(base, T1_FASTLINK + T1_OUTSTAT) != 0x1) /* tx empty */
+               return 1;
+       if (t1inp(base, T1_FASTLINK + T1_INSTAT) != 0x0) /* rx empty */
+               return 2;
+       if (t1inp(base, T1_FASTLINK + T1_IRQENABLE) != 0x0)
+               return 3;
+       if ((t1inp(base, T1_FASTLINK + T1_FIFOSTAT) & 0xf0) != 0x70)
+               return 4;
+       if ((t1inp(base, T1_FASTLINK + T1_IRQMASTER) & 0x0e) != 0)
+               return 5;
+       if ((t1inp(base, T1_FASTLINK + T1_IDENT) & 0x7d) != 1)
+               return 6;
+       if (t1inp(base, T1_SLOWLINK + T1_OUTSTAT) != 0x1) /* tx empty */
+               return 7;
+       if ((t1inp(base, T1_SLOWLINK + T1_IRQMASTER) & 0x0e) != 0)
+               return 8;
+       if ((t1inp(base, T1_SLOWLINK + T1_IDENT) & 0x7d) != 0)
+               return 9;
+       return 0;
+}
+
+static irqreturn_t t1isa_interrupt(int interrupt, void *devptr)
+{
+       avmcard *card = devptr;
+       avmctrl_info *cinfo = &card->ctrlinfo[0];
+       struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+       unsigned char b1cmd;
+       struct sk_buff *skb;
+
+       unsigned ApplId;
+       unsigned MsgLen;
+       unsigned DataB3Len;
+       unsigned NCCI;
+       unsigned WindowSize;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       while (b1_rx_full(card->port)) {
+
+               b1cmd = b1_get_byte(card->port);
+
+               switch (b1cmd) {
+
+               case RECEIVE_DATA_B3_IND:
+
+                       ApplId = (unsigned) b1_get_word(card->port);
+                       MsgLen = t1_get_slice(card->port, card->msgbuf);
+                       DataB3Len = t1_get_slice(card->port, card->databuf);
+                       spin_unlock_irqrestore(&card->lock, flags);
+
+                       if (MsgLen < 30) { /* not CAPI 64Bit */
+                               memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
+                               MsgLen = 30;
+                               CAPIMSG_SETLEN(card->msgbuf, 30);
+                       }
+                       if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
+                               printk(KERN_ERR "%s: incoming packet dropped\n",
+                                      card->name);
+                       } else {
+                               skb_put_data(skb, card->msgbuf, MsgLen);
+                               skb_put_data(skb, card->databuf, DataB3Len);
+                               capi_ctr_handle_message(ctrl, ApplId, skb);
+                       }
+                       break;
+
+               case RECEIVE_MESSAGE:
+
+                       ApplId = (unsigned) b1_get_word(card->port);
+                       MsgLen = t1_get_slice(card->port, card->msgbuf);
+                       if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+                               spin_unlock_irqrestore(&card->lock, flags);
+                               printk(KERN_ERR "%s: incoming packet dropped\n",
+                                      card->name);
+                       } else {
+                               skb_put_data(skb, card->msgbuf, MsgLen);
+                               if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3)
+                                       capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+                                                            CAPIMSG_NCCI(skb->data),
+                                                            CAPIMSG_MSGID(skb->data));
+                               spin_unlock_irqrestore(&card->lock, flags);
+                               capi_ctr_handle_message(ctrl, ApplId, skb);
+                       }
+                       break;
+
+               case RECEIVE_NEW_NCCI:
+
+                       ApplId = b1_get_word(card->port);
+                       NCCI = b1_get_word(card->port);
+                       WindowSize = b1_get_word(card->port);
+                       capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       break;
+
+               case RECEIVE_FREE_NCCI:
+
+                       ApplId = b1_get_word(card->port);
+                       NCCI = b1_get_word(card->port);
+                       if (NCCI != 0xffffffff)
+                               capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       break;
+
+               case RECEIVE_START:
+                       b1_put_byte(card->port, SEND_POLLACK);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       capi_ctr_resume_output(ctrl);
+                       break;
+
+               case RECEIVE_STOP:
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       capi_ctr_suspend_output(ctrl);
+                       break;
+
+               case RECEIVE_INIT:
+
+                       cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       b1_parse_version(cinfo);
+                       printk(KERN_INFO "%s: %s-card (%s) now active\n",
+                              card->name,
+                              cinfo->version[VER_CARDTYPE],
+                              cinfo->version[VER_DRIVER]);
+                       capi_ctr_ready(ctrl);
+                       break;
+
+               case RECEIVE_TASK_READY:
+                       ApplId = (unsigned) b1_get_word(card->port);
+                       MsgLen = t1_get_slice(card->port, card->msgbuf);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       card->msgbuf[MsgLen] = 0;
+                       while (MsgLen > 0
+                              && (card->msgbuf[MsgLen - 1] == '\n'
+                                  || card->msgbuf[MsgLen - 1] == '\r')) {
+                               card->msgbuf[MsgLen - 1] = 0;
+                               MsgLen--;
+                       }
+                       printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+                              card->name, ApplId, card->msgbuf);
+                       break;
+
+               case RECEIVE_DEBUGMSG:
+                       MsgLen = t1_get_slice(card->port, card->msgbuf);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       card->msgbuf[MsgLen] = 0;
+                       while (MsgLen > 0
+                              && (card->msgbuf[MsgLen - 1] == '\n'
+                                  || card->msgbuf[MsgLen - 1] == '\r')) {
+                               card->msgbuf[MsgLen - 1] = 0;
+                               MsgLen--;
+                       }
+                       printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+                       break;
+
+
+               case 0xff:
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       printk(KERN_ERR "%s: card reseted ?\n", card->name);
+                       return IRQ_HANDLED;
+               default:
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
+                              card->name, b1cmd);
+                       return IRQ_NONE;
+               }
+       }
+       return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+
+static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+       int retval;
+
+       t1_disable_irq(port);
+       b1_reset(port);
+
+       if ((retval = b1_load_t4file(card, &data->firmware))) {
+               b1_reset(port);
+               printk(KERN_ERR "%s: failed to load t4file!!\n",
+                      card->name);
+               return retval;
+       }
+
+       if (data->configuration.len > 0 && data->configuration.data) {
+               if ((retval = b1_load_config(card, &data->configuration))) {
+                       b1_reset(port);
+                       printk(KERN_ERR "%s: failed to load config!!\n",
+                              card->name);
+                       return retval;
+               }
+       }
+
+       if (!b1_loaded(card)) {
+               printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+               return -EIO;
+       }
+
+       spin_lock_irqsave(&card->lock, flags);
+       b1_setinterrupt(port, card->irq, card->cardtype);
+       b1_put_byte(port, SEND_INIT);
+       b1_put_word(port, CAPI_MAXAPPL);
+       b1_put_word(port, AVM_NCCI_PER_CHANNEL * 30);
+       b1_put_word(port, ctrl->cnr - 1);
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       return 0;
+}
+
+static void t1isa_reset_ctr(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+
+       t1_disable_irq(port);
+       b1_reset(port);
+       b1_reset(port);
+
+       memset(cinfo->version, 0, sizeof(cinfo->version));
+       spin_lock_irqsave(&card->lock, flags);
+       capilib_release(&cinfo->ncci_head);
+       spin_unlock_irqrestore(&card->lock, flags);
+       capi_ctr_down(ctrl);
+}
+
+static void t1isa_remove(struct pci_dev *pdev)
+{
+       avmctrl_info *cinfo = pci_get_drvdata(pdev);
+       avmcard *card;
+
+       if (!cinfo)
+               return;
+
+       card = cinfo->card;
+
+       t1_disable_irq(card->port);
+       b1_reset(card->port);
+       b1_reset(card->port);
+       t1_reset(card->port);
+
+       detach_capi_ctr(&cinfo->capi_ctrl);
+       free_irq(card->irq, card);
+       release_region(card->port, AVMB1_PORTLEN);
+       b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+static char *t1isa_procinfo(struct capi_ctr *ctrl);
+
+static int t1isa_probe(struct pci_dev *pdev, int cardnr)
+{
+       avmctrl_info *cinfo;
+       avmcard *card;
+       int retval;
+
+       card = b1_alloc_card(1);
+       if (!card) {
+               printk(KERN_WARNING "t1isa: no memory.\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       cinfo = card->ctrlinfo;
+       card->port = pci_resource_start(pdev, 0);
+       card->irq = pdev->irq;
+       card->cardtype = avm_t1isa;
+       card->cardnr = cardnr;
+       sprintf(card->name, "t1isa-%x", card->port);
+
+       if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) {
+               printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port);
+               retval = -EINVAL;
+               goto err_free;
+       }
+       if (hema_irq_table[card->irq & 0xf] == 0) {
+               printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq);
+               retval = -EINVAL;
+               goto err_free;
+       }
+       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+               printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n",
+                      card->port, card->port + AVMB1_PORTLEN);
+               retval = -EBUSY;
+               goto err_free;
+       }
+       retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card);
+       if (retval) {
+               printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq);
+               retval = -EBUSY;
+               goto err_release_region;
+       }
+
+       if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) {
+               printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n",
+                      card->port, retval);
+               retval = -ENODEV;
+               goto err_free_irq;
+       }
+       t1_disable_irq(card->port);
+       b1_reset(card->port);
+
+       cinfo->capi_ctrl.owner = THIS_MODULE;
+       cinfo->capi_ctrl.driver_name   = "t1isa";
+       cinfo->capi_ctrl.driverdata    = cinfo;
+       cinfo->capi_ctrl.register_appl = b1_register_appl;
+       cinfo->capi_ctrl.release_appl  = b1_release_appl;
+       cinfo->capi_ctrl.send_message  = t1isa_send_message;
+       cinfo->capi_ctrl.load_firmware = t1isa_load_firmware;
+       cinfo->capi_ctrl.reset_ctr     = t1isa_reset_ctr;
+       cinfo->capi_ctrl.procinfo      = t1isa_procinfo;
+       cinfo->capi_ctrl.proc_show     = b1_proc_show;
+       strcpy(cinfo->capi_ctrl.name, card->name);
+
+       retval = attach_capi_ctr(&cinfo->capi_ctrl);
+       if (retval) {
+               printk(KERN_INFO "t1isa: attach controller failed.\n");
+               goto err_free_irq;
+       }
+
+       printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
+              card->port, card->irq, card->cardnr);
+
+       pci_set_drvdata(pdev, cinfo);
+       return 0;
+
+err_free_irq:
+       free_irq(card->irq, card);
+err_release_region:
+       release_region(card->port, AVMB1_PORTLEN);
+err_free:
+       b1_free_card(card);
+err:
+       return retval;
+}
+
+static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+       avmcard *card = cinfo->card;
+       unsigned int port = card->port;
+       unsigned long flags;
+       u16 len = CAPIMSG_LEN(skb->data);
+       u8 cmd = CAPIMSG_COMMAND(skb->data);
+       u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+       u16 dlen, retval;
+
+       spin_lock_irqsave(&card->lock, flags);
+       if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+               retval = capilib_data_b3_req(&cinfo->ncci_head,
+                                            CAPIMSG_APPID(skb->data),
+                                            CAPIMSG_NCCI(skb->data),
+                                            CAPIMSG_MSGID(skb->data));
+               if (retval != CAPI_NOERROR) {
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       return retval;
+               }
+               dlen = CAPIMSG_DATALEN(skb->data);
+
+               b1_put_byte(port, SEND_DATA_B3_REQ);
+               t1_put_slice(port, skb->data, len);
+               t1_put_slice(port, skb->data + len, dlen);
+       } else {
+               b1_put_byte(port, SEND_MESSAGE);
+               t1_put_slice(port, skb->data, len);
+       }
+       spin_unlock_irqrestore(&card->lock, flags);
+       dev_kfree_skb_any(skb);
+       return CAPI_NOERROR;
+}
+/* ------------------------------------------------------------- */
+
+static char *t1isa_procinfo(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d %d",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->port : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               cinfo->card ? cinfo->card->cardnr : 0
+               );
+       return cinfo->infobuf;
+}
+
+
+/* ------------------------------------------------------------- */
+
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+static int cardnr[MAX_CARDS];
+
+module_param_hw_array(io, int, ioport, NULL, 0);
+module_param_hw_array(irq, int, irq, NULL, 0);
+module_param_array(cardnr, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)");
+
+static int t1isa_add_card(struct capi_driver *driver, capicardparams *data)
+{
+       int i;
+
+       for (i = 0; i < MAX_CARDS; i++) {
+               if (isa_dev[i].resource[0].start)
+                       continue;
+
+               isa_dev[i].resource[0].start = data->port;
+               isa_dev[i].irq = data->irq;
+
+               if (t1isa_probe(&isa_dev[i], data->cardnr) == 0)
+                       return 0;
+       }
+       return -ENODEV;
+}
+
+static struct capi_driver capi_driver_t1isa = {
+       .name           = "t1isa",
+       .revision       = "1.0",
+       .add_card       = t1isa_add_card,
+};
+
+static int __init t1isa_init(void)
+{
+       char rev[32];
+       char *p;
+       int i;
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, 32);
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+       for (i = 0; i < MAX_CARDS; i++) {
+               if (!io[i])
+                       break;
+
+               isa_dev[i].resource[0].start = io[i];
+               isa_dev[i].irq = irq[i];
+
+               if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0)
+                       return -ENODEV;
+       }
+
+       strlcpy(capi_driver_t1isa.revision, rev, 32);
+       register_capi_driver(&capi_driver_t1isa);
+       printk(KERN_INFO "t1isa: revision %s\n", rev);
+
+       return 0;
+}
+
+static void __exit t1isa_exit(void)
+{
+       int i;
+
+       unregister_capi_driver(&capi_driver_t1isa);
+       for (i = 0; i < MAX_CARDS; i++) {
+               if (!io[i])
+                       break;
+
+               t1isa_remove(&isa_dev[i]);
+       }
+}
+
+module_init(t1isa_init);
+module_exit(t1isa_exit);
diff --git a/drivers/staging/isdn/avm/t1pci.c b/drivers/staging/isdn/avm/t1pci.c
new file mode 100644 (file)
index 0000000..f5ed1d5
--- /dev/null
@@ -0,0 +1,259 @@
+/* $Id: t1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM T1 PCI-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+#undef CONFIG_T1PCI_DEBUG
+#undef CONFIG_T1PCI_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+static char *revision = "$Revision: 1.1.2.2 $";
+/* ------------------------------------------------------------- */
+
+static struct pci_device_id t1pci_pci_tbl[] = {
+       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, PCI_ANY_ID, PCI_ANY_ID },
+       { }                             /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, t1pci_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 PCI card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static char *t1pci_procinfo(struct capi_ctr *ctrl);
+
+static int t1pci_add_card(struct capicardparams *p, struct pci_dev *pdev)
+{
+       avmcard *card;
+       avmctrl_info *cinfo;
+       int retval;
+
+       card = b1_alloc_card(1);
+       if (!card) {
+               printk(KERN_WARNING "t1pci: no memory.\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       card->dma = avmcard_dma_alloc("t1pci", pdev, 2048 + 128, 2048 + 128);
+       if (!card->dma) {
+               printk(KERN_WARNING "t1pci: no memory.\n");
+               retval = -ENOMEM;
+               goto err_free;
+       }
+
+       cinfo = card->ctrlinfo;
+       sprintf(card->name, "t1pci-%x", p->port);
+       card->port = p->port;
+       card->irq = p->irq;
+       card->membase = p->membase;
+       card->cardtype = avm_t1pci;
+
+       if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+               printk(KERN_WARNING "t1pci: ports 0x%03x-0x%03x in use.\n",
+                      card->port, card->port + AVMB1_PORTLEN);
+               retval = -EBUSY;
+               goto err_free_dma;
+       }
+
+       card->mbase = ioremap(card->membase, 64);
+       if (!card->mbase) {
+               printk(KERN_NOTICE "t1pci: can't remap memory at 0x%lx\n",
+                      card->membase);
+               retval = -EIO;
+               goto err_release_region;
+       }
+
+       b1dma_reset(card);
+
+       retval = t1pci_detect(card);
+       if (retval != 0) {
+               if (retval < 6)
+                       printk(KERN_NOTICE "t1pci: NO card at 0x%x (%d)\n",
+                              card->port, retval);
+               else
+                       printk(KERN_NOTICE "t1pci: card at 0x%x, but cable not connected or T1 has no power (%d)\n",
+                              card->port, retval);
+               retval = -EIO;
+               goto err_unmap;
+       }
+       b1dma_reset(card);
+
+       retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
+       if (retval) {
+               printk(KERN_ERR "t1pci: unable to get IRQ %d.\n", card->irq);
+               retval = -EBUSY;
+               goto err_unmap;
+       }
+
+       cinfo->capi_ctrl.owner         = THIS_MODULE;
+       cinfo->capi_ctrl.driver_name   = "t1pci";
+       cinfo->capi_ctrl.driverdata    = cinfo;
+       cinfo->capi_ctrl.register_appl = b1dma_register_appl;
+       cinfo->capi_ctrl.release_appl  = b1dma_release_appl;
+       cinfo->capi_ctrl.send_message  = b1dma_send_message;
+       cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
+       cinfo->capi_ctrl.reset_ctr     = b1dma_reset_ctr;
+       cinfo->capi_ctrl.procinfo      = t1pci_procinfo;
+       cinfo->capi_ctrl.proc_show     = b1dma_proc_show;
+       strcpy(cinfo->capi_ctrl.name, card->name);
+
+       retval = attach_capi_ctr(&cinfo->capi_ctrl);
+       if (retval) {
+               printk(KERN_ERR "t1pci: attach controller failed.\n");
+               retval = -EBUSY;
+               goto err_free_irq;
+       }
+       card->cardnr = cinfo->capi_ctrl.cnr;
+
+       printk(KERN_INFO "t1pci: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n",
+              card->port, card->irq, card->membase);
+
+       pci_set_drvdata(pdev, card);
+       return 0;
+
+err_free_irq:
+       free_irq(card->irq, card);
+err_unmap:
+       iounmap(card->mbase);
+err_release_region:
+       release_region(card->port, AVMB1_PORTLEN);
+err_free_dma:
+       avmcard_dma_free(card->dma);
+err_free:
+       b1_free_card(card);
+err:
+       return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_remove(struct pci_dev *pdev)
+{
+       avmcard *card = pci_get_drvdata(pdev);
+       avmctrl_info *cinfo = card->ctrlinfo;
+
+       b1dma_reset(card);
+
+       detach_capi_ctr(&cinfo->capi_ctrl);
+       free_irq(card->irq, card);
+       iounmap(card->mbase);
+       release_region(card->port, AVMB1_PORTLEN);
+       avmcard_dma_free(card->dma);
+       b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *t1pci_procinfo(struct capi_ctr *ctrl)
+{
+       avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->port : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               cinfo->card ? cinfo->card->membase : 0
+               );
+       return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int t1pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+       struct capicardparams param;
+       int retval;
+
+       if (pci_enable_device(dev) < 0) {
+               printk(KERN_ERR "t1pci: failed to enable AVM-T1-PCI\n");
+               return -ENODEV;
+       }
+       pci_set_master(dev);
+
+       param.port = pci_resource_start(dev, 1);
+       param.irq = dev->irq;
+       param.membase = pci_resource_start(dev, 0);
+
+       printk(KERN_INFO "t1pci: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n",
+              param.port, param.irq, param.membase);
+
+       retval = t1pci_add_card(&param, dev);
+       if (retval != 0) {
+               printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n",
+                      param.port, param.irq, param.membase);
+               pci_disable_device(dev);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static struct pci_driver t1pci_pci_driver = {
+       .name           = "t1pci",
+       .id_table       = t1pci_pci_tbl,
+       .probe          = t1pci_probe,
+       .remove         = t1pci_remove,
+};
+
+static struct capi_driver capi_driver_t1pci = {
+       .name           = "t1pci",
+       .revision       = "1.0",
+};
+
+static int __init t1pci_init(void)
+{
+       char *p;
+       char rev[32];
+       int err;
+
+       if ((p = strchr(revision, ':')) != NULL && p[1]) {
+               strlcpy(rev, p + 2, 32);
+               if ((p = strchr(rev, '$')) != NULL && p > rev)
+                       *(p - 1) = 0;
+       } else
+               strcpy(rev, "1.0");
+
+       err = pci_register_driver(&t1pci_pci_driver);
+       if (!err) {
+               strlcpy(capi_driver_t1pci.revision, rev, 32);
+               register_capi_driver(&capi_driver_t1pci);
+               printk(KERN_INFO "t1pci: revision %s\n", rev);
+       }
+       return err;
+}
+
+static void __exit t1pci_exit(void)
+{
+       unregister_capi_driver(&capi_driver_t1pci);
+       pci_unregister_driver(&t1pci_pci_driver);
+}
+
+module_init(t1pci_init);
+module_exit(t1pci_exit);
diff --git a/drivers/staging/isdn/gigaset/Kconfig b/drivers/staging/isdn/gigaset/Kconfig
new file mode 100644 (file)
index 0000000..c593105
--- /dev/null
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig ISDN_DRV_GIGASET
+       tristate "Siemens Gigaset support"
+       depends on TTY
+       select CRC_CCITT
+       select BITREVERSE
+       help
+         This driver supports the Siemens Gigaset SX205/255 family of
+         ISDN DECT bases, including the predecessors Gigaset 3070/3075
+         and 4170/4175 and their T-Com versions Sinus 45isdn and Sinus
+         721X.
+         If you have one of these devices, say M here and for at least
+         one of the connection specific parts that follow.
+         This will build a module called "gigaset".
+         Note: If you build your ISDN subsystem (ISDN_CAPI or ISDN_I4L)
+         as a module, you have to build this driver as a module too,
+         otherwise the Gigaset device won't show up as an ISDN device.
+
+if ISDN_DRV_GIGASET
+
+config GIGASET_CAPI
+       bool "Gigaset CAPI support"
+       depends on ISDN_CAPI='y'||(ISDN_CAPI='m'&&ISDN_DRV_GIGASET='m')
+       default 'y'
+       help
+         Build the Gigaset driver as a CAPI 2.0 driver interfacing with
+         the Kernel CAPI subsystem. To use it with the old ISDN4Linux
+         subsystem you'll have to enable the capidrv glue driver.
+         (select ISDN_CAPI_CAPIDRV.)
+         Say N to build the old native ISDN4Linux variant.
+         If unsure, say Y.
+
+config GIGASET_BASE
+       tristate "Gigaset base station support"
+       depends on USB
+       help
+         Say M here if you want to use the USB interface of the Gigaset
+         base for connection to your system.
+         This will build a module called "bas_gigaset".
+
+config GIGASET_M105
+       tristate "Gigaset M105 support"
+       depends on USB
+       help
+         Say M here if you want to connect to the Gigaset base via DECT
+         using a Gigaset M105 (Sinus 45 Data 2) USB DECT device.
+         This will build a module called "usb_gigaset".
+
+config GIGASET_M101
+       tristate "Gigaset M101 support"
+       help
+         Say M here if you want to connect to the Gigaset base via DECT
+         using a Gigaset M101 (Sinus 45 Data 1) RS232 DECT device.
+         This will build a module called "ser_gigaset".
+
+config GIGASET_DEBUG
+       bool "Gigaset debugging"
+       help
+         This enables debugging code in the Gigaset drivers.
+         If in doubt, say yes.
+
+endif # ISDN_DRV_GIGASET
diff --git a/drivers/staging/isdn/gigaset/Makefile b/drivers/staging/isdn/gigaset/Makefile
new file mode 100644 (file)
index 0000000..9c01089
--- /dev/null
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
+
+ifdef CONFIG_GIGASET_CAPI
+gigaset-y += capi.o
+else
+gigaset-y += dummyll.o
+endif
+
+usb_gigaset-y := usb-gigaset.o
+ser_gigaset-y := ser-gigaset.o
+bas_gigaset-y := bas-gigaset.o isocdata.o
+
+obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset.o
+obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o
+obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o
+obj-$(CONFIG_GIGASET_M101) += ser_gigaset.o
diff --git a/drivers/staging/isdn/gigaset/asyncdata.c b/drivers/staging/isdn/gigaset/asyncdata.c
new file mode 100644 (file)
index 0000000..c0cbee0
--- /dev/null
@@ -0,0 +1,609 @@
+/*
+ * Common data handling layer for ser_gigaset and usb_gigaset
+ *
+ * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
+ *                       Hansjoerg Lipp <hjlipp@web.de>,
+ *                       Stefan Eilers.
+ *
+ * =====================================================================
+ *     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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/crc-ccitt.h>
+#include <linux/bitrev.h>
+#include <linux/export.h>
+
+/* check if byte must be stuffed/escaped
+ * I'm not sure which data should be encoded.
+ * Therefore I will go the hard way and encode every value
+ * less than 0x20, the flag sequence and the control escape char.
+ */
+static inline int muststuff(unsigned char c)
+{
+       if (c < PPP_TRANS) return 1;
+       if (c == PPP_FLAG) return 1;
+       if (c == PPP_ESCAPE) return 1;
+       /* other possible candidates: */
+       /* 0x91: XON with parity set */
+       /* 0x93: XOFF with parity set */
+       return 0;
+}
+
+/* == data input =========================================================== */
+
+/* process a block of received bytes in command mode
+ * (mstate != MS_LOCKED && (inputstate & INS_command))
+ * Append received bytes to the command response buffer and forward them
+ * line by line to the response handler. Exit whenever a mode/state change
+ * might have occurred.
+ * Note: Received lines may be terminated by CR, LF, or CR LF, which will be
+ * removed before passing the line to the response handler.
+ * Return value:
+ *     number of processed bytes
+ */
+static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)
+{
+       unsigned char *src = inbuf->data + inbuf->head;
+       struct cardstate *cs = inbuf->cs;
+       unsigned cbytes = cs->cbytes;
+       unsigned procbytes = 0;
+       unsigned char c;
+
+       while (procbytes < numbytes) {
+               c = *src++;
+               procbytes++;
+
+               switch (c) {
+               case '\n':
+                       if (cbytes == 0 && cs->respdata[0] == '\r') {
+                               /* collapse LF with preceding CR */
+                               cs->respdata[0] = 0;
+                               break;
+                       }
+                       /* fall through */
+               case '\r':
+                       /* end of message line, pass to response handler */
+                       if (cbytes >= MAX_RESP_SIZE) {
+                               dev_warn(cs->dev, "response too large (%d)\n",
+                                        cbytes);
+                               cbytes = MAX_RESP_SIZE;
+                       }
+                       cs->cbytes = cbytes;
+                       gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
+                                          cbytes, cs->respdata);
+                       gigaset_handle_modem_response(cs);
+                       cbytes = 0;
+
+                       /* store EOL byte for CRLF collapsing */
+                       cs->respdata[0] = c;
+
+                       /* cs->dle may have changed */
+                       if (cs->dle && !(inbuf->inputstate & INS_DLE_command))
+                               inbuf->inputstate &= ~INS_command;
+
+                       /* return for reevaluating state */
+                       goto exit;
+
+               case DLE_FLAG:
+                       if (inbuf->inputstate & INS_DLE_char) {
+                               /* quoted DLE: clear quote flag */
+                               inbuf->inputstate &= ~INS_DLE_char;
+                       } else if (cs->dle ||
+                                  (inbuf->inputstate & INS_DLE_command)) {
+                               /* DLE escape, pass up for handling */
+                               inbuf->inputstate |= INS_DLE_char;
+                               goto exit;
+                       }
+                       /* quoted or not in DLE mode: treat as regular data */
+                       /* fall through */
+               default:
+                       /* append to line buffer if possible */
+                       if (cbytes < MAX_RESP_SIZE)
+                               cs->respdata[cbytes] = c;
+                       cbytes++;
+               }
+       }
+exit:
+       cs->cbytes = cbytes;
+       return procbytes;
+}
+
+/* process a block of received bytes in lock mode
+ * All received bytes are passed unmodified to the tty i/f.
+ * Return value:
+ *     number of processed bytes
+ */
+static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)
+{
+       unsigned char *src = inbuf->data + inbuf->head;
+
+       gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src);
+       gigaset_if_receive(inbuf->cs, src, numbytes);
+       return numbytes;
+}
+
+/* process a block of received bytes in HDLC data mode
+ * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
+ * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
+ * When a frame is complete, check the FCS and pass valid frames to the LL.
+ * If DLE is encountered, return immediately to let the caller handle it.
+ * Return value:
+ *     number of processed bytes
+ */
+static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
+{
+       struct cardstate *cs = inbuf->cs;
+       struct bc_state *bcs = cs->bcs;
+       int inputstate = bcs->inputstate;
+       __u16 fcs = bcs->rx_fcs;
+       struct sk_buff *skb = bcs->rx_skb;
+       unsigned char *src = inbuf->data + inbuf->head;
+       unsigned procbytes = 0;
+       unsigned char c;
+
+       if (inputstate & INS_byte_stuff) {
+               if (!numbytes)
+                       return 0;
+               inputstate &= ~INS_byte_stuff;
+               goto byte_stuff;
+       }
+
+       while (procbytes < numbytes) {
+               c = *src++;
+               procbytes++;
+               if (c == DLE_FLAG) {
+                       if (inputstate & INS_DLE_char) {
+                               /* quoted DLE: clear quote flag */
+                               inputstate &= ~INS_DLE_char;
+                       } else if (cs->dle || (inputstate & INS_DLE_command)) {
+                               /* DLE escape, pass up for handling */
+                               inputstate |= INS_DLE_char;
+                               break;
+                       }
+               }
+
+               if (c == PPP_ESCAPE) {
+                       /* byte stuffing indicator: pull in next byte */
+                       if (procbytes >= numbytes) {
+                               /* end of buffer, save for later processing */
+                               inputstate |= INS_byte_stuff;
+                               break;
+                       }
+byte_stuff:
+                       c = *src++;
+                       procbytes++;
+                       if (c == DLE_FLAG) {
+                               if (inputstate & INS_DLE_char) {
+                                       /* quoted DLE: clear quote flag */
+                                       inputstate &= ~INS_DLE_char;
+                               } else if (cs->dle ||
+                                          (inputstate & INS_DLE_command)) {
+                                       /* DLE escape, pass up for handling */
+                                       inputstate |=
+                                               INS_DLE_char | INS_byte_stuff;
+                                       break;
+                               }
+                       }
+                       c ^= PPP_TRANS;
+#ifdef CONFIG_GIGASET_DEBUG
+                       if (!muststuff(c))
+                               gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
+#endif
+               } else if (c == PPP_FLAG) {
+                       /* end of frame: process content if any */
+                       if (inputstate & INS_have_data) {
+                               gig_dbg(DEBUG_HDLC,
+                                       "7e----------------------------");
+
+                               /* check and pass received frame */
+                               if (!skb) {
+                                       /* skipped frame */
+                                       gigaset_isdn_rcv_err(bcs);
+                               } else if (skb->len < 2) {
+                                       /* frame too short for FCS */
+                                       dev_warn(cs->dev,
+                                                "short frame (%d)\n",
+                                                skb->len);
+                                       gigaset_isdn_rcv_err(bcs);
+                                       dev_kfree_skb_any(skb);
+                               } else if (fcs != PPP_GOODFCS) {
+                                       /* frame check error */
+                                       dev_err(cs->dev,
+                                               "Checksum failed, %u bytes corrupted!\n",
+                                               skb->len);
+                                       gigaset_isdn_rcv_err(bcs);
+                                       dev_kfree_skb_any(skb);
+                               } else {
+                                       /* good frame */
+                                       __skb_trim(skb, skb->len - 2);
+                                       gigaset_skb_rcvd(bcs, skb);
+                               }
+
+                               /* prepare reception of next frame */
+                               inputstate &= ~INS_have_data;
+                               skb = gigaset_new_rx_skb(bcs);
+                       } else {
+                               /* empty frame (7E 7E) */
+#ifdef CONFIG_GIGASET_DEBUG
+                               ++bcs->emptycount;
+#endif
+                               if (!skb) {
+                                       /* skipped (?) */
+                                       gigaset_isdn_rcv_err(bcs);
+                                       skb = gigaset_new_rx_skb(bcs);
+                               }
+                       }
+
+                       fcs = PPP_INITFCS;
+                       continue;
+#ifdef CONFIG_GIGASET_DEBUG
+               } else if (muststuff(c)) {
+                       /* Should not happen. Possible after ZDLE=1<CR><LF>. */
+                       gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
+#endif
+               }
+
+               /* regular data byte, append to skb */
+#ifdef CONFIG_GIGASET_DEBUG
+               if (!(inputstate & INS_have_data)) {
+                       gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
+                               bcs->emptycount);
+                       bcs->emptycount = 0;
+               }
+#endif
+               inputstate |= INS_have_data;
+               if (skb) {
+                       if (skb->len >= bcs->rx_bufsize) {
+                               dev_warn(cs->dev, "received packet too long\n");
+                               dev_kfree_skb_any(skb);
+                               /* skip remainder of packet */
+                               bcs->rx_skb = skb = NULL;
+                       } else {
+                               __skb_put_u8(skb, c);
+                               fcs = crc_ccitt_byte(fcs, c);
+                       }
+               }
+       }
+
+       bcs->inputstate = inputstate;
+       bcs->rx_fcs = fcs;
+       return procbytes;
+}
+
+/* process a block of received bytes in transparent data mode
+ * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC)
+ * Invert bytes, undoing byte stuffing and watching for DLE escapes.
+ * If DLE is encountered, return immediately to let the caller handle it.
+ * Return value:
+ *     number of processed bytes
+ */
+static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
+{
+       struct cardstate *cs = inbuf->cs;
+       struct bc_state *bcs = cs->bcs;
+       int inputstate = bcs->inputstate;
+       struct sk_buff *skb = bcs->rx_skb;
+       unsigned char *src = inbuf->data + inbuf->head;
+       unsigned procbytes = 0;
+       unsigned char c;
+
+       if (!skb) {
+               /* skip this block */
+               gigaset_new_rx_skb(bcs);
+               return numbytes;
+       }
+
+       while (procbytes < numbytes && skb->len < bcs->rx_bufsize) {
+               c = *src++;
+               procbytes++;
+
+               if (c == DLE_FLAG) {
+                       if (inputstate & INS_DLE_char) {
+                               /* quoted DLE: clear quote flag */
+                               inputstate &= ~INS_DLE_char;
+                       } else if (cs->dle || (inputstate & INS_DLE_command)) {
+                               /* DLE escape, pass up for handling */
+                               inputstate |= INS_DLE_char;
+                               break;
+                       }
+               }
+
+               /* regular data byte: append to current skb */
+               inputstate |= INS_have_data;
+               __skb_put_u8(skb, bitrev8(c));
+       }
+
+       /* pass data up */
+       if (inputstate & INS_have_data) {
+               gigaset_skb_rcvd(bcs, skb);
+               inputstate &= ~INS_have_data;
+               gigaset_new_rx_skb(bcs);
+       }
+
+       bcs->inputstate = inputstate;
+       return procbytes;
+}
+
+/* process DLE escapes
+ * Called whenever a DLE sequence might be encountered in the input stream.
+ * Either processes the entire DLE sequence or, if that isn't possible,
+ * notes the fact that an initial DLE has been received in the INS_DLE_char
+ * inputstate flag and resumes processing of the sequence on the next call.
+ */
+static void handle_dle(struct inbuf_t *inbuf)
+{
+       struct cardstate *cs = inbuf->cs;
+
+       if (cs->mstate == MS_LOCKED)
+               return;         /* no DLE processing in lock mode */
+
+       if (!(inbuf->inputstate & INS_DLE_char)) {
+               /* no DLE pending */
+               if (inbuf->data[inbuf->head] == DLE_FLAG &&
+                   (cs->dle || inbuf->inputstate & INS_DLE_command)) {
+                       /* start of DLE sequence */
+                       inbuf->head++;
+                       if (inbuf->head == inbuf->tail ||
+                           inbuf->head == RBUFSIZE) {
+                               /* end of buffer, save for later processing */
+                               inbuf->inputstate |= INS_DLE_char;
+                               return;
+                       }
+               } else {
+                       /* regular data byte */
+                       return;
+               }
+       }
+
+       /* consume pending DLE */
+       inbuf->inputstate &= ~INS_DLE_char;
+
+       switch (inbuf->data[inbuf->head]) {
+       case 'X':       /* begin of event message */
+               if (inbuf->inputstate & INS_command)
+                       dev_notice(cs->dev,
+                                  "received <DLE>X in command mode\n");
+               inbuf->inputstate |= INS_command | INS_DLE_command;
+               inbuf->head++;  /* byte consumed */
+               break;
+       case '.':       /* end of event message */
+               if (!(inbuf->inputstate & INS_DLE_command))
+                       dev_notice(cs->dev,
+                                  "received <DLE>. without <DLE>X\n");
+               inbuf->inputstate &= ~INS_DLE_command;
+               /* return to data mode if in DLE mode */
+               if (cs->dle)
+                       inbuf->inputstate &= ~INS_command;
+               inbuf->head++;  /* byte consumed */
+               break;
+       case DLE_FLAG:  /* DLE in data stream */
+               /* mark as quoted */
+               inbuf->inputstate |= INS_DLE_char;
+               if (!(cs->dle || inbuf->inputstate & INS_DLE_command))
+                       dev_notice(cs->dev,
+                                  "received <DLE><DLE> not in DLE mode\n");
+               break;  /* quoted byte left in buffer */
+       default:
+               dev_notice(cs->dev, "received <DLE><%02x>\n",
+                          inbuf->data[inbuf->head]);
+               /* quoted byte left in buffer */
+       }
+}
+
+/**
+ * gigaset_m10x_input() - process a block of data received from the device
+ * @inbuf:     received data and device descriptor structure.
+ *
+ * Called by hardware module {ser,usb}_gigaset with a block of received
+ * bytes. Separates the bytes received over the serial data channel into
+ * user data and command replies (locked/unlocked) according to the
+ * current state of the interface.
+ */
+void gigaset_m10x_input(struct inbuf_t *inbuf)
+{
+       struct cardstate *cs = inbuf->cs;
+       unsigned numbytes, procbytes;
+
+       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail);
+
+       while (inbuf->head != inbuf->tail) {
+               /* check for DLE escape */
+               handle_dle(inbuf);
+
+               /* process a contiguous block of bytes */
+               numbytes = (inbuf->head > inbuf->tail ?
+                           RBUFSIZE : inbuf->tail) - inbuf->head;
+               gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
+               /*
+                * numbytes may be 0 if handle_dle() ate the last byte.
+                * This does no harm, *_loop() will just return 0 immediately.
+                */
+
+               if (cs->mstate == MS_LOCKED)
+                       procbytes = lock_loop(numbytes, inbuf);
+               else if (inbuf->inputstate & INS_command)
+                       procbytes = cmd_loop(numbytes, inbuf);
+               else if (cs->bcs->proto2 == L2_HDLC)
+                       procbytes = hdlc_loop(numbytes, inbuf);
+               else
+                       procbytes = iraw_loop(numbytes, inbuf);
+               inbuf->head += procbytes;
+
+               /* check for buffer wraparound */
+               if (inbuf->head >= RBUFSIZE)
+                       inbuf->head = 0;
+
+               gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head);
+       }
+}
+EXPORT_SYMBOL_GPL(gigaset_m10x_input);
+
+
+/* == data output ========================================================== */
+
+/*
+ * Encode a data packet into an octet stuffed HDLC frame with FCS,
+ * opening and closing flags, preserving headroom data.
+ * parameters:
+ *     skb             skb containing original packet (freed upon return)
+ * Return value:
+ *     pointer to newly allocated skb containing the result frame
+ *     and the original link layer header, NULL on error
+ */
+static struct sk_buff *HDLC_Encode(struct sk_buff *skb)
+{
+       struct sk_buff *hdlc_skb;
+       __u16 fcs;
+       unsigned char c;
+       unsigned char *cp;
+       int len;
+       unsigned int stuf_cnt;
+
+       stuf_cnt = 0;
+       fcs = PPP_INITFCS;
+       cp = skb->data;
+       len = skb->len;
+       while (len--) {
+               if (muststuff(*cp))
+                       stuf_cnt++;
+               fcs = crc_ccitt_byte(fcs, *cp++);
+       }
+       fcs ^= 0xffff;                  /* complement */
+
+       /* size of new buffer: original size + number of stuffing bytes
+        * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
+        * + room for link layer header
+        */
+       hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len);
+       if (!hdlc_skb) {
+               dev_kfree_skb_any(skb);
+               return NULL;
+       }
+
+       /* Copy link layer header into new skb */
+       skb_reset_mac_header(hdlc_skb);
+       skb_reserve(hdlc_skb, skb->mac_len);
+       memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len);
+       hdlc_skb->mac_len = skb->mac_len;
+
+       /* Add flag sequence in front of everything.. */
+       skb_put_u8(hdlc_skb, PPP_FLAG);
+
+       /* Perform byte stuffing while copying data. */
+       while (skb->len--) {
+               if (muststuff(*skb->data)) {
+                       skb_put_u8(hdlc_skb, PPP_ESCAPE);
+                       skb_put_u8(hdlc_skb, (*skb->data++) ^ PPP_TRANS);
+               } else
+                       skb_put_u8(hdlc_skb, *skb->data++);
+       }
+
+       /* Finally add FCS (byte stuffed) and flag sequence */
+       c = (fcs & 0x00ff);     /* least significant byte first */
+       if (muststuff(c)) {
+               skb_put_u8(hdlc_skb, PPP_ESCAPE);
+               c ^= PPP_TRANS;
+       }
+       skb_put_u8(hdlc_skb, c);
+
+       c = ((fcs >> 8) & 0x00ff);
+       if (muststuff(c)) {
+               skb_put_u8(hdlc_skb, PPP_ESCAPE);
+               c ^= PPP_TRANS;
+       }
+       skb_put_u8(hdlc_skb, c);
+
+       skb_put_u8(hdlc_skb, PPP_FLAG);
+
+       dev_kfree_skb_any(skb);
+       return hdlc_skb;
+}
+
+/*
+ * Encode a data packet into an octet stuffed raw bit inverted frame,
+ * preserving headroom data.
+ * parameters:
+ *     skb             skb containing original packet (freed upon return)
+ * Return value:
+ *     pointer to newly allocated skb containing the result frame
+ *     and the original link layer header, NULL on error
+ */
+static struct sk_buff *iraw_encode(struct sk_buff *skb)
+{
+       struct sk_buff *iraw_skb;
+       unsigned char c;
+       unsigned char *cp;
+       int len;
+
+       /* size of new buffer (worst case = every byte must be stuffed):
+        * 2 * original size + room for link layer header
+        */
+       iraw_skb = dev_alloc_skb(2 * skb->len + skb->mac_len);
+       if (!iraw_skb) {
+               dev_kfree_skb_any(skb);
+               return NULL;
+       }
+
+       /* copy link layer header into new skb */
+       skb_reset_mac_header(iraw_skb);
+       skb_reserve(iraw_skb, skb->mac_len);
+       memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len);
+       iraw_skb->mac_len = skb->mac_len;
+
+       /* copy and stuff data */
+       cp = skb->data;
+       len = skb->len;
+       while (len--) {
+               c = bitrev8(*cp++);
+               if (c == DLE_FLAG)
+                       skb_put_u8(iraw_skb, c);
+               skb_put_u8(iraw_skb, c);
+       }
+       dev_kfree_skb_any(skb);
+       return iraw_skb;
+}
+
+/**
+ * gigaset_m10x_send_skb() - queue an skb for sending
+ * @bcs:       B channel descriptor structure.
+ * @skb:       data to send.
+ *
+ * Called by LL to encode and queue an skb for sending, and start
+ * transmission if necessary.
+ * Once the payload data has been transmitted completely, gigaset_skb_sent()
+ * will be called with the skb's link layer header preserved.
+ *
+ * Return value:
+ *     number of bytes accepted for sending (skb->len) if ok,
+ *     error code < 0 (eg. -ENOMEM) on error
+ */
+int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
+{
+       struct cardstate *cs = bcs->cs;
+       unsigned len = skb->len;
+       unsigned long flags;
+
+       if (bcs->proto2 == L2_HDLC)
+               skb = HDLC_Encode(skb);
+       else
+               skb = iraw_encode(skb);
+       if (!skb) {
+               dev_err(cs->dev,
+                       "unable to allocate memory for encoding!\n");
+               return -ENOMEM;
+       }
+
+       skb_queue_tail(&bcs->squeue, skb);
+       spin_lock_irqsave(&cs->lock, flags);
+       if (cs->connected)
+               tasklet_schedule(&cs->write_tasklet);
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       return len;     /* ok so far */
+}
+EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);
diff --git a/drivers/staging/isdn/gigaset/bas-gigaset.c b/drivers/staging/isdn/gigaset/bas-gigaset.c
new file mode 100644 (file)
index 0000000..149b1ac
--- /dev/null
@@ -0,0 +1,2675 @@
+/*
+ * USB driver for Gigaset 307x base via direct USB connection.
+ *
+ * Copyright (c) 2001 by Hansjoerg Lipp <hjlipp@web.de>,
+ *                       Tilman Schmidt <tilman@imap.cc>,
+ *                       Stefan Eilers.
+ *
+ * =====================================================================
+ *     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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Tilman Schmidt <tilman@imap.cc>, Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
+#define DRIVER_DESC "USB Driver for Gigaset 307x"
+
+
+/* Module parameters */
+
+static int startmode = SM_ISDN;
+static int cidmode = 1;
+
+module_param(startmode, int, S_IRUGO);
+module_param(cidmode, int, S_IRUGO);
+MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
+MODULE_PARM_DESC(cidmode, "Call-ID mode");
+
+#define GIGASET_MINORS     1
+#define GIGASET_MINOR      16
+#define GIGASET_MODULENAME "bas_gigaset"
+#define GIGASET_DEVNAME    "ttyGB"
+
+/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
+#define IF_WRITEBUF 264
+
+/* interrupt pipe message size according to ibid. ch. 2.2 */
+#define IP_MSGSIZE 3
+
+/* Values for the Gigaset 307x */
+#define USB_GIGA_VENDOR_ID      0x0681
+#define USB_3070_PRODUCT_ID     0x0001
+#define USB_3075_PRODUCT_ID     0x0002
+#define USB_SX303_PRODUCT_ID    0x0021
+#define USB_SX353_PRODUCT_ID    0x0022
+
+/* table of devices that work with this driver */
+static const struct usb_device_id gigaset_table[] = {
+       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) },
+       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) },
+       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) },
+       { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX353_PRODUCT_ID) },
+       { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, gigaset_table);
+
+/*======================= local function prototypes ==========================*/
+
+/* function called if a new device belonging to this driver is connected */
+static int gigaset_probe(struct usb_interface *interface,
+                        const struct usb_device_id *id);
+
+/* Function will be called if the device is unplugged */
+static void gigaset_disconnect(struct usb_interface *interface);
+
+/* functions called before/after suspend */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
+static int gigaset_resume(struct usb_interface *intf);
+
+/* functions called before/after device reset */
+static int gigaset_pre_reset(struct usb_interface *intf);
+static int gigaset_post_reset(struct usb_interface *intf);
+
+static int atread_submit(struct cardstate *, int);
+static void stopurbs(struct bas_bc_state *);
+static int req_submit(struct bc_state *, int, int, int);
+static int atwrite_submit(struct cardstate *, unsigned char *, int);
+static int start_cbsend(struct cardstate *);
+
+/*============================================================================*/
+
+struct bas_cardstate {
+       struct usb_device       *udev;          /* USB device pointer */
+       struct cardstate        *cs;
+       struct usb_interface    *interface;     /* interface for this device */
+       unsigned char           minor;          /* starting minor number */
+
+       struct urb              *urb_ctrl;      /* control pipe default URB */
+       struct usb_ctrlrequest  dr_ctrl;
+       struct timer_list       timer_ctrl;     /* control request timeout */
+       int                     retry_ctrl;
+
+       struct timer_list       timer_atrdy;    /* AT command ready timeout */
+       struct urb              *urb_cmd_out;   /* for sending AT commands */
+       struct usb_ctrlrequest  dr_cmd_out;
+       int                     retry_cmd_out;
+
+       struct urb              *urb_cmd_in;    /* for receiving AT replies */
+       struct usb_ctrlrequest  dr_cmd_in;
+       struct timer_list       timer_cmd_in;   /* receive request timeout */
+       unsigned char           *rcvbuf;        /* AT reply receive buffer */
+
+       struct urb              *urb_int_in;    /* URB for interrupt pipe */
+       unsigned char           *int_in_buf;
+       struct work_struct      int_in_wq;      /* for usb_clear_halt() */
+       struct timer_list       timer_int_in;   /* int read retry delay */
+       int                     retry_int_in;
+
+       spinlock_t              lock;           /* locks all following */
+       int                     basstate;       /* bitmap (BS_*) */
+       int                     pending;        /* uncompleted base request */
+       wait_queue_head_t       waitqueue;
+       int                     rcvbuf_size;    /* size of AT receive buffer */
+                                               /* 0: no receive in progress */
+       int                     retry_cmd_in;   /* receive req retry count */
+};
+
+/* status of direct USB connection to 307x base (bits in basstate) */
+#define BS_ATOPEN      0x001   /* AT channel open */
+#define BS_B1OPEN      0x002   /* B channel 1 open */
+#define BS_B2OPEN      0x004   /* B channel 2 open */
+#define BS_ATREADY     0x008   /* base ready for AT command */
+#define BS_INIT                0x010   /* base has signalled INIT_OK */
+#define BS_ATTIMER     0x020   /* waiting for HD_READY_SEND_ATDATA */
+#define BS_ATRDPEND    0x040   /* urb_cmd_in in use */
+#define BS_ATWRPEND    0x080   /* urb_cmd_out in use */
+#define BS_SUSPEND     0x100   /* USB port suspended */
+#define BS_RESETTING   0x200   /* waiting for HD_RESET_INTERRUPT_PIPE_ACK */
+
+
+static struct gigaset_driver *driver;
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver gigaset_usb_driver = {
+       .name =         GIGASET_MODULENAME,
+       .probe =        gigaset_probe,
+       .disconnect =   gigaset_disconnect,
+       .id_table =     gigaset_table,
+       .suspend =      gigaset_suspend,
+       .resume =       gigaset_resume,
+       .reset_resume = gigaset_post_reset,
+       .pre_reset =    gigaset_pre_reset,
+       .post_reset =   gigaset_post_reset,
+       .disable_hub_initiated_lpm = 1,
+};
+
+/* get message text for usb_submit_urb return code
+ */
+static char *get_usb_rcmsg(int rc)
+{
+       static char unkmsg[28];
+
+       switch (rc) {
+       case 0:
+               return "success";
+       case -ENOMEM:
+               return "out of memory";
+       case -ENODEV:
+               return "device not present";
+       case -ENOENT:
+               return "endpoint not present";
+       case -ENXIO:
+               return "URB type not supported";
+       case -EINVAL:
+               return "invalid argument";
+       case -EAGAIN:
+               return "start frame too early or too much scheduled";
+       case -EFBIG:
+               return "too many isoc frames requested";
+       case -EPIPE:
+               return "endpoint stalled";
+       case -EMSGSIZE:
+               return "invalid packet size";
+       case -ENOSPC:
+               return "would overcommit USB bandwidth";
+       case -ESHUTDOWN:
+               return "device shut down";
+       case -EPERM:
+               return "reject flag set";
+       case -EHOSTUNREACH:
+               return "device suspended";
+       default:
+               snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", rc);
+               return unkmsg;
+       }
+}
+
+/* get message text for USB status code
+ */
+static char *get_usb_statmsg(int status)
+{
+       static char unkmsg[28];
+
+       switch (status) {
+       case 0:
+               return "success";
+       case -ENOENT:
+               return "unlinked (sync)";
+       case -EINPROGRESS:
+               return "URB still pending";
+       case -EPROTO:
+               return "bitstuff error, timeout, or unknown USB error";
+       case -EILSEQ:
+               return "CRC mismatch, timeout, or unknown USB error";
+       case -ETIME:
+               return "USB response timeout";
+       case -EPIPE:
+               return "endpoint stalled";
+       case -ECOMM:
+               return "IN buffer overrun";
+       case -ENOSR:
+               return "OUT buffer underrun";
+       case -EOVERFLOW:
+               return "endpoint babble";
+       case -EREMOTEIO:
+               return "short packet";
+       case -ENODEV:
+               return "device removed";
+       case -EXDEV:
+               return "partial isoc transfer";
+       case -EINVAL:
+               return "ISO madness";
+       case -ECONNRESET:
+               return "unlinked (async)";
+       case -ESHUTDOWN:
+               return "device shut down";
+       default:
+               snprintf(unkmsg, sizeof(unkmsg), "unknown status %d", status);
+               return unkmsg;
+       }
+}
+
+/* usb_pipetype_str
+ * retrieve string representation of USB pipe type
+ */
+static inline char *usb_pipetype_str(int pipe)
+{
+       if (usb_pipeisoc(pipe))
+               return "Isoc";
+       if (usb_pipeint(pipe))
+               return "Int";
+       if (usb_pipecontrol(pipe))
+               return "Ctrl";
+       if (usb_pipebulk(pipe))
+               return "Bulk";
+       return "?";
+}
+
+/* dump_urb
+ * write content of URB to syslog for debugging
+ */
+static inline void dump_urb(enum debuglevel level, const char *tag,
+                           struct urb *urb)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+       int i;
+       gig_dbg(level, "%s urb(0x%08lx)->{", tag, (unsigned long) urb);
+       if (urb) {
+               gig_dbg(level,
+                       "  dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, "
+                       "hcpriv=0x%08lx, transfer_flags=0x%x,",
+                       (unsigned long) urb->dev,
+                       usb_pipetype_str(urb->pipe),
+                       usb_pipeendpoint(urb->pipe), usb_pipedevice(urb->pipe),
+                       usb_pipein(urb->pipe) ? "in" : "out",
+                       (unsigned long) urb->hcpriv,
+                       urb->transfer_flags);
+               gig_dbg(level,
+                       "  transfer_buffer=0x%08lx[%d], actual_length=%d, "
+                       "setup_packet=0x%08lx,",
+                       (unsigned long) urb->transfer_buffer,
+                       urb->transfer_buffer_length, urb->actual_length,
+                       (unsigned long) urb->setup_packet);
+               gig_dbg(level,
+                       "  start_frame=%d, number_of_packets=%d, interval=%d, "
+                       "error_count=%d,",
+                       urb->start_frame, urb->number_of_packets, urb->interval,
+                       urb->error_count);
+               gig_dbg(level,
+                       "  context=0x%08lx, complete=0x%08lx, "
+                       "iso_frame_desc[]={",
+                       (unsigned long) urb->context,
+                       (unsigned long) urb->complete);
+               for (i = 0; i < urb->number_of_packets; i++) {
+                       struct usb_iso_packet_descriptor *pifd
+                               = &urb->iso_frame_desc[i];
+                       gig_dbg(level,
+                               "    {offset=%u, length=%u, actual_length=%u, "
+                               "status=%u}",
+                               pifd->offset, pifd->length, pifd->actual_length,
+                               pifd->status);
+               }
+       }
+       gig_dbg(level, "}}");
+#endif
+}
+
+/* read/set modem control bits etc. (m10x only) */
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
+                                 unsigned new_state)
+{
+       return -EINVAL;
+}
+
+static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
+{
+       return -EINVAL;
+}
+
+static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
+{
+       return -EINVAL;
+}
+
+/* set/clear bits in base connection state, return previous state
+ */
+static inline int update_basstate(struct bas_cardstate *ucs,
+                                 int set, int clear)
+{
+       unsigned long flags;
+       int state;
+
+       spin_lock_irqsave(&ucs->lock, flags);
+       state = ucs->basstate;
+       ucs->basstate = (state & ~clear) | set;
+       spin_unlock_irqrestore(&ucs->lock, flags);
+       return state;
+}
+
+/* error_hangup
+ * hang up any existing connection because of an unrecoverable error
+ * This function may be called from any context and takes care of scheduling
+ * the necessary actions for execution outside of interrupt context.
+ * cs->lock must not be held.
+ * argument:
+ *     B channel control structure
+ */
+static inline void error_hangup(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+
+       gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL);
+       gigaset_schedule_event(cs);
+}
+
+/* error_reset
+ * reset Gigaset device because of an unrecoverable error
+ * This function may be called from any context, and takes care of
+ * scheduling the necessary actions for execution outside of interrupt context.
+ * cs->hw.bas->lock must not be held.
+ * argument:
+ *     controller state structure
+ */
+static inline void error_reset(struct cardstate *cs)
+{
+       /* reset interrupt pipe to recover (ignore errors) */
+       update_basstate(cs->hw.bas, BS_RESETTING, 0);
+       if (req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT))
+               /* submission failed, escalate to USB port reset */
+               usb_queue_reset_device(cs->hw.bas->interface);
+}
+
+/* check_pending
+ * check for completion of pending control request
+ * parameter:
+ *     ucs     hardware specific controller state structure
+ */
+static void check_pending(struct bas_cardstate *ucs)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ucs->lock, flags);
+       switch (ucs->pending) {
+       case 0:
+               break;
+       case HD_OPEN_ATCHANNEL:
+               if (ucs->basstate & BS_ATOPEN)
+                       ucs->pending = 0;
+               break;
+       case HD_OPEN_B1CHANNEL:
+               if (ucs->basstate & BS_B1OPEN)
+                       ucs->pending = 0;
+               break;
+       case HD_OPEN_B2CHANNEL:
+               if (ucs->basstate & BS_B2OPEN)
+                       ucs->pending = 0;
+               break;
+       case HD_CLOSE_ATCHANNEL:
+               if (!(ucs->basstate & BS_ATOPEN))
+                       ucs->pending = 0;
+               break;
+       case HD_CLOSE_B1CHANNEL:
+               if (!(ucs->basstate & BS_B1OPEN))
+                       ucs->pending = 0;
+               break;
+       case HD_CLOSE_B2CHANNEL:
+               if (!(ucs->basstate & BS_B2OPEN))
+                       ucs->pending = 0;
+               break;
+       case HD_DEVICE_INIT_ACK:                /* no reply expected */
+               ucs->pending = 0;
+               break;
+       case HD_RESET_INTERRUPT_PIPE:
+               if (!(ucs->basstate & BS_RESETTING))
+                       ucs->pending = 0;
+               break;
+       /*
+        * HD_READ_ATMESSAGE and HD_WRITE_ATMESSAGE are handled separately
+        * and should never end up here
+        */
+       default:
+               dev_warn(&ucs->interface->dev,
+                        "unknown pending request 0x%02x cleared\n",
+                        ucs->pending);
+               ucs->pending = 0;
+       }
+
+       if (!ucs->pending)
+               del_timer(&ucs->timer_ctrl);
+
+       spin_unlock_irqrestore(&ucs->lock, flags);
+}
+
+/* cmd_in_timeout
+ * timeout routine for command input request
+ * argument:
+ *     controller state structure
+ */
+static void cmd_in_timeout(struct timer_list *t)
+{
+       struct bas_cardstate *ucs = from_timer(ucs, t, timer_cmd_in);
+       struct cardstate *cs = ucs->cs;
+       int rc;
+
+       if (!ucs->rcvbuf_size) {
+               gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__);
+               return;
+       }
+
+       if (ucs->retry_cmd_in++ >= BAS_RETRY) {
+               dev_err(cs->dev,
+                       "control read: timeout, giving up after %d tries\n",
+                       ucs->retry_cmd_in);
+               kfree(ucs->rcvbuf);
+               ucs->rcvbuf = NULL;
+               ucs->rcvbuf_size = 0;
+               error_reset(cs);
+               return;
+       }
+
+       gig_dbg(DEBUG_USBREQ, "%s: timeout, retry %d",
+               __func__, ucs->retry_cmd_in);
+       rc = atread_submit(cs, BAS_TIMEOUT);
+       if (rc < 0) {
+               kfree(ucs->rcvbuf);
+               ucs->rcvbuf = NULL;
+               ucs->rcvbuf_size = 0;
+               if (rc != -ENODEV)
+                       error_reset(cs);
+       }
+}
+
+/* read_ctrl_callback
+ * USB completion handler for control pipe input
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ *     urb     USB request block
+ *             urb->context = inbuf structure for controller state
+ */
+static void read_ctrl_callback(struct urb *urb)
+{
+       struct inbuf_t *inbuf = urb->context;
+       struct cardstate *cs = inbuf->cs;
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int status = urb->status;
+       unsigned numbytes;
+       int rc;
+
+       update_basstate(ucs, 0, BS_ATRDPEND);
+       wake_up(&ucs->waitqueue);
+       del_timer(&ucs->timer_cmd_in);
+
+       switch (status) {
+       case 0:                         /* normal completion */
+               numbytes = urb->actual_length;
+               if (unlikely(numbytes != ucs->rcvbuf_size)) {
+                       dev_warn(cs->dev,
+                                "control read: received %d chars, expected %d\n",
+                                numbytes, ucs->rcvbuf_size);
+                       if (numbytes > ucs->rcvbuf_size)
+                               numbytes = ucs->rcvbuf_size;
+               }
+
+               /* copy received bytes to inbuf, notify event layer */
+               if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) {
+                       gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+                       gigaset_schedule_event(cs);
+               }
+               break;
+
+       case -ENOENT:                   /* cancelled */
+       case -ECONNRESET:               /* cancelled (async) */
+       case -EINPROGRESS:              /* pending */
+       case -ENODEV:                   /* device removed */
+       case -ESHUTDOWN:                /* device shut down */
+               /* no further action necessary */
+               gig_dbg(DEBUG_USBREQ, "%s: %s",
+                       __func__, get_usb_statmsg(status));
+               break;
+
+       default:                        /* other errors: retry */
+               if (ucs->retry_cmd_in++ < BAS_RETRY) {
+                       gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__,
+                               get_usb_statmsg(status), ucs->retry_cmd_in);
+                       rc = atread_submit(cs, BAS_TIMEOUT);
+                       if (rc >= 0)
+                               /* successfully resubmitted, skip freeing */
+                               return;
+                       if (rc == -ENODEV)
+                               /* disconnect, no further action necessary */
+                               break;
+               }
+               dev_err(cs->dev, "control read: %s, giving up after %d tries\n",
+                       get_usb_statmsg(status), ucs->retry_cmd_in);
+               error_reset(cs);
+       }
+
+       /* read finished, free buffer */
+       kfree(ucs->rcvbuf);
+       ucs->rcvbuf = NULL;
+       ucs->rcvbuf_size = 0;
+}
+
+/* atread_submit
+ * submit an HD_READ_ATMESSAGE command URB and optionally start a timeout
+ * parameters:
+ *     cs      controller state structure
+ *     timeout timeout in 1/10 sec., 0: none
+ * return value:
+ *     0 on success
+ *     -EBUSY if another request is pending
+ *     any URB submission error code
+ */
+static int atread_submit(struct cardstate *cs, int timeout)
+{
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int basstate;
+       int ret;
+
+       gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)",
+               ucs->rcvbuf_size);
+
+       basstate = update_basstate(ucs, BS_ATRDPEND, 0);
+       if (basstate & BS_ATRDPEND) {
+               dev_err(cs->dev,
+                       "could not submit HD_READ_ATMESSAGE: URB busy\n");
+               return -EBUSY;
+       }
+
+       if (basstate & BS_SUSPEND) {
+               dev_notice(cs->dev,
+                          "HD_READ_ATMESSAGE not submitted, "
+                          "suspend in progress\n");
+               update_basstate(ucs, 0, BS_ATRDPEND);
+               /* treat like disconnect */
+               return -ENODEV;
+       }
+
+       ucs->dr_cmd_in.bRequestType = IN_VENDOR_REQ;
+       ucs->dr_cmd_in.bRequest = HD_READ_ATMESSAGE;
+       ucs->dr_cmd_in.wValue = 0;
+       ucs->dr_cmd_in.wIndex = 0;
+       ucs->dr_cmd_in.wLength = cpu_to_le16(ucs->rcvbuf_size);
+       usb_fill_control_urb(ucs->urb_cmd_in, ucs->udev,
+                            usb_rcvctrlpipe(ucs->udev, 0),
+                            (unsigned char *) &ucs->dr_cmd_in,
+                            ucs->rcvbuf, ucs->rcvbuf_size,
+                            read_ctrl_callback, cs->inbuf);
+
+       ret = usb_submit_urb(ucs->urb_cmd_in, GFP_ATOMIC);
+       if (ret != 0) {
+               update_basstate(ucs, 0, BS_ATRDPEND);
+               dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n",
+                       get_usb_rcmsg(ret));
+               return ret;
+       }
+
+       if (timeout > 0) {
+               gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout);
+               mod_timer(&ucs->timer_cmd_in, jiffies + timeout * HZ / 10);
+       }
+       return 0;
+}
+
+/* int_in_work
+ * workqueue routine to clear halt on interrupt in endpoint
+ */
+
+static void int_in_work(struct work_struct *work)
+{
+       struct bas_cardstate *ucs =
+               container_of(work, struct bas_cardstate, int_in_wq);
+       struct urb *urb = ucs->urb_int_in;
+       struct cardstate *cs = urb->context;
+       int rc;
+
+       /* clear halt condition */
+       rc = usb_clear_halt(ucs->udev, urb->pipe);
+       gig_dbg(DEBUG_USBREQ, "clear_halt: %s", get_usb_rcmsg(rc));
+       if (rc == 0)
+               /* success, resubmit interrupt read URB */
+               rc = usb_submit_urb(urb, GFP_ATOMIC);
+
+       switch (rc) {
+       case 0:         /* success */
+       case -ENODEV:   /* device gone */
+       case -EINVAL:   /* URB already resubmitted, or terminal badness */
+               break;
+       default:        /* failure: try to recover by resetting the device */
+               dev_err(cs->dev, "clear halt failed: %s\n", get_usb_rcmsg(rc));
+               rc = usb_lock_device_for_reset(ucs->udev, ucs->interface);
+               if (rc == 0) {
+                       rc = usb_reset_device(ucs->udev);
+                       usb_unlock_device(ucs->udev);
+               }
+       }
+       ucs->retry_int_in = 0;
+}
+
+/* int_in_resubmit
+ * timer routine for interrupt read delayed resubmit
+ * argument:
+ *     controller state structure
+ */
+static void int_in_resubmit(struct timer_list *t)
+{
+       struct bas_cardstate *ucs = from_timer(ucs, t, timer_int_in);
+       struct cardstate *cs = ucs->cs;
+       int rc;
+
+       if (ucs->retry_int_in++ >= BAS_RETRY) {
+               dev_err(cs->dev, "interrupt read: giving up after %d tries\n",
+                       ucs->retry_int_in);
+               usb_queue_reset_device(ucs->interface);
+               return;
+       }
+
+       gig_dbg(DEBUG_USBREQ, "%s: retry %d", __func__, ucs->retry_int_in);
+       rc = usb_submit_urb(ucs->urb_int_in, GFP_ATOMIC);
+       if (rc != 0 && rc != -ENODEV) {
+               dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
+                       get_usb_rcmsg(rc));
+               usb_queue_reset_device(ucs->interface);
+       }
+}
+
+/* read_int_callback
+ * USB completion handler for interrupt pipe input
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ *     urb     USB request block
+ *             urb->context = controller state structure
+ */
+static void read_int_callback(struct urb *urb)
+{
+       struct cardstate *cs = urb->context;
+       struct bas_cardstate *ucs = cs->hw.bas;
+       struct bc_state *bcs;
+       int status = urb->status;
+       unsigned long flags;
+       int rc;
+       unsigned l;
+       int channel;
+
+       switch (status) {
+       case 0:                 /* success */
+               ucs->retry_int_in = 0;
+               break;
+       case -EPIPE:                    /* endpoint stalled */
+               schedule_work(&ucs->int_in_wq);
+               /* fall through */
+       case -ENOENT:                   /* cancelled */
+       case -ECONNRESET:               /* cancelled (async) */
+       case -EINPROGRESS:              /* pending */
+       case -ENODEV:                   /* device removed */
+       case -ESHUTDOWN:                /* device shut down */
+               /* no further action necessary */
+               gig_dbg(DEBUG_USBREQ, "%s: %s",
+                       __func__, get_usb_statmsg(status));
+               return;
+       case -EPROTO:                   /* protocol error or unplug */
+       case -EILSEQ:
+       case -ETIME:
+               /* resubmit after delay */
+               gig_dbg(DEBUG_USBREQ, "%s: %s",
+                       __func__, get_usb_statmsg(status));
+               mod_timer(&ucs->timer_int_in, jiffies + HZ / 10);
+               return;
+       default:                /* other errors: just resubmit */
+               dev_warn(cs->dev, "interrupt read: %s\n",
+                        get_usb_statmsg(status));
+               goto resubmit;
+       }
+
+       /* drop incomplete packets even if the missing bytes wouldn't matter */
+       if (unlikely(urb->actual_length < IP_MSGSIZE)) {
+               dev_warn(cs->dev, "incomplete interrupt packet (%d bytes)\n",
+                        urb->actual_length);
+               goto resubmit;
+       }
+
+       l = (unsigned) ucs->int_in_buf[1] +
+               (((unsigned) ucs->int_in_buf[2]) << 8);
+
+       gig_dbg(DEBUG_USBREQ, "<-------%d: 0x%02x (%u [0x%02x 0x%02x])",
+               urb->actual_length, (int)ucs->int_in_buf[0], l,
+               (int)ucs->int_in_buf[1], (int)ucs->int_in_buf[2]);
+
+       channel = 0;
+
+       switch (ucs->int_in_buf[0]) {
+       case HD_DEVICE_INIT_OK:
+               update_basstate(ucs, BS_INIT, 0);
+               break;
+
+       case HD_READY_SEND_ATDATA:
+               del_timer(&ucs->timer_atrdy);
+               update_basstate(ucs, BS_ATREADY, BS_ATTIMER);
+               start_cbsend(cs);
+               break;
+
+       case HD_OPEN_B2CHANNEL_ACK:
+               ++channel;
+               /* fall through */
+       case HD_OPEN_B1CHANNEL_ACK:
+               bcs = cs->bcs + channel;
+               update_basstate(ucs, BS_B1OPEN << channel, 0);
+               gigaset_bchannel_up(bcs);
+               break;
+
+       case HD_OPEN_ATCHANNEL_ACK:
+               update_basstate(ucs, BS_ATOPEN, 0);
+               start_cbsend(cs);
+               break;
+
+       case HD_CLOSE_B2CHANNEL_ACK:
+               ++channel;
+               /* fall through */
+       case HD_CLOSE_B1CHANNEL_ACK:
+               bcs = cs->bcs + channel;
+               update_basstate(ucs, 0, BS_B1OPEN << channel);
+               stopurbs(bcs->hw.bas);
+               gigaset_bchannel_down(bcs);
+               break;
+
+       case HD_CLOSE_ATCHANNEL_ACK:
+               update_basstate(ucs, 0, BS_ATOPEN);
+               break;
+
+       case HD_B2_FLOW_CONTROL:
+               ++channel;
+               /* fall through */
+       case HD_B1_FLOW_CONTROL:
+               bcs = cs->bcs + channel;
+               atomic_add((l - BAS_NORMFRAME) * BAS_CORRFRAMES,
+                          &bcs->hw.bas->corrbytes);
+               gig_dbg(DEBUG_ISO,
+                       "Flow control (channel %d, sub %d): 0x%02x => %d",
+                       channel, bcs->hw.bas->numsub, l,
+                       atomic_read(&bcs->hw.bas->corrbytes));
+               break;
+
+       case HD_RECEIVEATDATA_ACK:      /* AT response ready to be received */
+               if (!l) {
+                       dev_warn(cs->dev,
+                                "HD_RECEIVEATDATA_ACK with length 0 ignored\n");
+                       break;
+               }
+               spin_lock_irqsave(&cs->lock, flags);
+               if (ucs->basstate & BS_ATRDPEND) {
+                       spin_unlock_irqrestore(&cs->lock, flags);
+                       dev_warn(cs->dev,
+                                "HD_RECEIVEATDATA_ACK(%d) during HD_READ_ATMESSAGE(%d) ignored\n",
+                                l, ucs->rcvbuf_size);
+                       break;
+               }
+               if (ucs->rcvbuf_size) {
+                       /* throw away previous buffer - we have no queue */
+                       dev_err(cs->dev,
+                               "receive AT data overrun, %d bytes lost\n",
+                               ucs->rcvbuf_size);
+                       kfree(ucs->rcvbuf);
+                       ucs->rcvbuf_size = 0;
+               }
+               ucs->rcvbuf = kmalloc(l, GFP_ATOMIC);
+               if (ucs->rcvbuf == NULL) {
+                       spin_unlock_irqrestore(&cs->lock, flags);
+                       dev_err(cs->dev, "out of memory receiving AT data\n");
+                       break;
+               }
+               ucs->rcvbuf_size = l;
+               ucs->retry_cmd_in = 0;
+               rc = atread_submit(cs, BAS_TIMEOUT);
+               if (rc < 0) {
+                       kfree(ucs->rcvbuf);
+                       ucs->rcvbuf = NULL;
+                       ucs->rcvbuf_size = 0;
+               }
+               spin_unlock_irqrestore(&cs->lock, flags);
+               if (rc < 0 && rc != -ENODEV)
+                       error_reset(cs);
+               break;
+
+       case HD_RESET_INTERRUPT_PIPE_ACK:
+               update_basstate(ucs, 0, BS_RESETTING);
+               dev_notice(cs->dev, "interrupt pipe reset\n");
+               break;
+
+       case HD_SUSPEND_END:
+               gig_dbg(DEBUG_USBREQ, "HD_SUSPEND_END");
+               break;
+
+       default:
+               dev_warn(cs->dev,
+                        "unknown Gigaset signal 0x%02x (%u) ignored\n",
+                        (int) ucs->int_in_buf[0], l);
+       }
+
+       check_pending(ucs);
+       wake_up(&ucs->waitqueue);
+
+resubmit:
+       rc = usb_submit_urb(urb, GFP_ATOMIC);
+       if (unlikely(rc != 0 && rc != -ENODEV)) {
+               dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
+                       get_usb_rcmsg(rc));
+               error_reset(cs);
+       }
+}
+
+/* read_iso_callback
+ * USB completion handler for B channel isochronous input
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ *     urb     USB request block of completed request
+ *             urb->context = bc_state structure
+ */
+static void read_iso_callback(struct urb *urb)
+{
+       struct bc_state *bcs;
+       struct bas_bc_state *ubc;
+       int status = urb->status;
+       unsigned long flags;
+       int i, rc;
+
+       /* status codes not worth bothering the tasklet with */
+       if (unlikely(status == -ENOENT ||
+                    status == -ECONNRESET ||
+                    status == -EINPROGRESS ||
+                    status == -ENODEV ||
+                    status == -ESHUTDOWN)) {
+               gig_dbg(DEBUG_ISO, "%s: %s",
+                       __func__, get_usb_statmsg(status));
+               return;
+       }
+
+       bcs = urb->context;
+       ubc = bcs->hw.bas;
+
+       spin_lock_irqsave(&ubc->isoinlock, flags);
+       if (likely(ubc->isoindone == NULL)) {
+               /* pass URB to tasklet */
+               ubc->isoindone = urb;
+               ubc->isoinstatus = status;
+               tasklet_hi_schedule(&ubc->rcvd_tasklet);
+       } else {
+               /* tasklet still busy, drop data and resubmit URB */
+               gig_dbg(DEBUG_ISO, "%s: overrun", __func__);
+               ubc->loststatus = status;
+               for (i = 0; i < BAS_NUMFRAMES; i++) {
+                       ubc->isoinlost += urb->iso_frame_desc[i].actual_length;
+                       if (unlikely(urb->iso_frame_desc[i].status != 0 &&
+                                    urb->iso_frame_desc[i].status != -EINPROGRESS))
+                               ubc->loststatus = urb->iso_frame_desc[i].status;
+                       urb->iso_frame_desc[i].status = 0;
+                       urb->iso_frame_desc[i].actual_length = 0;
+               }
+               if (likely(ubc->running)) {
+                       /* urb->dev is clobbered by USB subsystem */
+                       urb->dev = bcs->cs->hw.bas->udev;
+                       urb->transfer_flags = URB_ISO_ASAP;
+                       urb->number_of_packets = BAS_NUMFRAMES;
+                       rc = usb_submit_urb(urb, GFP_ATOMIC);
+                       if (unlikely(rc != 0 && rc != -ENODEV)) {
+                               dev_err(bcs->cs->dev,
+                                       "could not resubmit isoc read URB: %s\n",
+                                       get_usb_rcmsg(rc));
+                               dump_urb(DEBUG_ISO, "isoc read", urb);
+                               error_hangup(bcs);
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&ubc->isoinlock, flags);
+}
+
+/* write_iso_callback
+ * USB completion handler for B channel isochronous output
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ *     urb     USB request block of completed request
+ *             urb->context = isow_urbctx_t structure
+ */
+static void write_iso_callback(struct urb *urb)
+{
+       struct isow_urbctx_t *ucx;
+       struct bas_bc_state *ubc;
+       int status = urb->status;
+       unsigned long flags;
+
+       /* status codes not worth bothering the tasklet with */
+       if (unlikely(status == -ENOENT ||
+                    status == -ECONNRESET ||
+                    status == -EINPROGRESS ||
+                    status == -ENODEV ||
+                    status == -ESHUTDOWN)) {
+               gig_dbg(DEBUG_ISO, "%s: %s",
+                       __func__, get_usb_statmsg(status));
+               return;
+       }
+
+       /* pass URB context to tasklet */
+       ucx = urb->context;
+       ubc = ucx->bcs->hw.bas;
+       ucx->status = status;
+
+       spin_lock_irqsave(&ubc->isooutlock, flags);
+       ubc->isooutovfl = ubc->isooutdone;
+       ubc->isooutdone = ucx;
+       spin_unlock_irqrestore(&ubc->isooutlock, flags);
+       tasklet_hi_schedule(&ubc->sent_tasklet);
+}
+
+/* starturbs
+ * prepare and submit USB request blocks for isochronous input and output
+ * argument:
+ *     B channel control structure
+ * return value:
+ *     0 on success
+ *     < 0 on error (no URBs submitted)
+ */
+static int starturbs(struct bc_state *bcs)
+{
+       struct usb_device *udev = bcs->cs->hw.bas->udev;
+       struct bas_bc_state *ubc = bcs->hw.bas;
+       struct urb *urb;
+       int j, k;
+       int rc;
+
+       /* initialize L2 reception */
+       if (bcs->proto2 == L2_HDLC)
+               bcs->inputstate |= INS_flag_hunt;
+
+       /* submit all isochronous input URBs */
+       ubc->running = 1;
+       for (k = 0; k < BAS_INURBS; k++) {
+               urb = ubc->isoinurbs[k];
+               if (!urb) {
+                       rc = -EFAULT;
+                       goto error;
+               }
+               usb_fill_int_urb(urb, udev,
+                                usb_rcvisocpipe(udev, 3 + 2 * bcs->channel),
+                                ubc->isoinbuf + k * BAS_INBUFSIZE,
+                                BAS_INBUFSIZE, read_iso_callback, bcs,
+                                BAS_FRAMETIME);
+
+               urb->transfer_flags = URB_ISO_ASAP;
+               urb->number_of_packets = BAS_NUMFRAMES;
+               for (j = 0; j < BAS_NUMFRAMES; j++) {
+                       urb->iso_frame_desc[j].offset = j * BAS_MAXFRAME;
+                       urb->iso_frame_desc[j].length = BAS_MAXFRAME;
+                       urb->iso_frame_desc[j].status = 0;
+                       urb->iso_frame_desc[j].actual_length = 0;
+               }
+
+               dump_urb(DEBUG_ISO, "Initial isoc read", urb);
+               rc = usb_submit_urb(urb, GFP_ATOMIC);
+               if (rc != 0)
+                       goto error;
+       }
+
+       /* initialize L2 transmission */
+       gigaset_isowbuf_init(ubc->isooutbuf, PPP_FLAG);
+
+       /* set up isochronous output URBs for flag idling */
+       for (k = 0; k < BAS_OUTURBS; ++k) {
+               urb = ubc->isoouturbs[k].urb;
+               if (!urb) {
+                       rc = -EFAULT;
+                       goto error;
+               }
+               usb_fill_int_urb(urb, udev,
+                                usb_sndisocpipe(udev, 4 + 2 * bcs->channel),
+                                ubc->isooutbuf->data,
+                                sizeof(ubc->isooutbuf->data),
+                                write_iso_callback, &ubc->isoouturbs[k],
+                                BAS_FRAMETIME);
+
+               urb->transfer_flags = URB_ISO_ASAP;
+               urb->number_of_packets = BAS_NUMFRAMES;
+               for (j = 0; j < BAS_NUMFRAMES; ++j) {
+                       urb->iso_frame_desc[j].offset = BAS_OUTBUFSIZE;
+                       urb->iso_frame_desc[j].length = BAS_NORMFRAME;
+                       urb->iso_frame_desc[j].status = 0;
+                       urb->iso_frame_desc[j].actual_length = 0;
+               }
+               ubc->isoouturbs[k].limit = -1;
+       }
+
+       /* keep one URB free, submit the others */
+       for (k = 0; k < BAS_OUTURBS - 1; ++k) {
+               dump_urb(DEBUG_ISO, "Initial isoc write", urb);
+               rc = usb_submit_urb(ubc->isoouturbs[k].urb, GFP_ATOMIC);
+               if (rc != 0)
+                       goto error;
+       }
+       dump_urb(DEBUG_ISO, "Initial isoc write (free)", urb);
+       ubc->isooutfree = &ubc->isoouturbs[BAS_OUTURBS - 1];
+       ubc->isooutdone = ubc->isooutovfl = NULL;
+       return 0;
+error:
+       stopurbs(ubc);
+       return rc;
+}
+
+/* stopurbs
+ * cancel the USB request blocks for isochronous input and output
+ * errors are silently ignored
+ * argument:
+ *     B channel control structure
+ */
+static void stopurbs(struct bas_bc_state *ubc)
+{
+       int k, rc;
+
+       ubc->running = 0;
+
+       for (k = 0; k < BAS_INURBS; ++k) {
+               rc = usb_unlink_urb(ubc->isoinurbs[k]);
+               gig_dbg(DEBUG_ISO,
+                       "%s: isoc input URB %d unlinked, result = %s",
+                       __func__, k, get_usb_rcmsg(rc));
+       }
+
+       for (k = 0; k < BAS_OUTURBS; ++k) {
+               rc = usb_unlink_urb(ubc->isoouturbs[k].urb);
+               gig_dbg(DEBUG_ISO,
+                       "%s: isoc output URB %d unlinked, result = %s",
+                       __func__, k, get_usb_rcmsg(rc));
+       }
+}
+
+/* Isochronous Write - Bottom Half */
+/* =============================== */
+
+/* submit_iso_write_urb
+ * fill and submit the next isochronous write URB
+ * parameters:
+ *     ucx     context structure containing URB
+ * return value:
+ *     number of frames submitted in URB
+ *     0 if URB not submitted because no data available (isooutbuf busy)
+ *     error code < 0 on error
+ */
+static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
+{
+       struct urb *urb = ucx->urb;
+       struct bas_bc_state *ubc = ucx->bcs->hw.bas;
+       struct usb_iso_packet_descriptor *ifd;
+       int corrbytes, nframe, rc;
+
+       /* urb->dev is clobbered by USB subsystem */
+       urb->dev = ucx->bcs->cs->hw.bas->udev;
+       urb->transfer_flags = URB_ISO_ASAP;
+       urb->transfer_buffer = ubc->isooutbuf->data;
+       urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data);
+
+       for (nframe = 0; nframe < BAS_NUMFRAMES; nframe++) {
+               ifd = &urb->iso_frame_desc[nframe];
+
+               /* compute frame length according to flow control */
+               ifd->length = BAS_NORMFRAME;
+               corrbytes = atomic_read(&ubc->corrbytes);
+               if (corrbytes != 0) {
+                       gig_dbg(DEBUG_ISO, "%s: corrbytes=%d",
+                               __func__, corrbytes);
+                       if (corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME)
+                               corrbytes = BAS_HIGHFRAME - BAS_NORMFRAME;
+                       else if (corrbytes < BAS_LOWFRAME - BAS_NORMFRAME)
+                               corrbytes = BAS_LOWFRAME - BAS_NORMFRAME;
+                       ifd->length += corrbytes;
+                       atomic_add(-corrbytes, &ubc->corrbytes);
+               }
+
+               /* retrieve block of data to send */
+               rc = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length);
+               if (rc < 0) {
+                       if (rc == -EBUSY) {
+                               gig_dbg(DEBUG_ISO,
+                                       "%s: buffer busy at frame %d",
+                                       __func__, nframe);
+                               /* tasklet will be restarted from
+                                  gigaset_isoc_send_skb() */
+                       } else {
+                               dev_err(ucx->bcs->cs->dev,
+                                       "%s: buffer error %d at frame %d\n",
+                                       __func__, rc, nframe);
+                               return rc;
+                       }
+                       break;
+               }
+               ifd->offset = rc;
+               ucx->limit = ubc->isooutbuf->nextread;
+               ifd->status = 0;
+               ifd->actual_length = 0;
+       }
+       if (unlikely(nframe == 0))
+               return 0;       /* no data to send */
+       urb->number_of_packets = nframe;
+
+       rc = usb_submit_urb(urb, GFP_ATOMIC);
+       if (unlikely(rc)) {
+               if (rc == -ENODEV)
+                       /* device removed - give up silently */
+                       gig_dbg(DEBUG_ISO, "%s: disconnected", __func__);
+               else
+                       dev_err(ucx->bcs->cs->dev,
+                               "could not submit isoc write URB: %s\n",
+                               get_usb_rcmsg(rc));
+               return rc;
+       }
+       ++ubc->numsub;
+       return nframe;
+}
+
+/* write_iso_tasklet
+ * tasklet scheduled when an isochronous output URB from the Gigaset device
+ * has completed
+ * parameter:
+ *     data    B channel state structure
+ */
+static void write_iso_tasklet(unsigned long data)
+{
+       struct bc_state *bcs = (struct bc_state *) data;
+       struct bas_bc_state *ubc = bcs->hw.bas;
+       struct cardstate *cs = bcs->cs;
+       struct isow_urbctx_t *done, *next, *ovfl;
+       struct urb *urb;
+       int status;
+       struct usb_iso_packet_descriptor *ifd;
+       unsigned long flags;
+       int i;
+       struct sk_buff *skb;
+       int len;
+       int rc;
+
+       /* loop while completed URBs arrive in time */
+       for (;;) {
+               if (unlikely(!(ubc->running))) {
+                       gig_dbg(DEBUG_ISO, "%s: not running", __func__);
+                       return;
+               }
+
+               /* retrieve completed URBs */
+               spin_lock_irqsave(&ubc->isooutlock, flags);
+               done = ubc->isooutdone;
+               ubc->isooutdone = NULL;
+               ovfl = ubc->isooutovfl;
+               ubc->isooutovfl = NULL;
+               spin_unlock_irqrestore(&ubc->isooutlock, flags);
+               if (ovfl) {
+                       dev_err(cs->dev, "isoc write underrun\n");
+                       error_hangup(bcs);
+                       break;
+               }
+               if (!done)
+                       break;
+
+               /* submit free URB if available */
+               spin_lock_irqsave(&ubc->isooutlock, flags);
+               next = ubc->isooutfree;
+               ubc->isooutfree = NULL;
+               spin_unlock_irqrestore(&ubc->isooutlock, flags);
+               if (next) {
+                       rc = submit_iso_write_urb(next);
+                       if (unlikely(rc <= 0 && rc != -ENODEV)) {
+                               /* could not submit URB, put it back */
+                               spin_lock_irqsave(&ubc->isooutlock, flags);
+                               if (ubc->isooutfree == NULL) {
+                                       ubc->isooutfree = next;
+                                       next = NULL;
+                               }
+                               spin_unlock_irqrestore(&ubc->isooutlock, flags);
+                               if (next) {
+                                       /* couldn't put it back */
+                                       dev_err(cs->dev,
+                                               "losing isoc write URB\n");
+                                       error_hangup(bcs);
+                               }
+                       }
+               }
+
+               /* process completed URB */
+               urb = done->urb;
+               status = done->status;
+               switch (status) {
+               case -EXDEV:                    /* partial completion */
+                       gig_dbg(DEBUG_ISO, "%s: URB partially completed",
+                               __func__);
+                       /* fall through - what's the difference anyway? */
+               case 0:                         /* normal completion */
+                       /* inspect individual frames
+                        * assumptions (for lack of documentation):
+                        * - actual_length bytes of first frame in error are
+                        *   successfully sent
+                        * - all following frames are not sent at all
+                        */
+                       for (i = 0; i < BAS_NUMFRAMES; i++) {
+                               ifd = &urb->iso_frame_desc[i];
+                               if (ifd->status ||
+                                   ifd->actual_length != ifd->length) {
+                                       dev_warn(cs->dev,
+                                                "isoc write: frame %d[%d/%d]: %s\n",
+                                                i, ifd->actual_length,
+                                                ifd->length,
+                                                get_usb_statmsg(ifd->status));
+                                       break;
+                               }
+                       }
+                       break;
+               case -EPIPE:                    /* stall - probably underrun */
+                       dev_err(cs->dev, "isoc write: stalled\n");
+                       error_hangup(bcs);
+                       break;
+               default:                        /* other errors */
+                       dev_warn(cs->dev, "isoc write: %s\n",
+                                get_usb_statmsg(status));
+               }
+
+               /* mark the write buffer area covered by this URB as free */
+               if (done->limit >= 0)
+                       ubc->isooutbuf->read = done->limit;
+
+               /* mark URB as free */
+               spin_lock_irqsave(&ubc->isooutlock, flags);
+               next = ubc->isooutfree;
+               ubc->isooutfree = done;
+               spin_unlock_irqrestore(&ubc->isooutlock, flags);
+               if (next) {
+                       /* only one URB still active - resubmit one */
+                       rc = submit_iso_write_urb(next);
+                       if (unlikely(rc <= 0 && rc != -ENODEV)) {
+                               /* couldn't submit */
+                               error_hangup(bcs);
+                       }
+               }
+       }
+
+       /* process queued SKBs */
+       while ((skb = skb_dequeue(&bcs->squeue))) {
+               /* copy to output buffer, doing L2 encapsulation */
+               len = skb->len;
+               if (gigaset_isoc_buildframe(bcs, skb->data, len) == -EAGAIN) {
+                       /* insufficient buffer space, push back onto queue */
+                       skb_queue_head(&bcs->squeue, skb);
+                       gig_dbg(DEBUG_ISO, "%s: skb requeued, qlen=%d",
+                               __func__, skb_queue_len(&bcs->squeue));
+                       break;
+               }
+               skb_pull(skb, len);
+               gigaset_skb_sent(bcs, skb);
+               dev_kfree_skb_any(skb);
+       }
+}
+
+/* Isochronous Read - Bottom Half */
+/* ============================== */
+
+/* read_iso_tasklet
+ * tasklet scheduled when an isochronous input URB from the Gigaset device
+ * has completed
+ * parameter:
+ *     data    B channel state structure
+ */
+static void read_iso_tasklet(unsigned long data)
+{
+       struct bc_state *bcs = (struct bc_state *) data;
+       struct bas_bc_state *ubc = bcs->hw.bas;
+       struct cardstate *cs = bcs->cs;
+       struct urb *urb;
+       int status;
+       struct usb_iso_packet_descriptor *ifd;
+       char *rcvbuf;
+       unsigned long flags;
+       int totleft, numbytes, offset, frame, rc;
+
+       /* loop while more completed URBs arrive in the meantime */
+       for (;;) {
+               /* retrieve URB */
+               spin_lock_irqsave(&ubc->isoinlock, flags);
+               urb = ubc->isoindone;
+               if (!urb) {
+                       spin_unlock_irqrestore(&ubc->isoinlock, flags);
+                       return;
+               }
+               status = ubc->isoinstatus;
+               ubc->isoindone = NULL;
+               if (unlikely(ubc->loststatus != -EINPROGRESS)) {
+                       dev_warn(cs->dev,
+                                "isoc read overrun, URB dropped (status: %s, %d bytes)\n",
+                                get_usb_statmsg(ubc->loststatus),
+                                ubc->isoinlost);
+                       ubc->loststatus = -EINPROGRESS;
+               }
+               spin_unlock_irqrestore(&ubc->isoinlock, flags);
+
+               if (unlikely(!(ubc->running))) {
+                       gig_dbg(DEBUG_ISO,
+                               "%s: channel not running, "
+                               "dropped URB with status: %s",
+                               __func__, get_usb_statmsg(status));
+                       return;
+               }
+
+               switch (status) {
+               case 0:                         /* normal completion */
+                       break;
+               case -EXDEV:                    /* inspect individual frames
+                                                  (we do that anyway) */
+                       gig_dbg(DEBUG_ISO, "%s: URB partially completed",
+                               __func__);
+                       break;
+               case -ENOENT:
+               case -ECONNRESET:
+               case -EINPROGRESS:
+                       gig_dbg(DEBUG_ISO, "%s: %s",
+                               __func__, get_usb_statmsg(status));
+                       continue;               /* -> skip */
+               case -EPIPE:
+                       dev_err(cs->dev, "isoc read: stalled\n");
+                       error_hangup(bcs);
+                       continue;               /* -> skip */
+               default:                        /* other error */
+                       dev_warn(cs->dev, "isoc read: %s\n",
+                                get_usb_statmsg(status));
+                       goto error;
+               }
+
+               rcvbuf = urb->transfer_buffer;
+               totleft = urb->actual_length;
+               for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) {
+                       ifd = &urb->iso_frame_desc[frame];
+                       numbytes = ifd->actual_length;
+                       switch (ifd->status) {
+                       case 0:                 /* success */
+                               break;
+                       case -EPROTO:           /* protocol error or unplug */
+                       case -EILSEQ:
+                       case -ETIME:
+                               /* probably just disconnected, ignore */
+                               gig_dbg(DEBUG_ISO,
+                                       "isoc read: frame %d[%d]: %s\n",
+                                       frame, numbytes,
+                                       get_usb_statmsg(ifd->status));
+                               break;
+                       default:                /* other error */
+                               /* report, assume transferred bytes are ok */
+                               dev_warn(cs->dev,
+                                        "isoc read: frame %d[%d]: %s\n",
+                                        frame, numbytes,
+                                        get_usb_statmsg(ifd->status));
+                       }
+                       if (unlikely(numbytes > BAS_MAXFRAME))
+                               dev_warn(cs->dev,
+                                        "isoc read: frame %d[%d]: %s\n",
+                                        frame, numbytes,
+                                        "exceeds max frame size");
+                       if (unlikely(numbytes > totleft)) {
+                               dev_warn(cs->dev,
+                                        "isoc read: frame %d[%d]: %s\n",
+                                        frame, numbytes,
+                                        "exceeds total transfer length");
+                               numbytes = totleft;
+                       }
+                       offset = ifd->offset;
+                       if (unlikely(offset + numbytes > BAS_INBUFSIZE)) {
+                               dev_warn(cs->dev,
+                                        "isoc read: frame %d[%d]: %s\n",
+                                        frame, numbytes,
+                                        "exceeds end of buffer");
+                               numbytes = BAS_INBUFSIZE - offset;
+                       }
+                       gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs);
+                       totleft -= numbytes;
+               }
+               if (unlikely(totleft > 0))
+                       dev_warn(cs->dev, "isoc read: %d data bytes missing\n",
+                                totleft);
+
+error:
+               /* URB processed, resubmit */
+               for (frame = 0; frame < BAS_NUMFRAMES; frame++) {
+                       urb->iso_frame_desc[frame].status = 0;
+                       urb->iso_frame_desc[frame].actual_length = 0;
+               }
+               /* urb->dev is clobbered by USB subsystem */
+               urb->dev = bcs->cs->hw.bas->udev;
+               urb->transfer_flags = URB_ISO_ASAP;
+               urb->number_of_packets = BAS_NUMFRAMES;
+               rc = usb_submit_urb(urb, GFP_ATOMIC);
+               if (unlikely(rc != 0 && rc != -ENODEV)) {
+                       dev_err(cs->dev,
+                               "could not resubmit isoc read URB: %s\n",
+                               get_usb_rcmsg(rc));
+                       dump_urb(DEBUG_ISO, "resubmit isoc read", urb);
+                       error_hangup(bcs);
+               }
+       }
+}
+
+/* Channel Operations */
+/* ================== */
+
+/* req_timeout
+ * timeout routine for control output request
+ * argument:
+ *     controller state structure
+ */
+static void req_timeout(struct timer_list *t)
+{
+       struct bas_cardstate *ucs = from_timer(ucs, t, timer_ctrl);
+       struct cardstate *cs = ucs->cs;
+       int pending;
+       unsigned long flags;
+
+       check_pending(ucs);
+
+       spin_lock_irqsave(&ucs->lock, flags);
+       pending = ucs->pending;
+       ucs->pending = 0;
+       spin_unlock_irqrestore(&ucs->lock, flags);
+
+       switch (pending) {
+       case 0:                                 /* no pending request */
+               gig_dbg(DEBUG_USBREQ, "%s: no request pending", __func__);
+               break;
+
+       case HD_OPEN_ATCHANNEL:
+               dev_err(cs->dev, "timeout opening AT channel\n");
+               error_reset(cs);
+               break;
+
+       case HD_OPEN_B1CHANNEL:
+               dev_err(cs->dev, "timeout opening channel 1\n");
+               error_hangup(&cs->bcs[0]);
+               break;
+
+       case HD_OPEN_B2CHANNEL:
+               dev_err(cs->dev, "timeout opening channel 2\n");
+               error_hangup(&cs->bcs[1]);
+               break;
+
+       case HD_CLOSE_ATCHANNEL:
+               dev_err(cs->dev, "timeout closing AT channel\n");
+               error_reset(cs);
+               break;
+
+       case HD_CLOSE_B1CHANNEL:
+               dev_err(cs->dev, "timeout closing channel 1\n");
+               error_reset(cs);
+               break;
+
+       case HD_CLOSE_B2CHANNEL:
+               dev_err(cs->dev, "timeout closing channel 2\n");
+               error_reset(cs);
+               break;
+
+       case HD_RESET_INTERRUPT_PIPE:
+               /* error recovery escalation */
+               dev_err(cs->dev,
+                       "reset interrupt pipe timeout, attempting USB reset\n");
+               usb_queue_reset_device(ucs->interface);
+               break;
+
+       default:
+               dev_warn(cs->dev, "request 0x%02x timed out, clearing\n",
+                        pending);
+       }
+
+       wake_up(&ucs->waitqueue);
+}
+
+/* write_ctrl_callback
+ * USB completion handler for control pipe output
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ *     urb     USB request block of completed request
+ *             urb->context = hardware specific controller state structure
+ */
+static void write_ctrl_callback(struct urb *urb)
+{
+       struct bas_cardstate *ucs = urb->context;
+       int status = urb->status;
+       int rc;
+       unsigned long flags;
+
+       /* check status */
+       switch (status) {
+       case 0:                                 /* normal completion */
+               spin_lock_irqsave(&ucs->lock, flags);
+               switch (ucs->pending) {
+               case HD_DEVICE_INIT_ACK:        /* no reply expected */
+                       del_timer(&ucs->timer_ctrl);
+                       ucs->pending = 0;
+                       break;
+               }
+               spin_unlock_irqrestore(&ucs->lock, flags);
+               return;
+
+       case -ENOENT:                   /* cancelled */
+       case -ECONNRESET:               /* cancelled (async) */
+       case -EINPROGRESS:              /* pending */
+       case -ENODEV:                   /* device removed */
+       case -ESHUTDOWN:                /* device shut down */
+               /* ignore silently */
+               gig_dbg(DEBUG_USBREQ, "%s: %s",
+                       __func__, get_usb_statmsg(status));
+               break;
+
+       default:                                /* any failure */
+               /* don't retry if suspend requested */
+               if (++ucs->retry_ctrl > BAS_RETRY ||
+                   (ucs->basstate & BS_SUSPEND)) {
+                       dev_err(&ucs->interface->dev,
+                               "control request 0x%02x failed: %s\n",
+                               ucs->dr_ctrl.bRequest,
+                               get_usb_statmsg(status));
+                       break;          /* give up */
+               }
+               dev_notice(&ucs->interface->dev,
+                          "control request 0x%02x: %s, retry %d\n",
+                          ucs->dr_ctrl.bRequest, get_usb_statmsg(status),
+                          ucs->retry_ctrl);
+               /* urb->dev is clobbered by USB subsystem */
+               urb->dev = ucs->udev;
+               rc = usb_submit_urb(urb, GFP_ATOMIC);
+               if (unlikely(rc)) {
+                       dev_err(&ucs->interface->dev,
+                               "could not resubmit request 0x%02x: %s\n",
+                               ucs->dr_ctrl.bRequest, get_usb_rcmsg(rc));
+                       break;
+               }
+               /* resubmitted */
+               return;
+       }
+
+       /* failed, clear pending request */
+       spin_lock_irqsave(&ucs->lock, flags);
+       del_timer(&ucs->timer_ctrl);
+       ucs->pending = 0;
+       spin_unlock_irqrestore(&ucs->lock, flags);
+       wake_up(&ucs->waitqueue);
+}
+
+/* req_submit
+ * submit a control output request without message buffer to the Gigaset base
+ * and optionally start a timeout
+ * parameters:
+ *     bcs     B channel control structure
+ *     req     control request code (HD_*)
+ *     val     control request parameter value (set to 0 if unused)
+ *     timeout timeout in seconds (0: no timeout)
+ * return value:
+ *     0 on success
+ *     -EBUSY if another request is pending
+ *     any URB submission error code
+ */
+static int req_submit(struct bc_state *bcs, int req, int val, int timeout)
+{
+       struct bas_cardstate *ucs = bcs->cs->hw.bas;
+       int ret;
+       unsigned long flags;
+
+       gig_dbg(DEBUG_USBREQ, "-------> 0x%02x (%d)", req, val);
+
+       spin_lock_irqsave(&ucs->lock, flags);
+       if (ucs->pending) {
+               spin_unlock_irqrestore(&ucs->lock, flags);
+               dev_err(bcs->cs->dev,
+                       "submission of request 0x%02x failed: "
+                       "request 0x%02x still pending\n",
+                       req, ucs->pending);
+               return -EBUSY;
+       }
+
+       ucs->dr_ctrl.bRequestType = OUT_VENDOR_REQ;
+       ucs->dr_ctrl.bRequest = req;
+       ucs->dr_ctrl.wValue = cpu_to_le16(val);
+       ucs->dr_ctrl.wIndex = 0;
+       ucs->dr_ctrl.wLength = 0;
+       usb_fill_control_urb(ucs->urb_ctrl, ucs->udev,
+                            usb_sndctrlpipe(ucs->udev, 0),
+                            (unsigned char *) &ucs->dr_ctrl, NULL, 0,
+                            write_ctrl_callback, ucs);
+       ucs->retry_ctrl = 0;
+       ret = usb_submit_urb(ucs->urb_ctrl, GFP_ATOMIC);
+       if (unlikely(ret)) {
+               dev_err(bcs->cs->dev, "could not submit request 0x%02x: %s\n",
+                       req, get_usb_rcmsg(ret));
+               spin_unlock_irqrestore(&ucs->lock, flags);
+               return ret;
+       }
+       ucs->pending = req;
+
+       if (timeout > 0) {
+               gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout);
+               mod_timer(&ucs->timer_ctrl, jiffies + timeout * HZ / 10);
+       }
+
+       spin_unlock_irqrestore(&ucs->lock, flags);
+       return 0;
+}
+
+/* gigaset_init_bchannel
+ * called by common.c to connect a B channel
+ * initialize isochronous I/O and tell the Gigaset base to open the channel
+ * argument:
+ *     B channel control structure
+ * return value:
+ *     0 on success, error code < 0 on error
+ */
+static int gigaset_init_bchannel(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+       int req, ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (unlikely(!cs->connected)) {
+               gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+               spin_unlock_irqrestore(&cs->lock, flags);
+               return -ENODEV;
+       }
+
+       if (cs->hw.bas->basstate & BS_SUSPEND) {
+               dev_notice(cs->dev,
+                          "not starting isoc I/O, suspend in progress\n");
+               spin_unlock_irqrestore(&cs->lock, flags);
+               return -EHOSTUNREACH;
+       }
+
+       ret = starturbs(bcs);
+       if (ret < 0) {
+               spin_unlock_irqrestore(&cs->lock, flags);
+               dev_err(cs->dev,
+                       "could not start isoc I/O for channel B%d: %s\n",
+                       bcs->channel + 1,
+                       ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret));
+               if (ret != -ENODEV)
+                       error_hangup(bcs);
+               return ret;
+       }
+
+       req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL;
+       ret = req_submit(bcs, req, 0, BAS_TIMEOUT);
+       if (ret < 0) {
+               dev_err(cs->dev, "could not open channel B%d\n",
+                       bcs->channel + 1);
+               stopurbs(bcs->hw.bas);
+       }
+
+       spin_unlock_irqrestore(&cs->lock, flags);
+       if (ret < 0 && ret != -ENODEV)
+               error_hangup(bcs);
+       return ret;
+}
+
+/* gigaset_close_bchannel
+ * called by common.c to disconnect a B channel
+ * tell the Gigaset base to close the channel
+ * stopping isochronous I/O and LL notification will be done when the
+ * acknowledgement for the close arrives
+ * argument:
+ *     B channel control structure
+ * return value:
+ *     0 on success, error code < 0 on error
+ */
+static int gigaset_close_bchannel(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+       int req, ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (unlikely(!cs->connected)) {
+               spin_unlock_irqrestore(&cs->lock, flags);
+               gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+               return -ENODEV;
+       }
+
+       if (!(cs->hw.bas->basstate & (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) {
+               /* channel not running: just signal common.c */
+               spin_unlock_irqrestore(&cs->lock, flags);
+               gigaset_bchannel_down(bcs);
+               return 0;
+       }
+
+       /* channel running: tell device to close it */
+       req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL;
+       ret = req_submit(bcs, req, 0, BAS_TIMEOUT);
+       if (ret < 0)
+               dev_err(cs->dev, "closing channel B%d failed\n",
+                       bcs->channel + 1);
+
+       spin_unlock_irqrestore(&cs->lock, flags);
+       return ret;
+}
+
+/* Device Operations */
+/* ================= */
+
+/* complete_cb
+ * unqueue first command buffer from queue, waking any sleepers
+ * must be called with cs->cmdlock held
+ * parameter:
+ *     cs      controller state structure
+ */
+static void complete_cb(struct cardstate *cs)
+{
+       struct cmdbuf_t *cb = cs->cmdbuf;
+
+       /* unqueue completed buffer */
+       cs->cmdbytes -= cs->curlen;
+       gig_dbg(DEBUG_OUTPUT, "write_command: sent %u bytes, %u left",
+               cs->curlen, cs->cmdbytes);
+       if (cb->next != NULL) {
+               cs->cmdbuf = cb->next;
+               cs->cmdbuf->prev = NULL;
+               cs->curlen = cs->cmdbuf->len;
+       } else {
+               cs->cmdbuf = NULL;
+               cs->lastcmdbuf = NULL;
+               cs->curlen = 0;
+       }
+
+       if (cb->wake_tasklet)
+               tasklet_schedule(cb->wake_tasklet);
+
+       kfree(cb);
+}
+
+/* write_command_callback
+ * USB completion handler for AT command transmission
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ *     urb     USB request block of completed request
+ *             urb->context = controller state structure
+ */
+static void write_command_callback(struct urb *urb)
+{
+       struct cardstate *cs = urb->context;
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int status = urb->status;
+       unsigned long flags;
+
+       update_basstate(ucs, 0, BS_ATWRPEND);
+       wake_up(&ucs->waitqueue);
+
+       /* check status */
+       switch (status) {
+       case 0:                                 /* normal completion */
+               break;
+       case -ENOENT:                   /* cancelled */
+       case -ECONNRESET:               /* cancelled (async) */
+       case -EINPROGRESS:              /* pending */
+       case -ENODEV:                   /* device removed */
+       case -ESHUTDOWN:                /* device shut down */
+               /* ignore silently */
+               gig_dbg(DEBUG_USBREQ, "%s: %s",
+                       __func__, get_usb_statmsg(status));
+               return;
+       default:                                /* any failure */
+               if (++ucs->retry_cmd_out > BAS_RETRY) {
+                       dev_warn(cs->dev,
+                                "command write: %s, "
+                                "giving up after %d retries\n",
+                                get_usb_statmsg(status),
+                                ucs->retry_cmd_out);
+                       break;
+               }
+               if (ucs->basstate & BS_SUSPEND) {
+                       dev_warn(cs->dev,
+                                "command write: %s, "
+                                "won't retry - suspend requested\n",
+                                get_usb_statmsg(status));
+                       break;
+               }
+               if (cs->cmdbuf == NULL) {
+                       dev_warn(cs->dev,
+                                "command write: %s, "
+                                "cannot retry - cmdbuf gone\n",
+                                get_usb_statmsg(status));
+                       break;
+               }
+               dev_notice(cs->dev, "command write: %s, retry %d\n",
+                          get_usb_statmsg(status), ucs->retry_cmd_out);
+               if (atwrite_submit(cs, cs->cmdbuf->buf, cs->cmdbuf->len) >= 0)
+                       /* resubmitted - bypass regular exit block */
+                       return;
+               /* command send failed, assume base still waiting */
+               update_basstate(ucs, BS_ATREADY, 0);
+       }
+
+       spin_lock_irqsave(&cs->cmdlock, flags);
+       if (cs->cmdbuf != NULL)
+               complete_cb(cs);
+       spin_unlock_irqrestore(&cs->cmdlock, flags);
+}
+
+/* atrdy_timeout
+ * timeout routine for AT command transmission
+ * argument:
+ *     controller state structure
+ */
+static void atrdy_timeout(struct timer_list *t)
+{
+       struct bas_cardstate *ucs = from_timer(ucs, t, timer_atrdy);
+       struct cardstate *cs = ucs->cs;
+
+       dev_warn(cs->dev, "timeout waiting for HD_READY_SEND_ATDATA\n");
+
+       /* fake the missing signal - what else can I do? */
+       update_basstate(ucs, BS_ATREADY, BS_ATTIMER);
+       start_cbsend(cs);
+}
+
+/* atwrite_submit
+ * submit an HD_WRITE_ATMESSAGE command URB
+ * parameters:
+ *     cs      controller state structure
+ *     buf     buffer containing command to send
+ *     len     length of command to send
+ * return value:
+ *     0 on success
+ *     -EBUSY if another request is pending
+ *     any URB submission error code
+ */
+static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len)
+{
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int rc;
+
+       gig_dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len);
+
+       if (update_basstate(ucs, BS_ATWRPEND, 0) & BS_ATWRPEND) {
+               dev_err(cs->dev,
+                       "could not submit HD_WRITE_ATMESSAGE: URB busy\n");
+               return -EBUSY;
+       }
+
+       ucs->dr_cmd_out.bRequestType = OUT_VENDOR_REQ;
+       ucs->dr_cmd_out.bRequest = HD_WRITE_ATMESSAGE;
+       ucs->dr_cmd_out.wValue = 0;
+       ucs->dr_cmd_out.wIndex = 0;
+       ucs->dr_cmd_out.wLength = cpu_to_le16(len);
+       usb_fill_control_urb(ucs->urb_cmd_out, ucs->udev,
+                            usb_sndctrlpipe(ucs->udev, 0),
+                            (unsigned char *) &ucs->dr_cmd_out, buf, len,
+                            write_command_callback, cs);
+       rc = usb_submit_urb(ucs->urb_cmd_out, GFP_ATOMIC);
+       if (unlikely(rc)) {
+               update_basstate(ucs, 0, BS_ATWRPEND);
+               dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: %s\n",
+                       get_usb_rcmsg(rc));
+               return rc;
+       }
+
+       /* submitted successfully, start timeout if necessary */
+       if (!(update_basstate(ucs, BS_ATTIMER, BS_ATREADY) & BS_ATTIMER)) {
+               gig_dbg(DEBUG_OUTPUT, "setting ATREADY timeout of %d/10 secs",
+                       ATRDY_TIMEOUT);
+               mod_timer(&ucs->timer_atrdy, jiffies + ATRDY_TIMEOUT * HZ / 10);
+       }
+       return 0;
+}
+
+/* start_cbsend
+ * start transmission of AT command queue if necessary
+ * parameter:
+ *     cs              controller state structure
+ * return value:
+ *     0 on success
+ *     error code < 0 on error
+ */
+static int start_cbsend(struct cardstate *cs)
+{
+       struct cmdbuf_t *cb;
+       struct bas_cardstate *ucs = cs->hw.bas;
+       unsigned long flags;
+       int rc;
+       int retval = 0;
+
+       /* check if suspend requested */
+       if (ucs->basstate & BS_SUSPEND) {
+               gig_dbg(DEBUG_OUTPUT, "suspending");
+               return -EHOSTUNREACH;
+       }
+
+       /* check if AT channel is open */
+       if (!(ucs->basstate & BS_ATOPEN)) {
+               gig_dbg(DEBUG_OUTPUT, "AT channel not open");
+               rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT);
+               if (rc < 0) {
+                       /* flush command queue */
+                       spin_lock_irqsave(&cs->cmdlock, flags);
+                       while (cs->cmdbuf != NULL)
+                               complete_cb(cs);
+                       spin_unlock_irqrestore(&cs->cmdlock, flags);
+               }
+               return rc;
+       }
+
+       /* try to send first command in queue */
+       spin_lock_irqsave(&cs->cmdlock, flags);
+
+       while ((cb = cs->cmdbuf) != NULL && (ucs->basstate & BS_ATREADY)) {
+               ucs->retry_cmd_out = 0;
+               rc = atwrite_submit(cs, cb->buf, cb->len);
+               if (unlikely(rc)) {
+                       retval = rc;
+                       complete_cb(cs);
+               }
+       }
+
+       spin_unlock_irqrestore(&cs->cmdlock, flags);
+       return retval;
+}
+
+/* gigaset_write_cmd
+ * This function is called by the device independent part of the driver
+ * to transmit an AT command string to the Gigaset device.
+ * It encapsulates the device specific method for transmission over the
+ * direct USB connection to the base.
+ * The command string is added to the queue of commands to send, and
+ * USB transmission is started if necessary.
+ * parameters:
+ *     cs              controller state structure
+ *     cb              command buffer structure
+ * return value:
+ *     number of bytes queued on success
+ *     error code < 0 on error
+ */
+static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
+{
+       unsigned long flags;
+       int rc;
+
+       gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
+                          DEBUG_TRANSCMD : DEBUG_LOCKCMD,
+                          "CMD Transmit", cb->len, cb->buf);
+
+       /* translate "+++" escape sequence sent as a single separate command
+        * into "close AT channel" command for error recovery
+        * The next command will reopen the AT channel automatically.
+        */
+       if (cb->len == 3 && !memcmp(cb->buf, "+++", 3)) {
+               /* If an HD_RECEIVEATDATA_ACK message remains unhandled
+                * because of an error, the base never sends another one.
+                * The response channel is thus effectively blocked.
+                * Closing and reopening the AT channel does *not* clear
+                * this condition.
+                * As a stopgap measure, submit a zero-length AT read
+                * before closing the AT channel. This has the undocumented
+                * effect of triggering a new HD_RECEIVEATDATA_ACK message
+                * from the base if necessary.
+                * The subsequent AT channel close then discards any pending
+                * messages.
+                */
+               spin_lock_irqsave(&cs->lock, flags);
+               if (!(cs->hw.bas->basstate & BS_ATRDPEND)) {
+                       kfree(cs->hw.bas->rcvbuf);
+                       cs->hw.bas->rcvbuf = NULL;
+                       cs->hw.bas->rcvbuf_size = 0;
+                       cs->hw.bas->retry_cmd_in = 0;
+                       atread_submit(cs, 0);
+               }
+               spin_unlock_irqrestore(&cs->lock, flags);
+
+               rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
+               if (cb->wake_tasklet)
+                       tasklet_schedule(cb->wake_tasklet);
+               if (!rc)
+                       rc = cb->len;
+               kfree(cb);
+               return rc;
+       }
+
+       spin_lock_irqsave(&cs->cmdlock, flags);
+       cb->prev = cs->lastcmdbuf;
+       if (cs->lastcmdbuf)
+               cs->lastcmdbuf->next = cb;
+       else {
+               cs->cmdbuf = cb;
+               cs->curlen = cb->len;
+       }
+       cs->cmdbytes += cb->len;
+       cs->lastcmdbuf = cb;
+       spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (unlikely(!cs->connected)) {
+               spin_unlock_irqrestore(&cs->lock, flags);
+               gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+               /* flush command queue */
+               spin_lock_irqsave(&cs->cmdlock, flags);
+               while (cs->cmdbuf != NULL)
+                       complete_cb(cs);
+               spin_unlock_irqrestore(&cs->cmdlock, flags);
+               return -ENODEV;
+       }
+       rc = start_cbsend(cs);
+       spin_unlock_irqrestore(&cs->lock, flags);
+       return rc < 0 ? rc : cb->len;
+}
+
+/* gigaset_write_room
+ * tty_driver.write_room interface routine
+ * return number of characters the driver will accept to be written via
+ * gigaset_write_cmd
+ * parameter:
+ *     controller state structure
+ * return value:
+ *     number of characters
+ */
+static int gigaset_write_room(struct cardstate *cs)
+{
+       return IF_WRITEBUF;
+}
+
+/* gigaset_chars_in_buffer
+ * tty_driver.chars_in_buffer interface routine
+ * return number of characters waiting to be sent
+ * parameter:
+ *     controller state structure
+ * return value:
+ *     number of characters
+ */
+static int gigaset_chars_in_buffer(struct cardstate *cs)
+{
+       return cs->cmdbytes;
+}
+
+/* gigaset_brkchars
+ * implementation of ioctl(GIGASET_BRKCHARS)
+ * parameter:
+ *     controller state structure
+ * return value:
+ *     -EINVAL (unimplemented function)
+ */
+static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
+{
+       return -EINVAL;
+}
+
+
+/* Device Initialization/Shutdown */
+/* ============================== */
+
+/* Free hardware dependent part of the B channel structure
+ * parameter:
+ *     bcs     B channel structure
+ */
+static void gigaset_freebcshw(struct bc_state *bcs)
+{
+       struct bas_bc_state *ubc = bcs->hw.bas;
+       int i;
+
+       if (!ubc)
+               return;
+
+       /* kill URBs and tasklets before freeing - better safe than sorry */
+       ubc->running = 0;
+       gig_dbg(DEBUG_INIT, "%s: killing isoc URBs", __func__);
+       for (i = 0; i < BAS_OUTURBS; ++i) {
+               usb_kill_urb(ubc->isoouturbs[i].urb);
+               usb_free_urb(ubc->isoouturbs[i].urb);
+       }
+       for (i = 0; i < BAS_INURBS; ++i) {
+               usb_kill_urb(ubc->isoinurbs[i]);
+               usb_free_urb(ubc->isoinurbs[i]);
+       }
+       tasklet_kill(&ubc->sent_tasklet);
+       tasklet_kill(&ubc->rcvd_tasklet);
+       kfree(ubc->isooutbuf);
+       kfree(ubc);
+       bcs->hw.bas = NULL;
+}
+
+/* Initialize hardware dependent part of the B channel structure
+ * parameter:
+ *     bcs     B channel structure
+ * return value:
+ *     0 on success, error code < 0 on failure
+ */
+static int gigaset_initbcshw(struct bc_state *bcs)
+{
+       int i;
+       struct bas_bc_state *ubc;
+
+       bcs->hw.bas = ubc = kmalloc(sizeof(struct bas_bc_state), GFP_KERNEL);
+       if (!ubc) {
+               pr_err("out of memory\n");
+               return -ENOMEM;
+       }
+
+       ubc->running = 0;
+       atomic_set(&ubc->corrbytes, 0);
+       spin_lock_init(&ubc->isooutlock);
+       for (i = 0; i < BAS_OUTURBS; ++i) {
+               ubc->isoouturbs[i].urb = NULL;
+               ubc->isoouturbs[i].bcs = bcs;
+       }
+       ubc->isooutdone = ubc->isooutfree = ubc->isooutovfl = NULL;
+       ubc->numsub = 0;
+       ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL);
+       if (!ubc->isooutbuf) {
+               pr_err("out of memory\n");
+               kfree(ubc);
+               bcs->hw.bas = NULL;
+               return -ENOMEM;
+       }
+       tasklet_init(&ubc->sent_tasklet,
+                    write_iso_tasklet, (unsigned long) bcs);
+
+       spin_lock_init(&ubc->isoinlock);
+       for (i = 0; i < BAS_INURBS; ++i)
+               ubc->isoinurbs[i] = NULL;
+       ubc->isoindone = NULL;
+       ubc->loststatus = -EINPROGRESS;
+       ubc->isoinlost = 0;
+       ubc->seqlen = 0;
+       ubc->inbyte = 0;
+       ubc->inbits = 0;
+       ubc->goodbytes = 0;
+       ubc->alignerrs = 0;
+       ubc->fcserrs = 0;
+       ubc->frameerrs = 0;
+       ubc->giants = 0;
+       ubc->runts = 0;
+       ubc->aborts = 0;
+       ubc->shared0s = 0;
+       ubc->stolen0s = 0;
+       tasklet_init(&ubc->rcvd_tasklet,
+                    read_iso_tasklet, (unsigned long) bcs);
+       return 0;
+}
+
+static void gigaset_reinitbcshw(struct bc_state *bcs)
+{
+       struct bas_bc_state *ubc = bcs->hw.bas;
+
+       bcs->hw.bas->running = 0;
+       atomic_set(&bcs->hw.bas->corrbytes, 0);
+       bcs->hw.bas->numsub = 0;
+       spin_lock_init(&ubc->isooutlock);
+       spin_lock_init(&ubc->isoinlock);
+       ubc->loststatus = -EINPROGRESS;
+}
+
+static void gigaset_freecshw(struct cardstate *cs)
+{
+       /* timers, URBs and rcvbuf are disposed of in disconnect */
+       kfree(cs->hw.bas->int_in_buf);
+       kfree(cs->hw.bas);
+       cs->hw.bas = NULL;
+}
+
+/* Initialize hardware dependent part of the cardstate structure
+ * parameter:
+ *     cs      cardstate structure
+ * return value:
+ *     0 on success, error code < 0 on failure
+ */
+static int gigaset_initcshw(struct cardstate *cs)
+{
+       struct bas_cardstate *ucs;
+
+       cs->hw.bas = ucs = kzalloc(sizeof(*ucs), GFP_KERNEL);
+       if (!ucs) {
+               pr_err("out of memory\n");
+               return -ENOMEM;
+       }
+       ucs->int_in_buf = kmalloc(IP_MSGSIZE, GFP_KERNEL);
+       if (!ucs->int_in_buf) {
+               kfree(ucs);
+               pr_err("out of memory\n");
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&ucs->lock);
+       ucs->cs = cs;
+       timer_setup(&ucs->timer_ctrl, req_timeout, 0);
+       timer_setup(&ucs->timer_atrdy, atrdy_timeout, 0);
+       timer_setup(&ucs->timer_cmd_in, cmd_in_timeout, 0);
+       timer_setup(&ucs->timer_int_in, int_in_resubmit, 0);
+       init_waitqueue_head(&ucs->waitqueue);
+       INIT_WORK(&ucs->int_in_wq, int_in_work);
+
+       return 0;
+}
+
+/* freeurbs
+ * unlink and deallocate all URBs unconditionally
+ * caller must make sure that no commands are still in progress
+ * parameter:
+ *     cs      controller state structure
+ */
+static void freeurbs(struct cardstate *cs)
+{
+       struct bas_cardstate *ucs = cs->hw.bas;
+       struct bas_bc_state *ubc;
+       int i, j;
+
+       gig_dbg(DEBUG_INIT, "%s: killing URBs", __func__);
+       for (j = 0; j < BAS_CHANNELS; ++j) {
+               ubc = cs->bcs[j].hw.bas;
+               for (i = 0; i < BAS_OUTURBS; ++i) {
+                       usb_kill_urb(ubc->isoouturbs[i].urb);
+                       usb_free_urb(ubc->isoouturbs[i].urb);
+                       ubc->isoouturbs[i].urb = NULL;
+               }
+               for (i = 0; i < BAS_INURBS; ++i) {
+                       usb_kill_urb(ubc->isoinurbs[i]);
+                       usb_free_urb(ubc->isoinurbs[i]);
+                       ubc->isoinurbs[i] = NULL;
+               }
+       }
+       usb_kill_urb(ucs->urb_int_in);
+       usb_free_urb(ucs->urb_int_in);
+       ucs->urb_int_in = NULL;
+       usb_kill_urb(ucs->urb_cmd_out);
+       usb_free_urb(ucs->urb_cmd_out);
+       ucs->urb_cmd_out = NULL;
+       usb_kill_urb(ucs->urb_cmd_in);
+       usb_free_urb(ucs->urb_cmd_in);
+       ucs->urb_cmd_in = NULL;
+       usb_kill_urb(ucs->urb_ctrl);
+       usb_free_urb(ucs->urb_ctrl);
+       ucs->urb_ctrl = NULL;
+}
+
+/* gigaset_probe
+ * This function is called when a new USB device is connected.
+ * It checks whether the new device is handled by this driver.
+ */
+static int gigaset_probe(struct usb_interface *interface,
+                        const struct usb_device_id *id)
+{
+       struct usb_host_interface *hostif;
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct cardstate *cs = NULL;
+       struct bas_cardstate *ucs = NULL;
+       struct bas_bc_state *ubc;
+       struct usb_endpoint_descriptor *endpoint;
+       int i, j;
+       int rc;
+
+       gig_dbg(DEBUG_INIT,
+               "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)",
+               __func__, le16_to_cpu(udev->descriptor.idVendor),
+               le16_to_cpu(udev->descriptor.idProduct));
+
+       /* set required alternate setting */
+       hostif = interface->cur_altsetting;
+       if (hostif->desc.bAlternateSetting != 3) {
+               gig_dbg(DEBUG_INIT,
+                       "%s: wrong alternate setting %d - trying to switch",
+                       __func__, hostif->desc.bAlternateSetting);
+               if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3)
+                   < 0) {
+                       dev_warn(&udev->dev, "usb_set_interface failed, "
+                                "device %d interface %d altsetting %d\n",
+                                udev->devnum, hostif->desc.bInterfaceNumber,
+                                hostif->desc.bAlternateSetting);
+                       return -ENODEV;
+               }
+               hostif = interface->cur_altsetting;
+       }
+
+       /* Reject application specific interfaces
+        */
+       if (hostif->desc.bInterfaceClass != 255) {
+               dev_warn(&udev->dev, "%s: bInterfaceClass == %d\n",
+                        __func__, hostif->desc.bInterfaceClass);
+               return -ENODEV;
+       }
+
+       if (hostif->desc.bNumEndpoints < 1)
+               return -ENODEV;
+
+       dev_info(&udev->dev,
+                "%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n",
+                __func__, le16_to_cpu(udev->descriptor.idVendor),
+                le16_to_cpu(udev->descriptor.idProduct));
+
+       /* allocate memory for our device state and initialize it */
+       cs = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
+                           GIGASET_MODULENAME);
+       if (!cs)
+               return -ENODEV;
+       ucs = cs->hw.bas;
+
+       /* save off device structure ptrs for later use */
+       usb_get_dev(udev);
+       ucs->udev = udev;
+       ucs->interface = interface;
+       cs->dev = &interface->dev;
+
+       /* allocate URBs:
+        * - one for the interrupt pipe
+        * - three for the different uses of the default control pipe
+        * - three for each isochronous pipe
+        */
+       if (!(ucs->urb_int_in = usb_alloc_urb(0, GFP_KERNEL)) ||
+           !(ucs->urb_cmd_in = usb_alloc_urb(0, GFP_KERNEL)) ||
+           !(ucs->urb_cmd_out = usb_alloc_urb(0, GFP_KERNEL)) ||
+           !(ucs->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL)))
+               goto allocerr;
+
+       for (j = 0; j < BAS_CHANNELS; ++j) {
+               ubc = cs->bcs[j].hw.bas;
+               for (i = 0; i < BAS_OUTURBS; ++i)
+                       if (!(ubc->isoouturbs[i].urb =
+                             usb_alloc_urb(BAS_NUMFRAMES, GFP_KERNEL)))
+                               goto allocerr;
+               for (i = 0; i < BAS_INURBS; ++i)
+                       if (!(ubc->isoinurbs[i] =
+                             usb_alloc_urb(BAS_NUMFRAMES, GFP_KERNEL)))
+                               goto allocerr;
+       }
+
+       ucs->rcvbuf = NULL;
+       ucs->rcvbuf_size = 0;
+
+       /* Fill the interrupt urb and send it to the core */
+       endpoint = &hostif->endpoint[0].desc;
+       usb_fill_int_urb(ucs->urb_int_in, udev,
+                        usb_rcvintpipe(udev,
+                                       usb_endpoint_num(endpoint)),
+                        ucs->int_in_buf, IP_MSGSIZE, read_int_callback, cs,
+                        endpoint->bInterval);
+       rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
+       if (rc != 0) {
+               dev_err(cs->dev, "could not submit interrupt URB: %s\n",
+                       get_usb_rcmsg(rc));
+               goto error;
+       }
+       ucs->retry_int_in = 0;
+
+       /* tell the device that the driver is ready */
+       rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0);
+       if (rc != 0)
+               goto error;
+
+       /* tell common part that the device is ready */
+       if (startmode == SM_LOCKED)
+               cs->mstate = MS_LOCKED;
+
+       /* save address of controller structure */
+       usb_set_intfdata(interface, cs);
+
+       rc = gigaset_start(cs);
+       if (rc < 0)
+               goto error;
+
+       return 0;
+
+allocerr:
+       dev_err(cs->dev, "could not allocate URBs\n");
+       rc = -ENOMEM;
+error:
+       freeurbs(cs);
+       usb_set_intfdata(interface, NULL);
+       usb_put_dev(udev);
+       gigaset_freecs(cs);
+       return rc;
+}
+
+/* gigaset_disconnect
+ * This function is called when the Gigaset base is unplugged.
+ */
+static void gigaset_disconnect(struct usb_interface *interface)
+{
+       struct cardstate *cs;
+       struct bas_cardstate *ucs;
+       int j;
+
+       cs = usb_get_intfdata(interface);
+
+       ucs = cs->hw.bas;
+
+       dev_info(cs->dev, "disconnecting Gigaset base\n");
+
+       /* mark base as not ready, all channels disconnected */
+       ucs->basstate = 0;
+
+       /* tell LL all channels are down */
+       for (j = 0; j < BAS_CHANNELS; ++j)
+               gigaset_bchannel_down(cs->bcs + j);
+
+       /* stop driver (common part) */
+       gigaset_stop(cs);
+
+       /* stop delayed work and URBs, free ressources */
+       del_timer_sync(&ucs->timer_ctrl);
+       del_timer_sync(&ucs->timer_atrdy);
+       del_timer_sync(&ucs->timer_cmd_in);
+       del_timer_sync(&ucs->timer_int_in);
+       cancel_work_sync(&ucs->int_in_wq);
+       freeurbs(cs);
+       usb_set_intfdata(interface, NULL);
+       kfree(ucs->rcvbuf);
+       ucs->rcvbuf = NULL;
+       ucs->rcvbuf_size = 0;
+       usb_put_dev(ucs->udev);
+       ucs->interface = NULL;
+       ucs->udev = NULL;
+       cs->dev = NULL;
+       gigaset_freecs(cs);
+}
+
+/* gigaset_suspend
+ * This function is called before the USB connection is suspended
+ * or before the USB device is reset.
+ * In the latter case, message == PMSG_ON.
+ */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct cardstate *cs = usb_get_intfdata(intf);
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int rc;
+
+       /* set suspend flag; this stops AT command/response traffic */
+       if (update_basstate(ucs, BS_SUSPEND, 0) & BS_SUSPEND) {
+               gig_dbg(DEBUG_SUSPEND, "already suspended");
+               return 0;
+       }
+
+       /* wait a bit for blocking conditions to go away */
+       rc = wait_event_timeout(ucs->waitqueue,
+                               !(ucs->basstate &
+                                 (BS_B1OPEN | BS_B2OPEN | BS_ATRDPEND | BS_ATWRPEND)),
+                               BAS_TIMEOUT * HZ / 10);
+       gig_dbg(DEBUG_SUSPEND, "wait_event_timeout() -> %d", rc);
+
+       /* check for conditions preventing suspend */
+       if (ucs->basstate & (BS_B1OPEN | BS_B2OPEN | BS_ATRDPEND | BS_ATWRPEND)) {
+               dev_warn(cs->dev, "cannot suspend:\n");
+               if (ucs->basstate & BS_B1OPEN)
+                       dev_warn(cs->dev, " B channel 1 open\n");
+               if (ucs->basstate & BS_B2OPEN)
+                       dev_warn(cs->dev, " B channel 2 open\n");
+               if (ucs->basstate & BS_ATRDPEND)
+                       dev_warn(cs->dev, " receiving AT reply\n");
+               if (ucs->basstate & BS_ATWRPEND)
+                       dev_warn(cs->dev, " sending AT command\n");
+               update_basstate(ucs, 0, BS_SUSPEND);
+               return -EBUSY;
+       }
+
+       /* close AT channel if open */
+       if (ucs->basstate & BS_ATOPEN) {
+               gig_dbg(DEBUG_SUSPEND, "closing AT channel");
+               rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, 0);
+               if (rc) {
+                       update_basstate(ucs, 0, BS_SUSPEND);
+                       return rc;
+               }
+               wait_event_timeout(ucs->waitqueue, !ucs->pending,
+                                  BAS_TIMEOUT * HZ / 10);
+               /* in case of timeout, proceed anyway */
+       }
+
+       /* kill all URBs and delayed work that might still be pending */
+       usb_kill_urb(ucs->urb_ctrl);
+       usb_kill_urb(ucs->urb_int_in);
+       del_timer_sync(&ucs->timer_ctrl);
+       del_timer_sync(&ucs->timer_atrdy);
+       del_timer_sync(&ucs->timer_cmd_in);
+       del_timer_sync(&ucs->timer_int_in);
+
+       /* don't try to cancel int_in_wq from within reset as it
+        * might be the one requesting the reset
+        */
+       if (message.event != PM_EVENT_ON)
+               cancel_work_sync(&ucs->int_in_wq);
+
+       gig_dbg(DEBUG_SUSPEND, "suspend complete");
+       return 0;
+}
+
+/* gigaset_resume
+ * This function is called after the USB connection has been resumed.
+ */
+static int gigaset_resume(struct usb_interface *intf)
+{
+       struct cardstate *cs = usb_get_intfdata(intf);
+       struct bas_cardstate *ucs = cs->hw.bas;
+       int rc;
+
+       /* resubmit interrupt URB for spontaneous messages from base */
+       rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
+       if (rc) {
+               dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
+                       get_usb_rcmsg(rc));
+               return rc;
+       }
+       ucs->retry_int_in = 0;
+
+       /* clear suspend flag to reallow activity */
+       update_basstate(ucs, 0, BS_SUSPEND);
+
+       gig_dbg(DEBUG_SUSPEND, "resume complete");
+       return 0;
+}
+
+/* gigaset_pre_reset
+ * This function is called before the USB connection is reset.
+ */
+static int gigaset_pre_reset(struct usb_interface *intf)
+{
+       /* handle just like suspend */
+       return gigaset_suspend(intf, PMSG_ON);
+}
+
+/* gigaset_post_reset
+ * This function is called after the USB connection has been reset.
+ */
+static int gigaset_post_reset(struct usb_interface *intf)
+{
+       /* FIXME: send HD_DEVICE_INIT_ACK? */
+
+       /* resume operations */
+       return gigaset_resume(intf);
+}
+
+
+static const struct gigaset_ops gigops = {
+       .write_cmd = gigaset_write_cmd,
+       .write_room = gigaset_write_room,
+       .chars_in_buffer = gigaset_chars_in_buffer,
+       .brkchars = gigaset_brkchars,
+       .init_bchannel = gigaset_init_bchannel,
+       .close_bchannel = gigaset_close_bchannel,
+       .initbcshw = gigaset_initbcshw,
+       .freebcshw = gigaset_freebcshw,
+       .reinitbcshw = gigaset_reinitbcshw,
+       .initcshw = gigaset_initcshw,
+       .freecshw = gigaset_freecshw,
+       .set_modem_ctrl = gigaset_set_modem_ctrl,
+       .baud_rate = gigaset_baud_rate,
+       .set_line_ctrl = gigaset_set_line_ctrl,
+       .send_skb = gigaset_isoc_send_skb,
+       .handle_input = gigaset_isoc_input,
+};
+
+/* bas_gigaset_init
+ * This function is called after the kernel module is loaded.
+ */
+static int __init bas_gigaset_init(void)
+{
+       int result;
+
+       /* allocate memory for our driver state and initialize it */
+       driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+                                   GIGASET_MODULENAME, GIGASET_DEVNAME,
+                                   &gigops, THIS_MODULE);
+       if (driver == NULL)
+               goto error;
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&gigaset_usb_driver);
+       if (result < 0) {
+               pr_err("error %d registering USB driver\n", -result);
+               goto error;
+       }
+
+       pr_info(DRIVER_DESC "\n");
+       return 0;
+
+error:
+       if (driver)
+               gigaset_freedriver(driver);
+       driver = NULL;
+       return -1;
+}
+
+/* bas_gigaset_exit
+ * This function is called before the kernel module is unloaded.
+ */
+static void __exit bas_gigaset_exit(void)
+{
+       struct bas_cardstate *ucs;
+       int i;
+
+       gigaset_blockdriver(driver); /* => probe will fail
+                                     * => no gigaset_start any more
+                                     */
+
+       /* stop all connected devices */
+       for (i = 0; i < driver->minors; i++) {
+               if (gigaset_shutdown(driver->cs + i) < 0)
+                       continue;               /* no device */
+               /* from now on, no isdn callback should be possible */
+
+               /* close all still open channels */
+               ucs = driver->cs[i].hw.bas;
+               if (ucs->basstate & BS_B1OPEN) {
+                       gig_dbg(DEBUG_INIT, "closing B1 channel");
+                       usb_control_msg(ucs->udev,
+                                       usb_sndctrlpipe(ucs->udev, 0),
+                                       HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ,
+                                       0, 0, NULL, 0, BAS_TIMEOUT);
+               }
+               if (ucs->basstate & BS_B2OPEN) {
+                       gig_dbg(DEBUG_INIT, "closing B2 channel");
+                       usb_control_msg(ucs->udev,
+                                       usb_sndctrlpipe(ucs->udev, 0),
+                                       HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ,
+                                       0, 0, NULL, 0, BAS_TIMEOUT);
+               }
+               if (ucs->basstate & BS_ATOPEN) {
+                       gig_dbg(DEBUG_INIT, "closing AT channel");
+                       usb_control_msg(ucs->udev,
+                                       usb_sndctrlpipe(ucs->udev, 0),
+                                       HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ,
+                                       0, 0, NULL, 0, BAS_TIMEOUT);
+               }
+               ucs->basstate = 0;
+       }
+
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&gigaset_usb_driver);
+       /* this will call the disconnect-callback */
+       /* from now on, no disconnect/probe callback should be running */
+
+       gigaset_freedriver(driver);
+       driver = NULL;
+}
+
+
+module_init(bas_gigaset_init);
+module_exit(bas_gigaset_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/isdn/gigaset/capi.c b/drivers/staging/isdn/gigaset/capi.c
new file mode 100644 (file)
index 0000000..9cb2ab5
--- /dev/null
@@ -0,0 +1,2520 @@
+/*
+ * Kernel CAPI interface for the Gigaset driver
+ *
+ * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ *     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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/ratelimit.h>
+#include <linux/isdn/capilli.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/export.h>
+
+/* missing from kernelcapi.h */
+#define CapiNcpiNotSupportedByProtocol 0x0001
+#define CapiFlagsNotSupportedByProtocol        0x0002
+#define CapiAlertAlreadySent           0x0003
+#define CapiFacilitySpecificFunctionNotSupported       0x3011
+
+/* missing from capicmd.h */
+#define CAPI_CONNECT_IND_BASELEN       (CAPI_MSG_BASELEN + 4 + 2 + 8 * 1)
+#define CAPI_CONNECT_ACTIVE_IND_BASELEN        (CAPI_MSG_BASELEN + 4 + 3 * 1)
+#define CAPI_CONNECT_B3_IND_BASELEN    (CAPI_MSG_BASELEN + 4 + 1)
+#define CAPI_CONNECT_B3_ACTIVE_IND_BASELEN     (CAPI_MSG_BASELEN + 4 + 1)
+#define CAPI_DATA_B3_REQ_LEN64         (CAPI_MSG_BASELEN + 4 + 4 + 2 + 2 + 2 + 8)
+#define CAPI_DATA_B3_CONF_LEN          (CAPI_MSG_BASELEN + 4 + 2 + 2)
+#define CAPI_DISCONNECT_IND_LEN                (CAPI_MSG_BASELEN + 4 + 2)
+#define CAPI_DISCONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN + 4 + 2 + 1)
+#define CAPI_FACILITY_CONF_BASELEN     (CAPI_MSG_BASELEN + 4 + 2 + 2 + 1)
+/* most _CONF messages contain only Controller/PLCI/NCCI and Info parameters */
+#define CAPI_STDCONF_LEN               (CAPI_MSG_BASELEN + 4 + 2)
+
+#define CAPI_FACILITY_HANDSET  0x0000
+#define CAPI_FACILITY_DTMF     0x0001
+#define CAPI_FACILITY_V42BIS   0x0002
+#define CAPI_FACILITY_SUPPSVC  0x0003
+#define CAPI_FACILITY_WAKEUP   0x0004
+#define CAPI_FACILITY_LI       0x0005
+
+#define CAPI_SUPPSVC_GETSUPPORTED      0x0000
+#define CAPI_SUPPSVC_LISTEN            0x0001
+
+/* missing from capiutil.h */
+#define CAPIMSG_PLCI_PART(m)   CAPIMSG_U8(m, 9)
+#define CAPIMSG_NCCI_PART(m)   CAPIMSG_U16(m, 10)
+#define CAPIMSG_HANDLE_REQ(m)  CAPIMSG_U16(m, 18) /* DATA_B3_REQ/_IND only! */
+#define CAPIMSG_FLAGS(m)       CAPIMSG_U16(m, 20)
+#define CAPIMSG_SETCONTROLLER(m, contr)        capimsg_setu8(m, 8, contr)
+#define CAPIMSG_SETPLCI_PART(m, plci)  capimsg_setu8(m, 9, plci)
+#define CAPIMSG_SETNCCI_PART(m, ncci)  capimsg_setu16(m, 10, ncci)
+#define CAPIMSG_SETFLAGS(m, flags)     capimsg_setu16(m, 20, flags)
+
+/* parameters with differing location in DATA_B3_CONF/_RESP: */
+#define CAPIMSG_SETHANDLE_CONF(m, handle)      capimsg_setu16(m, 12, handle)
+#define        CAPIMSG_SETINFO_CONF(m, info)           capimsg_setu16(m, 14, info)
+
+/* Flags (DATA_B3_REQ/_IND) */
+#define CAPI_FLAGS_DELIVERY_CONFIRMATION       0x04
+#define CAPI_FLAGS_RESERVED                    (~0x1f)
+
+/* buffer sizes */
+#define MAX_BC_OCTETS 11
+#define MAX_HLC_OCTETS 3
+#define MAX_NUMBER_DIGITS 20
+#define MAX_FMT_IE_LEN 20
+
+/* values for bcs->apconnstate */
+#define APCONN_NONE    0       /* inactive/listening */
+#define APCONN_SETUP   1       /* connecting */
+#define APCONN_ACTIVE  2       /* B channel up */
+
+/* registered application data structure */
+struct gigaset_capi_appl {
+       struct list_head ctrlist;
+       struct gigaset_capi_appl *bcnext;
+       u16 id;
+       struct capi_register_params rp;
+       u16 nextMessageNumber;
+       u32 listenInfoMask;
+       u32 listenCIPmask;
+};
+
+/* CAPI specific controller data structure */
+struct gigaset_capi_ctr {
+       struct capi_ctr ctr;
+       struct list_head appls;
+       struct sk_buff_head sendqueue;
+       atomic_t sendqlen;
+       /* two _cmsg structures possibly used concurrently: */
+       _cmsg hcmsg;    /* for message composition triggered from hardware */
+       _cmsg acmsg;    /* for dissection of messages sent from application */
+       u8 bc_buf[MAX_BC_OCTETS + 1];
+       u8 hlc_buf[MAX_HLC_OCTETS + 1];
+       u8 cgpty_buf[MAX_NUMBER_DIGITS + 3];
+       u8 cdpty_buf[MAX_NUMBER_DIGITS + 2];
+};
+
+/* CIP Value table (from CAPI 2.0 standard, ch. 6.1) */
+static struct {
+       u8 *bc;
+       u8 *hlc;
+} cip2bchlc[] = {
+       [1] = { "8090A3", NULL },       /* Speech (A-law) */
+       [2] = { "8890", NULL },         /* Unrestricted digital information */
+       [3] = { "8990", NULL },         /* Restricted digital information */
+       [4] = { "9090A3", NULL },       /* 3,1 kHz audio (A-law) */
+       [5] = { "9190", NULL },         /* 7 kHz audio */
+       [6] = { "9890", NULL },         /* Video */
+       [7] = { "88C0C6E6", NULL },     /* Packet mode */
+       [8] = { "8890218F", NULL },     /* 56 kbit/s rate adaptation */
+       [9] = { "9190A5", NULL },       /* Unrestricted digital information
+                                        * with tones/announcements */
+       [16] = { "8090A3", "9181" },    /* Telephony */
+       [17] = { "9090A3", "9184" },    /* Group 2/3 facsimile */
+       [18] = { "8890", "91A1" },      /* Group 4 facsimile Class 1 */
+       [19] = { "8890", "91A4" },      /* Teletex service basic and mixed mode
+                                        * and Group 4 facsimile service
+                                        * Classes II and III */
+       [20] = { "8890", "91A8" },      /* Teletex service basic and
+                                        * processable mode */
+       [21] = { "8890", "91B1" },      /* Teletex service basic mode */
+       [22] = { "8890", "91B2" },      /* International interworking for
+                                        * Videotex */
+       [23] = { "8890", "91B5" },      /* Telex */
+       [24] = { "8890", "91B8" },      /* Message Handling Systems
+                                        * in accordance with X.400 */
+       [25] = { "8890", "91C1" },      /* OSI application
+                                        * in accordance with X.200 */
+       [26] = { "9190A5", "9181" },    /* 7 kHz telephony */
+       [27] = { "9190A5", "916001" },  /* Video telephony, first connection */
+       [28] = { "8890", "916002" },    /* Video telephony, second connection */
+};
+
+/*
+ * helper functions
+ * ================
+ */
+
+/*
+ * emit unsupported parameter warning
+ */
+static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param,
+                                       char *msgname, char *paramname)
+{
+       if (param && *param)
+               dev_warn(cs->dev, "%s: ignoring unsupported parameter: %s\n",
+                        msgname, paramname);
+}
+
+/*
+ * convert an IE from Gigaset hex string to ETSI binary representation
+ * including length byte
+ * return value: result length, -1 on error
+ */
+static int encode_ie(char *in, u8 *out, int maxlen)
+{
+       int l = 0;
+       while (*in) {
+               if (!isxdigit(in[0]) || !isxdigit(in[1]) || l >= maxlen)
+                       return -1;
+               out[++l] = (hex_to_bin(in[0]) << 4) + hex_to_bin(in[1]);
+               in += 2;
+       }
+       out[0] = l;
+       return l;
+}
+
+/*
+ * convert an IE from ETSI binary representation including length byte
+ * to Gigaset hex string
+ */
+static void decode_ie(u8 *in, char *out)
+{
+       int i = *in;
+       while (i-- > 0) {
+               /* ToDo: conversion to upper case necessary? */
+               *out++ = toupper(hex_asc_hi(*++in));
+               *out++ = toupper(hex_asc_lo(*in));
+       }
+}
+
+/*
+ * retrieve application data structure for an application ID
+ */
+static inline struct gigaset_capi_appl *
+get_appl(struct gigaset_capi_ctr *iif, u16 appl)
+{
+       struct gigaset_capi_appl *ap;
+
+       list_for_each_entry(ap, &iif->appls, ctrlist)
+               if (ap->id == appl)
+                       return ap;
+       return NULL;
+}
+
+/*
+ * dump CAPI message to kernel messages for debugging
+ */
+static inline void dump_cmsg(enum debuglevel level, const char *tag, _cmsg *p)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+       /* dump at most 20 messages in 20 secs */
+       static DEFINE_RATELIMIT_STATE(msg_dump_ratelimit, 20 * HZ, 20);
+       _cdebbuf *cdb;
+
+       if (!(gigaset_debuglevel & level))
+               return;
+       if (!___ratelimit(&msg_dump_ratelimit, tag))
+               return;
+
+       cdb = capi_cmsg2str(p);
+       if (cdb) {
+               gig_dbg(level, "%s: [%d] %s", tag, p->ApplId, cdb->buf);
+               cdebbuf_free(cdb);
+       } else {
+               gig_dbg(level, "%s: [%d] %s", tag, p->ApplId,
+                       capi_cmd2str(p->Command, p->Subcommand));
+       }
+#endif
+}
+
+static inline void dump_rawmsg(enum debuglevel level, const char *tag,
+                              unsigned char *data)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+       char *dbgline;
+       int i, l;
+
+       if (!(gigaset_debuglevel & level))
+               return;
+
+       l = CAPIMSG_LEN(data);
+       if (l < 12) {
+               gig_dbg(level, "%s: ??? LEN=%04d", tag, l);
+               return;
+       }
+       gig_dbg(level, "%s: 0x%02x:0x%02x: ID=%03d #0x%04x LEN=%04d NCCI=0x%x",
+               tag, CAPIMSG_COMMAND(data), CAPIMSG_SUBCOMMAND(data),
+               CAPIMSG_APPID(data), CAPIMSG_MSGID(data), l,
+               CAPIMSG_CONTROL(data));
+       l -= 12;
+       if (l <= 0)
+               return;
+       if (l > 64)
+               l = 64; /* arbitrary limit */
+       dbgline = kmalloc_array(3, l, GFP_ATOMIC);
+       if (!dbgline)
+               return;
+       for (i = 0; i < l; i++) {
+               dbgline[3 * i] = hex_asc_hi(data[12 + i]);
+               dbgline[3 * i + 1] = hex_asc_lo(data[12 + i]);
+               dbgline[3 * i + 2] = ' ';
+       }
+       dbgline[3 * l - 1] = '\0';
+       gig_dbg(level, "  %s", dbgline);
+       kfree(dbgline);
+       if (CAPIMSG_COMMAND(data) == CAPI_DATA_B3 &&
+           (CAPIMSG_SUBCOMMAND(data) == CAPI_REQ ||
+            CAPIMSG_SUBCOMMAND(data) == CAPI_IND)) {
+               l = CAPIMSG_DATALEN(data);
+               gig_dbg(level, "   DataLength=%d", l);
+               if (l <= 0 || !(gigaset_debuglevel & DEBUG_LLDATA))
+                       return;
+               if (l > 64)
+                       l = 64; /* arbitrary limit */
+               dbgline = kmalloc_array(3, l, GFP_ATOMIC);
+               if (!dbgline)
+                       return;
+               data += CAPIMSG_LEN(data);
+               for (i = 0; i < l; i++) {
+                       dbgline[3 * i] = hex_asc_hi(data[i]);
+                       dbgline[3 * i + 1] = hex_asc_lo(data[i]);
+                       dbgline[3 * i + 2] = ' ';
+               }
+               dbgline[3 * l - 1] = '\0';
+               gig_dbg(level, "  %s", dbgline);
+               kfree(dbgline);
+       }
+#endif
+}
+
+/*
+ * format CAPI IE as string
+ */
+
+#ifdef CONFIG_GIGASET_DEBUG
+static const char *format_ie(const char *ie)
+{
+       static char result[3 * MAX_FMT_IE_LEN];
+       int len, count;
+       char *pout = result;
+
+       if (!ie)
+               return "NULL";
+
+       count = len = ie[0];
+       if (count > MAX_FMT_IE_LEN)
+               count = MAX_FMT_IE_LEN - 1;
+       while (count--) {
+               *pout++ = hex_asc_hi(*++ie);
+               *pout++ = hex_asc_lo(*ie);
+               *pout++ = ' ';
+       }
+       if (len > MAX_FMT_IE_LEN) {
+               *pout++ = '.';
+               *pout++ = '.';
+               *pout++ = '.';
+       }
+       *--pout = 0;
+       return result;
+}
+#endif
+
+/*
+ * emit DATA_B3_CONF message
+ */
+static void send_data_b3_conf(struct cardstate *cs, struct capi_ctr *ctr,
+                             u16 appl, u16 msgid, int channel,
+                             u16 handle, u16 info)
+{
+       struct sk_buff *cskb;
+       u8 *msg;
+
+       cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC);
+       if (!cskb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       /* frequent message, avoid _cmsg overhead */
+       msg = __skb_put(cskb, CAPI_DATA_B3_CONF_LEN);
+       CAPIMSG_SETLEN(msg, CAPI_DATA_B3_CONF_LEN);
+       CAPIMSG_SETAPPID(msg, appl);
+       CAPIMSG_SETCOMMAND(msg, CAPI_DATA_B3);
+       CAPIMSG_SETSUBCOMMAND(msg,  CAPI_CONF);
+       CAPIMSG_SETMSGID(msg, msgid);
+       CAPIMSG_SETCONTROLLER(msg, ctr->cnr);
+       CAPIMSG_SETPLCI_PART(msg, channel);
+       CAPIMSG_SETNCCI_PART(msg, 1);
+       CAPIMSG_SETHANDLE_CONF(msg, handle);
+       CAPIMSG_SETINFO_CONF(msg, info);
+
+       /* emit message */
+       dump_rawmsg(DEBUG_MCMD, __func__, msg);
+       capi_ctr_handle_message(ctr, appl, cskb);
+}
+
+
+/*
+ * driver interface functions
+ * ==========================
+ */
+
+/**
+ * gigaset_skb_sent() - acknowledge transmission of outgoing skb
+ * @bcs:       B channel descriptor structure.
+ * @skb:       sent data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when the data in a
+ * skb has been successfully sent, for signalling completion to the LL.
+ */
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
+{
+       struct cardstate *cs = bcs->cs;
+       struct gigaset_capi_ctr *iif = cs->iif;
+       struct gigaset_capi_appl *ap = bcs->ap;
+       unsigned char *req = skb_mac_header(dskb);
+       u16 flags;
+
+       /* update statistics */
+       ++bcs->trans_up;
+
+       if (!ap) {
+               gig_dbg(DEBUG_MCMD, "%s: application gone", __func__);
+               return;
+       }
+
+       /* don't send further B3 messages if disconnected */
+       if (bcs->apconnstate < APCONN_ACTIVE) {
+               gig_dbg(DEBUG_MCMD, "%s: disconnected", __func__);
+               return;
+       }
+
+       /*
+        * send DATA_B3_CONF if "delivery confirmation" bit was set in request;
+        * otherwise it has already been sent by do_data_b3_req()
+        */
+       flags = CAPIMSG_FLAGS(req);
+       if (flags & CAPI_FLAGS_DELIVERY_CONFIRMATION)
+               send_data_b3_conf(cs, &iif->ctr, ap->id, CAPIMSG_MSGID(req),
+                                 bcs->channel + 1, CAPIMSG_HANDLE_REQ(req),
+                                 (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION) ?
+                                 CapiFlagsNotSupportedByProtocol :
+                                 CAPI_NOERROR);
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+
+/**
+ * gigaset_skb_rcvd() - pass received skb to LL
+ * @bcs:       B channel descriptor structure.
+ * @skb:       received data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when user data has
+ * been successfully received, for passing to the LL.
+ * Warning: skb must not be accessed anymore!
+ */
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+       struct cardstate *cs = bcs->cs;
+       struct gigaset_capi_ctr *iif = cs->iif;
+       struct gigaset_capi_appl *ap = bcs->ap;
+       int len = skb->len;
+
+       /* update statistics */
+       bcs->trans_down++;
+
+       if (!ap) {
+               gig_dbg(DEBUG_MCMD, "%s: application gone", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       /* don't send further B3 messages if disconnected */
+       if (bcs->apconnstate < APCONN_ACTIVE) {
+               gig_dbg(DEBUG_MCMD, "%s: disconnected", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       /*
+        * prepend DATA_B3_IND message to payload
+        * Parameters: NCCI = 1, all others 0/unused
+        * frequent message, avoid _cmsg overhead
+        */
+       skb_push(skb, CAPI_DATA_B3_REQ_LEN);
+       CAPIMSG_SETLEN(skb->data, CAPI_DATA_B3_REQ_LEN);
+       CAPIMSG_SETAPPID(skb->data, ap->id);
+       CAPIMSG_SETCOMMAND(skb->data, CAPI_DATA_B3);
+       CAPIMSG_SETSUBCOMMAND(skb->data,  CAPI_IND);
+       CAPIMSG_SETMSGID(skb->data, ap->nextMessageNumber++);
+       CAPIMSG_SETCONTROLLER(skb->data, iif->ctr.cnr);
+       CAPIMSG_SETPLCI_PART(skb->data, bcs->channel + 1);
+       CAPIMSG_SETNCCI_PART(skb->data, 1);
+       /* Data parameter not used */
+       CAPIMSG_SETDATALEN(skb->data, len);
+       /* Data handle parameter not used */
+       CAPIMSG_SETFLAGS(skb->data, 0);
+       /* Data64 parameter not present */
+
+       /* emit message */
+       dump_rawmsg(DEBUG_MCMD, __func__, skb->data);
+       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+/**
+ * gigaset_isdn_rcv_err() - signal receive error
+ * @bcs:       B channel descriptor structure.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when a receive error
+ * has occurred, for signalling to the LL.
+ */
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+       /* if currently ignoring packets, just count down */
+       if (bcs->ignore) {
+               bcs->ignore--;
+               return;
+       }
+
+       /* update statistics */
+       bcs->corrupted++;
+
+       /* ToDo: signal error -> LL */
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
+/**
+ * gigaset_isdn_icall() - signal incoming call
+ * @at_state:  connection state structure.
+ *
+ * Called by main module at tasklet level to notify the LL that an incoming
+ * call has been received. @at_state contains the parameters of the call.
+ *
+ * Return value: call disposition (ICALL_*)
+ */
+int gigaset_isdn_icall(struct at_state_t *at_state)
+{
+       struct cardstate *cs = at_state->cs;
+       struct bc_state *bcs = at_state->bcs;
+       struct gigaset_capi_ctr *iif = cs->iif;
+       struct gigaset_capi_appl *ap;
+       u32 actCIPmask;
+       struct sk_buff *skb;
+       unsigned int msgsize;
+       unsigned long flags;
+       int i;
+
+       /*
+        * ToDo: signal calls without a free B channel, too
+        * (requires a u8 handle for the at_state structure that can
+        * be stored in the PLCI and used in the CONNECT_RESP message
+        * handler to retrieve it)
+        */
+       if (!bcs)
+               return ICALL_IGNORE;
+
+       /* prepare CONNECT_IND message, using B channel number as PLCI */
+       capi_cmsg_header(&iif->hcmsg, 0, CAPI_CONNECT, CAPI_IND, 0,
+                        iif->ctr.cnr | ((bcs->channel + 1) << 8));
+
+       /* minimum size, all structs empty */
+       msgsize = CAPI_CONNECT_IND_BASELEN;
+
+       /* Bearer Capability (mandatory) */
+       if (at_state->str_var[STR_ZBC]) {
+               /* pass on BC from Gigaset */
+               if (encode_ie(at_state->str_var[STR_ZBC], iif->bc_buf,
+                             MAX_BC_OCTETS) < 0) {
+                       dev_warn(cs->dev, "RING ignored - bad BC %s\n",
+                                at_state->str_var[STR_ZBC]);
+                       return ICALL_IGNORE;
+               }
+
+               /* look up corresponding CIP value */
+               iif->hcmsg.CIPValue = 0;        /* default if nothing found */
+               for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
+                       if (cip2bchlc[i].bc != NULL &&
+                           cip2bchlc[i].hlc == NULL &&
+                           !strcmp(cip2bchlc[i].bc,
+                                   at_state->str_var[STR_ZBC])) {
+                               iif->hcmsg.CIPValue = i;
+                               break;
+                       }
+       } else {
+               /* no BC (internal call): assume CIP 1 (speech, A-law) */
+               iif->hcmsg.CIPValue = 1;
+               encode_ie(cip2bchlc[1].bc, iif->bc_buf, MAX_BC_OCTETS);
+       }
+       iif->hcmsg.BC = iif->bc_buf;
+       msgsize += iif->hcmsg.BC[0];
+
+       /* High Layer Compatibility (optional) */
+       if (at_state->str_var[STR_ZHLC]) {
+               /* pass on HLC from Gigaset */
+               if (encode_ie(at_state->str_var[STR_ZHLC], iif->hlc_buf,
+                             MAX_HLC_OCTETS) < 0) {
+                       dev_warn(cs->dev, "RING ignored - bad HLC %s\n",
+                                at_state->str_var[STR_ZHLC]);
+                       return ICALL_IGNORE;
+               }
+               iif->hcmsg.HLC = iif->hlc_buf;
+               msgsize += iif->hcmsg.HLC[0];
+
+               /* look up corresponding CIP value */
+               /* keep BC based CIP value if none found */
+               if (at_state->str_var[STR_ZBC])
+                       for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
+                               if (cip2bchlc[i].hlc != NULL &&
+                                   !strcmp(cip2bchlc[i].hlc,
+                                           at_state->str_var[STR_ZHLC]) &&
+                                   !strcmp(cip2bchlc[i].bc,
+                                           at_state->str_var[STR_ZBC])) {
+                                       iif->hcmsg.CIPValue = i;
+                                       break;
+                               }
+       }
+
+       /* Called Party Number (optional) */
+       if (at_state->str_var[STR_ZCPN]) {
+               i = strlen(at_state->str_var[STR_ZCPN]);
+               if (i > MAX_NUMBER_DIGITS) {
+                       dev_warn(cs->dev, "RING ignored - bad number %s\n",
+                                at_state->str_var[STR_ZBC]);
+                       return ICALL_IGNORE;
+               }
+               iif->cdpty_buf[0] = i + 1;
+               iif->cdpty_buf[1] = 0x80; /* type / numbering plan unknown */
+               memcpy(iif->cdpty_buf + 2, at_state->str_var[STR_ZCPN], i);
+               iif->hcmsg.CalledPartyNumber = iif->cdpty_buf;
+               msgsize += iif->hcmsg.CalledPartyNumber[0];
+       }
+
+       /* Calling Party Number (optional) */
+       if (at_state->str_var[STR_NMBR]) {
+               i = strlen(at_state->str_var[STR_NMBR]);
+               if (i > MAX_NUMBER_DIGITS) {
+                       dev_warn(cs->dev, "RING ignored - bad number %s\n",
+                                at_state->str_var[STR_ZBC]);
+                       return ICALL_IGNORE;
+               }
+               iif->cgpty_buf[0] = i + 2;
+               iif->cgpty_buf[1] = 0x00; /* type / numbering plan unknown */
+               iif->cgpty_buf[2] = 0x80; /* pres. allowed, not screened */
+               memcpy(iif->cgpty_buf + 3, at_state->str_var[STR_NMBR], i);
+               iif->hcmsg.CallingPartyNumber = iif->cgpty_buf;
+               msgsize += iif->hcmsg.CallingPartyNumber[0];
+       }
+
+       /* remaining parameters (not supported, always left NULL):
+        * - CalledPartySubaddress
+        * - CallingPartySubaddress
+        * - AdditionalInfo
+        *   - BChannelinformation
+        *   - Keypadfacility
+        *   - Useruserdata
+        *   - Facilitydataarray
+        */
+
+       gig_dbg(DEBUG_CMD, "icall: PLCI %x CIP %d BC %s",
+               iif->hcmsg.adr.adrPLCI, iif->hcmsg.CIPValue,
+               format_ie(iif->hcmsg.BC));
+       gig_dbg(DEBUG_CMD, "icall: HLC %s",
+               format_ie(iif->hcmsg.HLC));
+       gig_dbg(DEBUG_CMD, "icall: CgPty %s",
+               format_ie(iif->hcmsg.CallingPartyNumber));
+       gig_dbg(DEBUG_CMD, "icall: CdPty %s",
+               format_ie(iif->hcmsg.CalledPartyNumber));
+
+       /* scan application list for matching listeners */
+       spin_lock_irqsave(&bcs->aplock, flags);
+       if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE) {
+               dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
+                        __func__, bcs->ap, bcs->apconnstate);
+               bcs->ap = NULL;
+               bcs->apconnstate = APCONN_NONE;
+       }
+       spin_unlock_irqrestore(&bcs->aplock, flags);
+       actCIPmask = 1 | (1 << iif->hcmsg.CIPValue);
+       list_for_each_entry(ap, &iif->appls, ctrlist)
+               if (actCIPmask & ap->listenCIPmask) {
+                       /* build CONNECT_IND message for this application */
+                       iif->hcmsg.ApplId = ap->id;
+                       iif->hcmsg.Messagenumber = ap->nextMessageNumber++;
+
+                       skb = alloc_skb(msgsize, GFP_ATOMIC);
+                       if (!skb) {
+                               dev_err(cs->dev, "%s: out of memory\n",
+                                       __func__);
+                               break;
+                       }
+                       if (capi_cmsg2message(&iif->hcmsg,
+                                             __skb_put(skb, msgsize))) {
+                               dev_err(cs->dev, "%s: message parser failure\n",
+                                       __func__);
+                               dev_kfree_skb_any(skb);
+                               break;
+                       }
+                       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+
+                       /* add to listeners on this B channel, update state */
+                       spin_lock_irqsave(&bcs->aplock, flags);
+                       ap->bcnext = bcs->ap;
+                       bcs->ap = ap;
+                       bcs->chstate |= CHS_NOTIFY_LL;
+                       bcs->apconnstate = APCONN_SETUP;
+                       spin_unlock_irqrestore(&bcs->aplock, flags);
+
+                       /* emit message */
+                       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+               }
+
+       /*
+        * Return "accept" if any listeners.
+        * Gigaset will send ALERTING.
+        * There doesn't seem to be a way to avoid this.
+        */
+       return bcs->ap ? ICALL_ACCEPT : ICALL_IGNORE;
+}
+
+/*
+ * send a DISCONNECT_IND message to an application
+ * does not sleep, clobbers the controller's hcmsg structure
+ */
+static void send_disconnect_ind(struct bc_state *bcs,
+                               struct gigaset_capi_appl *ap, u16 reason)
+{
+       struct cardstate *cs = bcs->cs;
+       struct gigaset_capi_ctr *iif = cs->iif;
+       struct sk_buff *skb;
+
+       if (bcs->apconnstate == APCONN_NONE)
+               return;
+
+       capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND,
+                        ap->nextMessageNumber++,
+                        iif->ctr.cnr | ((bcs->channel + 1) << 8));
+       iif->hcmsg.Reason = reason;
+       skb = alloc_skb(CAPI_DISCONNECT_IND_LEN, GFP_ATOMIC);
+       if (!skb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       if (capi_cmsg2message(&iif->hcmsg,
+                             __skb_put(skb, CAPI_DISCONNECT_IND_LEN))) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/*
+ * send a DISCONNECT_B3_IND message to an application
+ * Parameters: NCCI = 1, NCPI empty, Reason_B3 = 0
+ * does not sleep, clobbers the controller's hcmsg structure
+ */
+static void send_disconnect_b3_ind(struct bc_state *bcs,
+                                  struct gigaset_capi_appl *ap)
+{
+       struct cardstate *cs = bcs->cs;
+       struct gigaset_capi_ctr *iif = cs->iif;
+       struct sk_buff *skb;
+
+       /* nothing to do if no logical connection active */
+       if (bcs->apconnstate < APCONN_ACTIVE)
+               return;
+       bcs->apconnstate = APCONN_SETUP;
+
+       capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
+                        ap->nextMessageNumber++,
+                        iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+       skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_ATOMIC);
+       if (!skb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       if (capi_cmsg2message(&iif->hcmsg,
+                         __skb_put(skb, CAPI_DISCONNECT_B3_IND_BASELEN))) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_connD() - signal D channel connect
+ * @bcs:       B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the D channel
+ * connection has been established.
+ */
+void gigaset_isdn_connD(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+       struct gigaset_capi_ctr *iif = cs->iif;
+       struct gigaset_capi_appl *ap;
+       struct sk_buff *skb;
+       unsigned int msgsize;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bcs->aplock, flags);
+       ap = bcs->ap;
+       if (!ap) {
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+               gig_dbg(DEBUG_CMD, "%s: application gone", __func__);
+               return;
+       }
+       if (bcs->apconnstate == APCONN_NONE) {
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+               dev_warn(cs->dev, "%s: application %u not connected\n",
+                        __func__, ap->id);
+               return;
+       }
+       spin_unlock_irqrestore(&bcs->aplock, flags);
+       while (ap->bcnext) {
+               /* this should never happen */
+               dev_warn(cs->dev, "%s: dropping extra application %u\n",
+                        __func__, ap->bcnext->id);
+               send_disconnect_ind(bcs, ap->bcnext,
+                                   CapiCallGivenToOtherApplication);
+               ap->bcnext = ap->bcnext->bcnext;
+       }
+
+       /* prepare CONNECT_ACTIVE_IND message
+        * Note: LLC not supported by device
+        */
+       capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_CONNECT_ACTIVE, CAPI_IND,
+                        ap->nextMessageNumber++,
+                        iif->ctr.cnr | ((bcs->channel + 1) << 8));
+
+       /* minimum size, all structs empty */
+       msgsize = CAPI_CONNECT_ACTIVE_IND_BASELEN;
+
+       /* ToDo: set parameter: Connected number
+        * (requires ev-layer state machine extension to collect
+        * ZCON device reply)
+        */
+
+       /* build and emit CONNECT_ACTIVE_IND message */
+       skb = alloc_skb(msgsize, GFP_ATOMIC);
+       if (!skb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       if (capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize))) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_hupD() - signal D channel hangup
+ * @bcs:       B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the D channel
+ * connection has been shut down.
+ */
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+       struct gigaset_capi_appl *ap;
+       unsigned long flags;
+
+       /*
+        * ToDo: pass on reason code reported by device
+        * (requires ev-layer state machine extension to collect
+        * ZCAU device reply)
+        */
+       spin_lock_irqsave(&bcs->aplock, flags);
+       while (bcs->ap != NULL) {
+               ap = bcs->ap;
+               bcs->ap = ap->bcnext;
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+               send_disconnect_b3_ind(bcs, ap);
+               send_disconnect_ind(bcs, ap, 0);
+               spin_lock_irqsave(&bcs->aplock, flags);
+       }
+       bcs->apconnstate = APCONN_NONE;
+       spin_unlock_irqrestore(&bcs->aplock, flags);
+}
+
+/**
+ * gigaset_isdn_connB() - signal B channel connect
+ * @bcs:       B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the B channel
+ * connection has been established.
+ */
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+       struct gigaset_capi_ctr *iif = cs->iif;
+       struct gigaset_capi_appl *ap;
+       struct sk_buff *skb;
+       unsigned long flags;
+       unsigned int msgsize;
+       u8 command;
+
+       spin_lock_irqsave(&bcs->aplock, flags);
+       ap = bcs->ap;
+       if (!ap) {
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+               gig_dbg(DEBUG_CMD, "%s: application gone", __func__);
+               return;
+       }
+       if (!bcs->apconnstate) {
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+               dev_warn(cs->dev, "%s: application %u not connected\n",
+                        __func__, ap->id);
+               return;
+       }
+
+       /*
+        * emit CONNECT_B3_ACTIVE_IND if we already got CONNECT_B3_REQ;
+        * otherwise we have to emit CONNECT_B3_IND first, and follow up with
+        * CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP
+        * Parameters in both cases always: NCCI = 1, NCPI empty
+        */
+       if (bcs->apconnstate >= APCONN_ACTIVE) {
+               command = CAPI_CONNECT_B3_ACTIVE;
+               msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
+       } else {
+               command = CAPI_CONNECT_B3;
+               msgsize = CAPI_CONNECT_B3_IND_BASELEN;
+       }
+       bcs->apconnstate = APCONN_ACTIVE;
+
+       spin_unlock_irqrestore(&bcs->aplock, flags);
+
+       while (ap->bcnext) {
+               /* this should never happen */
+               dev_warn(cs->dev, "%s: dropping extra application %u\n",
+                        __func__, ap->bcnext->id);
+               send_disconnect_ind(bcs, ap->bcnext,
+                                   CapiCallGivenToOtherApplication);
+               ap->bcnext = ap->bcnext->bcnext;
+       }
+
+       capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND,
+                        ap->nextMessageNumber++,
+                        iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+       skb = alloc_skb(msgsize, GFP_ATOMIC);
+       if (!skb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       if (capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize))) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_hupB() - signal B channel hangup
+ * @bcs:       B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+       struct gigaset_capi_appl *ap = bcs->ap;
+
+       /* ToDo: assure order of DISCONNECT_B3_IND and DISCONNECT_IND ? */
+
+       if (!ap) {
+               gig_dbg(DEBUG_CMD, "%s: application gone", __func__);
+               return;
+       }
+
+       send_disconnect_b3_ind(bcs, ap);
+}
+
+/**
+ * gigaset_isdn_start() - signal device availability
+ * @cs:                device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is available for
+ * use.
+ */
+void gigaset_isdn_start(struct cardstate *cs)
+{
+       struct gigaset_capi_ctr *iif = cs->iif;
+
+       /* fill profile data: manufacturer name */
+       strcpy(iif->ctr.manu, "Siemens");
+       /* CAPI and device version */
+       iif->ctr.version.majorversion = 2;              /* CAPI 2.0 */
+       iif->ctr.version.minorversion = 0;
+       /* ToDo: check/assert cs->gotfwver? */
+       iif->ctr.version.majormanuversion = cs->fwver[0];
+       iif->ctr.version.minormanuversion = cs->fwver[1];
+       /* number of B channels supported */
+       iif->ctr.profile.nbchannel = cs->channels;
+       /* global options: internal controller, supplementary services */
+       iif->ctr.profile.goptions = 0x11;
+       /* B1 protocols: 64 kbit/s HDLC or transparent */
+       iif->ctr.profile.support1 =  0x03;
+       /* B2 protocols: transparent only */
+       /* ToDo: X.75 SLP ? */
+       iif->ctr.profile.support2 =  0x02;
+       /* B3 protocols: transparent only */
+       iif->ctr.profile.support3 =  0x01;
+       /* no serial number */
+       strcpy(iif->ctr.serial, "0");
+       capi_ctr_ready(&iif->ctr);
+}
+
+/**
+ * gigaset_isdn_stop() - signal device unavailability
+ * @cs:                device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is no longer
+ * available for use.
+ */
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+       struct gigaset_capi_ctr *iif = cs->iif;
+       capi_ctr_down(&iif->ctr);
+}
+
+/*
+ * kernel CAPI callback methods
+ * ============================
+ */
+
+/*
+ * register CAPI application
+ */
+static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl,
+                                 capi_register_params *rp)
+{
+       struct gigaset_capi_ctr *iif
+               = container_of(ctr, struct gigaset_capi_ctr, ctr);
+       struct cardstate *cs = ctr->driverdata;
+       struct gigaset_capi_appl *ap;
+
+       gig_dbg(DEBUG_CMD, "%s [%u] l3cnt=%u blkcnt=%u blklen=%u",
+               __func__, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
+
+       list_for_each_entry(ap, &iif->appls, ctrlist)
+               if (ap->id == appl) {
+                       dev_notice(cs->dev,
+                                  "application %u already registered\n", appl);
+                       return;
+               }
+
+       ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+       if (!ap) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       ap->id = appl;
+       ap->rp = *rp;
+
+       list_add(&ap->ctrlist, &iif->appls);
+       dev_info(cs->dev, "application %u registered\n", ap->id);
+}
+
+/*
+ * remove CAPI application from channel
+ * helper function to keep indentation levels down and stay in 80 columns
+ */
+
+static inline void remove_appl_from_channel(struct bc_state *bcs,
+                                           struct gigaset_capi_appl *ap)
+{
+       struct cardstate *cs = bcs->cs;
+       struct gigaset_capi_appl *bcap;
+       unsigned long flags;
+       int prevconnstate;
+
+       spin_lock_irqsave(&bcs->aplock, flags);
+       bcap = bcs->ap;
+       if (bcap == NULL) {
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+               return;
+       }
+
+       /* check first application on channel */
+       if (bcap == ap) {
+               bcs->ap = ap->bcnext;
+               if (bcs->ap != NULL) {
+                       spin_unlock_irqrestore(&bcs->aplock, flags);
+                       return;
+               }
+
+               /* none left, clear channel state */
+               prevconnstate = bcs->apconnstate;
+               bcs->apconnstate = APCONN_NONE;
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+
+               if (prevconnstate == APCONN_ACTIVE) {
+                       dev_notice(cs->dev, "%s: hanging up channel %u\n",
+                                  __func__, bcs->channel);
+                       gigaset_add_event(cs, &bcs->at_state,
+                                         EV_HUP, NULL, 0, NULL);
+                       gigaset_schedule_event(cs);
+               }
+               return;
+       }
+
+       /* check remaining list */
+       do {
+               if (bcap->bcnext == ap) {
+                       bcap->bcnext = bcap->bcnext->bcnext;
+                       spin_unlock_irqrestore(&bcs->aplock, flags);
+                       return;
+               }
+               bcap = bcap->bcnext;
+       } while (bcap != NULL);
+       spin_unlock_irqrestore(&bcs->aplock, flags);
+}
+
+/*
+ * release CAPI application
+ */
+static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl)
+{
+       struct gigaset_capi_ctr *iif
+               = container_of(ctr, struct gigaset_capi_ctr, ctr);
+       struct cardstate *cs = iif->ctr.driverdata;
+       struct gigaset_capi_appl *ap, *tmp;
+       unsigned ch;
+
+       gig_dbg(DEBUG_CMD, "%s [%u]", __func__, appl);
+
+       list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist)
+               if (ap->id == appl) {
+                       /* remove from any channels */
+                       for (ch = 0; ch < cs->channels; ch++)
+                               remove_appl_from_channel(&cs->bcs[ch], ap);
+
+                       /* remove from registration list */
+                       list_del(&ap->ctrlist);
+                       kfree(ap);
+                       dev_info(cs->dev, "application %u released\n", appl);
+               }
+}
+
+/*
+ * =====================================================================
+ * outgoing CAPI message handler
+ * =====================================================================
+ */
+
+/*
+ * helper function: emit reply message with given Info value
+ */
+static void send_conf(struct gigaset_capi_ctr *iif,
+                     struct gigaset_capi_appl *ap,
+                     struct sk_buff *skb,
+                     u16 info)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+
+       /*
+        * _CONF replies always only have NCCI and Info parameters
+        * so they'll fit into the _REQ message skb
+        */
+       capi_cmsg_answer(&iif->acmsg);
+       iif->acmsg.Info = info;
+       if (capi_cmsg2message(&iif->acmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       __skb_trim(skb, CAPI_STDCONF_LEN);
+       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/*
+ * process FACILITY_REQ message
+ */
+static void do_facility_req(struct gigaset_capi_ctr *iif,
+                           struct gigaset_capi_appl *ap,
+                           struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       _cmsg *cmsg = &iif->acmsg;
+       struct sk_buff *cskb;
+       u8 *pparam;
+       unsigned int msgsize = CAPI_FACILITY_CONF_BASELEN;
+       u16 function, info;
+       static u8 confparam[10];        /* max. 9 octets + length byte */
+
+       /* decode message */
+       if (capi_message2cmsg(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+       /*
+        * Facility Request Parameter is not decoded by capi_message2cmsg()
+        * encoding depends on Facility Selector
+        */
+       switch (cmsg->FacilitySelector) {
+       case CAPI_FACILITY_DTMF:        /* ToDo */
+               info = CapiFacilityNotSupported;
+               confparam[0] = 2;       /* length */
+               /* DTMF information: Unknown DTMF request */
+               capimsg_setu16(confparam, 1, 2);
+               break;
+
+       case CAPI_FACILITY_V42BIS:      /* not supported */
+               info = CapiFacilityNotSupported;
+               confparam[0] = 2;       /* length */
+               /* V.42 bis information: not available */
+               capimsg_setu16(confparam, 1, 1);
+               break;
+
+       case CAPI_FACILITY_SUPPSVC:
+               /* decode Function parameter */
+               pparam = cmsg->FacilityRequestParameter;
+               if (pparam == NULL || pparam[0] < 2) {
+                       dev_notice(cs->dev, "%s: %s missing\n", "FACILITY_REQ",
+                                  "Facility Request Parameter");
+                       send_conf(iif, ap, skb, CapiIllMessageParmCoding);
+                       return;
+               }
+               function = CAPIMSG_U16(pparam, 1);
+               switch (function) {
+               case CAPI_SUPPSVC_GETSUPPORTED:
+                       info = CapiSuccess;
+                       /* Supplementary Service specific parameter */
+                       confparam[3] = 6;       /* length */
+                       /* Supplementary services info: Success */
+                       capimsg_setu16(confparam, 4, CapiSuccess);
+                       /* Supported Services: none */
+                       capimsg_setu32(confparam, 6, 0);
+                       break;
+               case CAPI_SUPPSVC_LISTEN:
+                       if (pparam[0] < 7 || pparam[3] < 4) {
+                               dev_notice(cs->dev, "%s: %s missing\n",
+                                          "FACILITY_REQ", "Notification Mask");
+                               send_conf(iif, ap, skb,
+                                         CapiIllMessageParmCoding);
+                               return;
+                       }
+                       if (CAPIMSG_U32(pparam, 4) != 0) {
+                               dev_notice(cs->dev,
+                                          "%s: unsupported supplementary service notification mask 0x%x\n",
+                                          "FACILITY_REQ", CAPIMSG_U32(pparam, 4));
+                               info = CapiFacilitySpecificFunctionNotSupported;
+                               confparam[3] = 2;       /* length */
+                               capimsg_setu16(confparam, 4,
+                                              CapiSupplementaryServiceNotSupported);
+                               break;
+                       }
+                       info = CapiSuccess;
+                       confparam[3] = 2;       /* length */
+                       capimsg_setu16(confparam, 4, CapiSuccess);
+                       break;
+
+               /* ToDo: add supported services */
+
+               default:
+                       dev_notice(cs->dev,
+                                  "%s: unsupported supplementary service function 0x%04x\n",
+                                  "FACILITY_REQ", function);
+                       info = CapiFacilitySpecificFunctionNotSupported;
+                       /* Supplementary Service specific parameter */
+                       confparam[3] = 2;       /* length */
+                       /* Supplementary services info: not supported */
+                       capimsg_setu16(confparam, 4,
+                                      CapiSupplementaryServiceNotSupported);
+               }
+
+               /* Facility confirmation parameter */
+               confparam[0] = confparam[3] + 3;        /* total length */
+               /* Function: copy from _REQ message */
+               capimsg_setu16(confparam, 1, function);
+               /* Supplementary Service specific parameter already set above */
+               break;
+
+       case CAPI_FACILITY_WAKEUP:      /* ToDo */
+               info = CapiFacilityNotSupported;
+               confparam[0] = 2;       /* length */
+               /* Number of accepted awake request parameters: 0 */
+               capimsg_setu16(confparam, 1, 0);
+               break;
+
+       default:
+               info = CapiFacilityNotSupported;
+               confparam[0] = 0;       /* empty struct */
+       }
+
+       /* send FACILITY_CONF with given Info and confirmation parameter */
+       dev_kfree_skb_any(skb);
+       capi_cmsg_answer(cmsg);
+       cmsg->Info = info;
+       cmsg->FacilityConfirmationParameter = confparam;
+       msgsize += confparam[0];        /* length */
+       cskb = alloc_skb(msgsize, GFP_ATOMIC);
+       if (!cskb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       if (capi_cmsg2message(cmsg, __skb_put(cskb, msgsize))) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(cskb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+       capi_ctr_handle_message(&iif->ctr, ap->id, cskb);
+}
+
+
+/*
+ * process LISTEN_REQ message
+ * just store the masks in the application data structure
+ */
+static void do_listen_req(struct gigaset_capi_ctr *iif,
+                         struct gigaset_capi_appl *ap,
+                         struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+
+       /* decode message */
+       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+
+       /* store listening parameters */
+       ap->listenInfoMask = iif->acmsg.InfoMask;
+       ap->listenCIPmask = iif->acmsg.CIPmask;
+       send_conf(iif, ap, skb, CapiSuccess);
+}
+
+/*
+ * process ALERT_REQ message
+ * nothing to do, Gigaset always alerts anyway
+ */
+static void do_alert_req(struct gigaset_capi_ctr *iif,
+                        struct gigaset_capi_appl *ap,
+                        struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+
+       /* decode message */
+       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+       send_conf(iif, ap, skb, CapiAlertAlreadySent);
+}
+
+/*
+ * process CONNECT_REQ message
+ * allocate a B channel, prepare dial commands, queue a DIAL event,
+ * emit CONNECT_CONF reply
+ */
+static void do_connect_req(struct gigaset_capi_ctr *iif,
+                          struct gigaset_capi_appl *ap,
+                          struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
+       char **commands;
+       char *s;
+       u8 *pp;
+       unsigned long flags;
+       int i, l, lbc, lhlc;
+       u16 info;
+
+       /* decode message */
+       if (capi_message2cmsg(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+       /* get free B channel & construct PLCI */
+       bcs = gigaset_get_free_channel(cs);
+       if (!bcs) {
+               dev_notice(cs->dev, "%s: no B channel available\n",
+                          "CONNECT_REQ");
+               send_conf(iif, ap, skb, CapiNoPlciAvailable);
+               return;
+       }
+       spin_lock_irqsave(&bcs->aplock, flags);
+       if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE)
+               dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
+                        __func__, bcs->ap, bcs->apconnstate);
+       ap->bcnext = NULL;
+       bcs->ap = ap;
+       bcs->apconnstate = APCONN_SETUP;
+       spin_unlock_irqrestore(&bcs->aplock, flags);
+
+       bcs->rx_bufsize = ap->rp.datablklen;
+       dev_kfree_skb(bcs->rx_skb);
+       gigaset_new_rx_skb(bcs);
+       cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8;
+
+       /* build command table */
+       commands = kcalloc(AT_NUM, sizeof(*commands), GFP_KERNEL);
+       if (!commands)
+               goto oom;
+
+       /* encode parameter: Called party number */
+       pp = cmsg->CalledPartyNumber;
+       if (pp == NULL || *pp == 0) {
+               dev_notice(cs->dev, "%s: %s missing\n",
+                          "CONNECT_REQ", "Called party number");
+               info = CapiIllMessageParmCoding;
+               goto error;
+       }
+       l = *pp++;
+       /* check type of number/numbering plan byte */
+       switch (*pp) {
+       case 0x80:      /* unknown type / unknown numbering plan */
+       case 0x81:      /* unknown type / ISDN/Telephony numbering plan */
+               break;
+       default:        /* others: warn about potential misinterpretation */
+               dev_notice(cs->dev, "%s: %s type/plan 0x%02x unsupported\n",
+                          "CONNECT_REQ", "Called party number", *pp);
+       }
+       pp++;
+       l--;
+       /* translate "**" internal call prefix to CTP value */
+       if (l >= 2 && pp[0] == '*' && pp[1] == '*') {
+               s = "^SCTP=0\r";
+               pp += 2;
+               l -= 2;
+       } else {
+               s = "^SCTP=1\r";
+       }
+       commands[AT_TYPE] = kstrdup(s, GFP_KERNEL);
+       if (!commands[AT_TYPE])
+               goto oom;
+       commands[AT_DIAL] = kmalloc(l + 3, GFP_KERNEL);
+       if (!commands[AT_DIAL])
+               goto oom;
+       snprintf(commands[AT_DIAL], l + 3, "D%.*s\r", l, pp);
+
+       /* encode parameter: Calling party number */
+       pp = cmsg->CallingPartyNumber;
+       if (pp != NULL && *pp > 0) {
+               l = *pp++;
+
+               /* check type of number/numbering plan byte */
+               /* ToDo: allow for/handle Ext=1? */
+               switch (*pp) {
+               case 0x00:      /* unknown type / unknown numbering plan */
+               case 0x01:      /* unknown type / ISDN/Telephony num. plan */
+                       break;
+               default:
+                       dev_notice(cs->dev,
+                                  "%s: %s type/plan 0x%02x unsupported\n",
+                                  "CONNECT_REQ", "Calling party number", *pp);
+               }
+               pp++;
+               l--;
+
+               /* check presentation indicator */
+               if (!l) {
+                       dev_notice(cs->dev, "%s: %s IE truncated\n",
+                                  "CONNECT_REQ", "Calling party number");
+                       info = CapiIllMessageParmCoding;
+                       goto error;
+               }
+               switch (*pp & 0xfc) { /* ignore Screening indicator */
+               case 0x80:      /* Presentation allowed */
+                       s = "^SCLIP=1\r";
+                       break;
+               case 0xa0:      /* Presentation restricted */
+                       s = "^SCLIP=0\r";
+                       break;
+               default:
+                       dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+                                  "CONNECT_REQ",
+                                  "Presentation/Screening indicator",
+                                  *pp);
+                       s = "^SCLIP=1\r";
+               }
+               commands[AT_CLIP] = kstrdup(s, GFP_KERNEL);
+               if (!commands[AT_CLIP])
+                       goto oom;
+               pp++;
+               l--;
+
+               if (l) {
+                       /* number */
+                       commands[AT_MSN] = kmalloc(l + 8, GFP_KERNEL);
+                       if (!commands[AT_MSN])
+                               goto oom;
+                       snprintf(commands[AT_MSN], l + 8, "^SMSN=%*s\r", l, pp);
+               }
+       }
+
+       /* check parameter: CIP Value */
+       if (cmsg->CIPValue >= ARRAY_SIZE(cip2bchlc) ||
+           (cmsg->CIPValue > 0 && cip2bchlc[cmsg->CIPValue].bc == NULL)) {
+               dev_notice(cs->dev, "%s: unknown CIP value %d\n",
+                          "CONNECT_REQ", cmsg->CIPValue);
+               info = CapiCipValueUnknown;
+               goto error;
+       }
+
+       /*
+        * check/encode parameters: BC & HLC
+        * must be encoded together as device doesn't accept HLC separately
+        * explicit parameters override values derived from CIP
+        */
+
+       /* determine lengths */
+       if (cmsg->BC && cmsg->BC[0])            /* BC specified explicitly */
+               lbc = 2 * cmsg->BC[0];
+       else if (cip2bchlc[cmsg->CIPValue].bc)  /* BC derived from CIP */
+               lbc = strlen(cip2bchlc[cmsg->CIPValue].bc);
+       else                                    /* no BC */
+               lbc = 0;
+       if (cmsg->HLC && cmsg->HLC[0])          /* HLC specified explicitly */
+               lhlc = 2 * cmsg->HLC[0];
+       else if (cip2bchlc[cmsg->CIPValue].hlc) /* HLC derived from CIP */
+               lhlc = strlen(cip2bchlc[cmsg->CIPValue].hlc);
+       else                                    /* no HLC */
+               lhlc = 0;
+
+       if (lbc) {
+               /* have BC: allocate and assemble command string */
+               l = lbc + 7;            /* "^SBC=" + value + "\r" + null byte */
+               if (lhlc)
+                       l += lhlc + 7;  /* ";^SHLC=" + value */
+               commands[AT_BC] = kmalloc(l, GFP_KERNEL);
+               if (!commands[AT_BC])
+                       goto oom;
+               strcpy(commands[AT_BC], "^SBC=");
+               if (cmsg->BC && cmsg->BC[0])    /* BC specified explicitly */
+                       decode_ie(cmsg->BC, commands[AT_BC] + 5);
+               else                            /* BC derived from CIP */
+                       strcpy(commands[AT_BC] + 5,
+                              cip2bchlc[cmsg->CIPValue].bc);
+               if (lhlc) {
+                       strcpy(commands[AT_BC] + lbc + 5, ";^SHLC=");
+                       if (cmsg->HLC && cmsg->HLC[0])
+                               /* HLC specified explicitly */
+                               decode_ie(cmsg->HLC,
+                                         commands[AT_BC] + lbc + 12);
+                       else    /* HLC derived from CIP */
+                               strcpy(commands[AT_BC] + lbc + 12,
+                                      cip2bchlc[cmsg->CIPValue].hlc);
+               }
+               strcpy(commands[AT_BC] + l - 2, "\r");
+       } else {
+               /* no BC */
+               if (lhlc) {
+                       dev_notice(cs->dev, "%s: cannot set HLC without BC\n",
+                                  "CONNECT_REQ");
+                       info = CapiIllMessageParmCoding; /* ? */
+                       goto error;
+               }
+       }
+
+       /* check/encode parameter: B Protocol */
+       if (cmsg->BProtocol == CAPI_DEFAULT) {
+               bcs->proto2 = L2_HDLC;
+               dev_warn(cs->dev,
+                        "B2 Protocol X.75 SLP unsupported, using Transparent\n");
+       } else {
+               switch (cmsg->B1protocol) {
+               case 0:
+                       bcs->proto2 = L2_HDLC;
+                       break;
+               case 1:
+                       bcs->proto2 = L2_VOICE;
+                       break;
+               default:
+                       dev_warn(cs->dev,
+                                "B1 Protocol %u unsupported, using Transparent\n",
+                                cmsg->B1protocol);
+                       bcs->proto2 = L2_VOICE;
+               }
+               if (cmsg->B2protocol != 1)
+                       dev_warn(cs->dev,
+                                "B2 Protocol %u unsupported, using Transparent\n",
+                                cmsg->B2protocol);
+               if (cmsg->B3protocol != 0)
+                       dev_warn(cs->dev,
+                                "B3 Protocol %u unsupported, using Transparent\n",
+                                cmsg->B3protocol);
+               ignore_cstruct_param(cs, cmsg->B1configuration,
+                                    "CONNECT_REQ", "B1 Configuration");
+               ignore_cstruct_param(cs, cmsg->B2configuration,
+                                    "CONNECT_REQ", "B2 Configuration");
+               ignore_cstruct_param(cs, cmsg->B3configuration,
+                                    "CONNECT_REQ", "B3 Configuration");
+       }
+       commands[AT_PROTO] = kmalloc(9, GFP_KERNEL);
+       if (!commands[AT_PROTO])
+               goto oom;
+       snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+
+       /* ToDo: check/encode remaining parameters */
+       ignore_cstruct_param(cs, cmsg->CalledPartySubaddress,
+                            "CONNECT_REQ", "Called pty subaddr");
+       ignore_cstruct_param(cs, cmsg->CallingPartySubaddress,
+                            "CONNECT_REQ", "Calling pty subaddr");
+       ignore_cstruct_param(cs, cmsg->LLC,
+                            "CONNECT_REQ", "LLC");
+       if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
+               ignore_cstruct_param(cs, cmsg->BChannelinformation,
+                                    "CONNECT_REQ", "B Channel Information");
+               ignore_cstruct_param(cs, cmsg->Keypadfacility,
+                                    "CONNECT_REQ", "Keypad Facility");
+               ignore_cstruct_param(cs, cmsg->Useruserdata,
+                                    "CONNECT_REQ", "User-User Data");
+               ignore_cstruct_param(cs, cmsg->Facilitydataarray,
+                                    "CONNECT_REQ", "Facility Data Array");
+       }
+
+       /* encode parameter: B channel to use */
+       commands[AT_ISO] = kmalloc(9, GFP_KERNEL);
+       if (!commands[AT_ISO])
+               goto oom;
+       snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
+                (unsigned) bcs->channel + 1);
+
+       /* queue & schedule EV_DIAL event */
+       if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
+                              bcs->at_state.seq_index, NULL)) {
+               info = CAPI_MSGOSRESOURCEERR;
+               goto error;
+       }
+       gigaset_schedule_event(cs);
+       send_conf(iif, ap, skb, CapiSuccess);
+       return;
+
+oom:
+       dev_err(cs->dev, "%s: out of memory\n", __func__);
+       info = CAPI_MSGOSRESOURCEERR;
+error:
+       if (commands)
+               for (i = 0; i < AT_NUM; i++)
+                       kfree(commands[i]);
+       kfree(commands);
+       gigaset_free_channel(bcs);
+       send_conf(iif, ap, skb, info);
+}
+
+/*
+ * process CONNECT_RESP message
+ * checks protocol parameters and queues an ACCEPT or HUP event
+ */
+static void do_connect_resp(struct gigaset_capi_ctr *iif,
+                           struct gigaset_capi_appl *ap,
+                           struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
+       struct gigaset_capi_appl *oap;
+       unsigned long flags;
+       int channel;
+
+       /* decode message */
+       if (capi_message2cmsg(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+       dev_kfree_skb_any(skb);
+
+       /* extract and check channel number from PLCI */
+       channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
+       if (!channel || channel > cs->channels) {
+               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+                          "CONNECT_RESP", "PLCI", cmsg->adr.adrPLCI);
+               return;
+       }
+       bcs = cs->bcs + channel - 1;
+
+       switch (cmsg->Reject) {
+       case 0:         /* Accept */
+               /* drop all competing applications, keep only this one */
+               spin_lock_irqsave(&bcs->aplock, flags);
+               while (bcs->ap != NULL) {
+                       oap = bcs->ap;
+                       bcs->ap = oap->bcnext;
+                       if (oap != ap) {
+                               spin_unlock_irqrestore(&bcs->aplock, flags);
+                               send_disconnect_ind(bcs, oap,
+                                                   CapiCallGivenToOtherApplication);
+                               spin_lock_irqsave(&bcs->aplock, flags);
+                       }
+               }
+               ap->bcnext = NULL;
+               bcs->ap = ap;
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+
+               bcs->rx_bufsize = ap->rp.datablklen;
+               dev_kfree_skb(bcs->rx_skb);
+               gigaset_new_rx_skb(bcs);
+               bcs->chstate |= CHS_NOTIFY_LL;
+
+               /* check/encode B channel protocol */
+               if (cmsg->BProtocol == CAPI_DEFAULT) {
+                       bcs->proto2 = L2_HDLC;
+                       dev_warn(cs->dev,
+                                "B2 Protocol X.75 SLP unsupported, using Transparent\n");
+               } else {
+                       switch (cmsg->B1protocol) {
+                       case 0:
+                               bcs->proto2 = L2_HDLC;
+                               break;
+                       case 1:
+                               bcs->proto2 = L2_VOICE;
+                               break;
+                       default:
+                               dev_warn(cs->dev,
+                                        "B1 Protocol %u unsupported, using Transparent\n",
+                                        cmsg->B1protocol);
+                               bcs->proto2 = L2_VOICE;
+                       }
+                       if (cmsg->B2protocol != 1)
+                               dev_warn(cs->dev,
+                                        "B2 Protocol %u unsupported, using Transparent\n",
+                                        cmsg->B2protocol);
+                       if (cmsg->B3protocol != 0)
+                               dev_warn(cs->dev,
+                                        "B3 Protocol %u unsupported, using Transparent\n",
+                                        cmsg->B3protocol);
+                       ignore_cstruct_param(cs, cmsg->B1configuration,
+                                            "CONNECT_RESP", "B1 Configuration");
+                       ignore_cstruct_param(cs, cmsg->B2configuration,
+                                            "CONNECT_RESP", "B2 Configuration");
+                       ignore_cstruct_param(cs, cmsg->B3configuration,
+                                            "CONNECT_RESP", "B3 Configuration");
+               }
+
+               /* ToDo: check/encode remaining parameters */
+               ignore_cstruct_param(cs, cmsg->ConnectedNumber,
+                                    "CONNECT_RESP", "Connected Number");
+               ignore_cstruct_param(cs, cmsg->ConnectedSubaddress,
+                                    "CONNECT_RESP", "Connected Subaddress");
+               ignore_cstruct_param(cs, cmsg->LLC,
+                                    "CONNECT_RESP", "LLC");
+               if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
+                       ignore_cstruct_param(cs, cmsg->BChannelinformation,
+                                            "CONNECT_RESP", "BChannel Information");
+                       ignore_cstruct_param(cs, cmsg->Keypadfacility,
+                                            "CONNECT_RESP", "Keypad Facility");
+                       ignore_cstruct_param(cs, cmsg->Useruserdata,
+                                            "CONNECT_RESP", "User-User Data");
+                       ignore_cstruct_param(cs, cmsg->Facilitydataarray,
+                                            "CONNECT_RESP", "Facility Data Array");
+               }
+
+               /* Accept call */
+               if (!gigaset_add_event(cs, &cs->bcs[channel - 1].at_state,
+                                      EV_ACCEPT, NULL, 0, NULL))
+                       return;
+               gigaset_schedule_event(cs);
+               return;
+
+       case 1:                 /* Ignore */
+               /* send DISCONNECT_IND to this application */
+               send_disconnect_ind(bcs, ap, 0);
+
+               /* remove it from the list of listening apps */
+               spin_lock_irqsave(&bcs->aplock, flags);
+               if (bcs->ap == ap) {
+                       bcs->ap = ap->bcnext;
+                       if (bcs->ap == NULL) {
+                               /* last one: stop ev-layer hupD notifications */
+                               bcs->apconnstate = APCONN_NONE;
+                               bcs->chstate &= ~CHS_NOTIFY_LL;
+                       }
+                       spin_unlock_irqrestore(&bcs->aplock, flags);
+                       return;
+               }
+               for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) {
+                       if (oap->bcnext == ap) {
+                               oap->bcnext = oap->bcnext->bcnext;
+                               spin_unlock_irqrestore(&bcs->aplock, flags);
+                               return;
+                       }
+               }
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+               dev_err(cs->dev, "%s: application %u not found\n",
+                       __func__, ap->id);
+               return;
+
+       default:                /* Reject */
+               /* drop all competing applications, keep only this one */
+               spin_lock_irqsave(&bcs->aplock, flags);
+               while (bcs->ap != NULL) {
+                       oap = bcs->ap;
+                       bcs->ap = oap->bcnext;
+                       if (oap != ap) {
+                               spin_unlock_irqrestore(&bcs->aplock, flags);
+                               send_disconnect_ind(bcs, oap,
+                                                   CapiCallGivenToOtherApplication);
+                               spin_lock_irqsave(&bcs->aplock, flags);
+                       }
+               }
+               ap->bcnext = NULL;
+               bcs->ap = ap;
+               spin_unlock_irqrestore(&bcs->aplock, flags);
+
+               /* reject call - will trigger DISCONNECT_IND for this app */
+               dev_info(cs->dev, "%s: Reject=%x\n",
+                        "CONNECT_RESP", cmsg->Reject);
+               if (!gigaset_add_event(cs, &cs->bcs[channel - 1].at_state,
+                                      EV_HUP, NULL, 0, NULL))
+                       return;
+               gigaset_schedule_event(cs);
+               return;
+       }
+}
+
+/*
+ * process CONNECT_B3_REQ message
+ * build NCCI and emit CONNECT_B3_CONF reply
+ */
+static void do_connect_b3_req(struct gigaset_capi_ctr *iif,
+                             struct gigaset_capi_appl *ap,
+                             struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
+       int channel;
+
+       /* decode message */
+       if (capi_message2cmsg(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+       /* extract and check channel number from PLCI */
+       channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
+       if (!channel || channel > cs->channels) {
+               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+                          "CONNECT_B3_REQ", "PLCI", cmsg->adr.adrPLCI);
+               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+               return;
+       }
+       bcs = &cs->bcs[channel - 1];
+
+       /* mark logical connection active */
+       bcs->apconnstate = APCONN_ACTIVE;
+
+       /* build NCCI: always 1 (one B3 connection only) */
+       cmsg->adr.adrNCCI |= 1 << 16;
+
+       /* NCPI parameter: not applicable for B3 Transparent */
+       ignore_cstruct_param(cs, cmsg->NCPI, "CONNECT_B3_REQ", "NCPI");
+       send_conf(iif, ap, skb,
+                 (cmsg->NCPI && cmsg->NCPI[0]) ?
+                 CapiNcpiNotSupportedByProtocol : CapiSuccess);
+}
+
+/*
+ * process CONNECT_B3_RESP message
+ * Depending on the Reject parameter, either emit CONNECT_B3_ACTIVE_IND
+ * or queue EV_HUP and emit DISCONNECT_B3_IND.
+ * The emitted message is always shorter than the received one,
+ * allowing to reuse the skb.
+ */
+static void do_connect_b3_resp(struct gigaset_capi_ctr *iif,
+                              struct gigaset_capi_appl *ap,
+                              struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
+       int channel;
+       unsigned int msgsize;
+       u8 command;
+
+       /* decode message */
+       if (capi_message2cmsg(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+       /* extract and check channel number and NCCI */
+       channel = (cmsg->adr.adrNCCI >> 8) & 0xff;
+       if (!channel || channel > cs->channels ||
+           ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) {
+               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+                          "CONNECT_B3_RESP", "NCCI", cmsg->adr.adrNCCI);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       bcs = &cs->bcs[channel - 1];
+
+       if (cmsg->Reject) {
+               /* Reject: clear B3 connect received flag */
+               bcs->apconnstate = APCONN_SETUP;
+
+               /* trigger hangup, causing eventual DISCONNECT_IND */
+               if (!gigaset_add_event(cs, &bcs->at_state,
+                                      EV_HUP, NULL, 0, NULL)) {
+                       dev_kfree_skb_any(skb);
+                       return;
+               }
+               gigaset_schedule_event(cs);
+
+               /* emit DISCONNECT_B3_IND */
+               command = CAPI_DISCONNECT_B3;
+               msgsize = CAPI_DISCONNECT_B3_IND_BASELEN;
+       } else {
+               /*
+                * Accept: emit CONNECT_B3_ACTIVE_IND immediately, as
+                * we only send CONNECT_B3_IND if the B channel is up
+                */
+               command = CAPI_CONNECT_B3_ACTIVE;
+               msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
+       }
+       capi_cmsg_header(cmsg, ap->id, command, CAPI_IND,
+                        ap->nextMessageNumber++, cmsg->adr.adrNCCI);
+       __skb_trim(skb, msgsize);
+       if (capi_cmsg2message(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+       capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/*
+ * process DISCONNECT_REQ message
+ * schedule EV_HUP and emit DISCONNECT_B3_IND if necessary,
+ * emit DISCONNECT_CONF reply
+ */
+static void do_disconnect_req(struct gigaset_capi_ctr *iif,
+                             struct gigaset_capi_appl *ap,
+                             struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
+       _cmsg *b3cmsg;
+       struct sk_buff *b3skb;
+       int channel;
+
+       /* decode message */
+       if (capi_message2cmsg(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+       /* extract and check channel number from PLCI */
+       channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
+       if (!channel || channel > cs->channels) {
+               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+                          "DISCONNECT_REQ", "PLCI", cmsg->adr.adrPLCI);
+               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+               return;
+       }
+       bcs = cs->bcs + channel - 1;
+
+       /* ToDo: process parameter: Additional info */
+       if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
+               ignore_cstruct_param(cs, cmsg->BChannelinformation,
+                                    "DISCONNECT_REQ", "B Channel Information");
+               ignore_cstruct_param(cs, cmsg->Keypadfacility,
+                                    "DISCONNECT_REQ", "Keypad Facility");
+               ignore_cstruct_param(cs, cmsg->Useruserdata,
+                                    "DISCONNECT_REQ", "User-User Data");
+               ignore_cstruct_param(cs, cmsg->Facilitydataarray,
+                                    "DISCONNECT_REQ", "Facility Data Array");
+       }
+
+       /* skip if DISCONNECT_IND already sent */
+       if (!bcs->apconnstate)
+               return;
+
+       /* check for active logical connection */
+       if (bcs->apconnstate >= APCONN_ACTIVE) {
+               /* clear it */
+               bcs->apconnstate = APCONN_SETUP;
+
+               /*
+                * emit DISCONNECT_B3_IND with cause 0x3301
+                * use separate cmsg structure, as the content of iif->acmsg
+                * is still needed for creating the _CONF message
+                */
+               b3cmsg = kmalloc(sizeof(*b3cmsg), GFP_KERNEL);
+               if (!b3cmsg) {
+                       dev_err(cs->dev, "%s: out of memory\n", __func__);
+                       send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+                       return;
+               }
+               capi_cmsg_header(b3cmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
+                                ap->nextMessageNumber++,
+                                cmsg->adr.adrPLCI | (1 << 16));
+               b3cmsg->Reason_B3 = CapiProtocolErrorLayer1;
+               b3skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_KERNEL);
+               if (b3skb == NULL) {
+                       dev_err(cs->dev, "%s: out of memory\n", __func__);
+                       send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+                       kfree(b3cmsg);
+                       return;
+               }
+               if (capi_cmsg2message(b3cmsg,
+                                     __skb_put(b3skb, CAPI_DISCONNECT_B3_IND_BASELEN))) {
+                       dev_err(cs->dev, "%s: message parser failure\n",
+                               __func__);
+                       kfree(b3cmsg);
+                       dev_kfree_skb_any(b3skb);
+                       return;
+               }
+               dump_cmsg(DEBUG_CMD, __func__, b3cmsg);
+               kfree(b3cmsg);
+               capi_ctr_handle_message(&iif->ctr, ap->id, b3skb);
+       }
+
+       /* trigger hangup, causing eventual DISCONNECT_IND */
+       if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
+               send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+               return;
+       }
+       gigaset_schedule_event(cs);
+
+       /* emit reply */
+       send_conf(iif, ap, skb, CapiSuccess);
+}
+
+/*
+ * process DISCONNECT_B3_REQ message
+ * schedule EV_HUP and emit DISCONNECT_B3_CONF reply
+ */
+static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
+                                struct gigaset_capi_appl *ap,
+                                struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       _cmsg *cmsg = &iif->acmsg;
+       struct bc_state *bcs;
+       int channel;
+
+       /* decode message */
+       if (capi_message2cmsg(cmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+       /* extract and check channel number and NCCI */
+       channel = (cmsg->adr.adrNCCI >> 8) & 0xff;
+       if (!channel || channel > cs->channels ||
+           ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) {
+               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+                          "DISCONNECT_B3_REQ", "NCCI", cmsg->adr.adrNCCI);
+               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+               return;
+       }
+       bcs = &cs->bcs[channel - 1];
+
+       /* reject if logical connection not active */
+       if (bcs->apconnstate < APCONN_ACTIVE) {
+               send_conf(iif, ap, skb,
+                         CapiMessageNotSupportedInCurrentState);
+               return;
+       }
+
+       /* trigger hangup, causing eventual DISCONNECT_B3_IND */
+       if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
+               send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+               return;
+       }
+       gigaset_schedule_event(cs);
+
+       /* NCPI parameter: not applicable for B3 Transparent */
+       ignore_cstruct_param(cs, cmsg->NCPI,
+                            "DISCONNECT_B3_REQ", "NCPI");
+       send_conf(iif, ap, skb,
+                 (cmsg->NCPI && cmsg->NCPI[0]) ?
+                 CapiNcpiNotSupportedByProtocol : CapiSuccess);
+}
+
+/*
+ * process DATA_B3_REQ message
+ */
+static void do_data_b3_req(struct gigaset_capi_ctr *iif,
+                          struct gigaset_capi_appl *ap,
+                          struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+       struct bc_state *bcs;
+       int channel = CAPIMSG_PLCI_PART(skb->data);
+       u16 ncci = CAPIMSG_NCCI_PART(skb->data);
+       u16 msglen = CAPIMSG_LEN(skb->data);
+       u16 datalen = CAPIMSG_DATALEN(skb->data);
+       u16 flags = CAPIMSG_FLAGS(skb->data);
+       u16 msgid = CAPIMSG_MSGID(skb->data);
+       u16 handle = CAPIMSG_HANDLE_REQ(skb->data);
+
+       /* frequent message, avoid _cmsg overhead */
+       dump_rawmsg(DEBUG_MCMD, __func__, skb->data);
+
+       /* check parameters */
+       if (channel == 0 || channel > cs->channels || ncci != 1) {
+               dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+                          "DATA_B3_REQ", "NCCI", CAPIMSG_NCCI(skb->data));
+               send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+               return;
+       }
+       bcs = &cs->bcs[channel - 1];
+       if (msglen != CAPI_DATA_B3_REQ_LEN && msglen != CAPI_DATA_B3_REQ_LEN64)
+               dev_notice(cs->dev, "%s: unexpected length %d\n",
+                          "DATA_B3_REQ", msglen);
+       if (msglen + datalen != skb->len)
+               dev_notice(cs->dev, "%s: length mismatch (%d+%d!=%d)\n",
+                          "DATA_B3_REQ", msglen, datalen, skb->len);
+       if (msglen + datalen > skb->len) {
+               /* message too short for announced data length */
+               send_conf(iif, ap, skb, CapiIllMessageParmCoding); /* ? */
+               return;
+       }
+       if (flags & CAPI_FLAGS_RESERVED) {
+               dev_notice(cs->dev, "%s: reserved flags set (%x)\n",
+                          "DATA_B3_REQ", flags);
+               send_conf(iif, ap, skb, CapiIllMessageParmCoding);
+               return;
+       }
+
+       /* reject if logical connection not active */
+       if (bcs->apconnstate < APCONN_ACTIVE) {
+               send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
+               return;
+       }
+
+       /* pull CAPI message into link layer header */
+       skb_reset_mac_header(skb);
+       skb->mac_len = msglen;
+       skb_pull(skb, msglen);
+
+       /* pass to device-specific module */
+       if (cs->ops->send_skb(bcs, skb) < 0) {
+               send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+               return;
+       }
+
+       /*
+        * DATA_B3_CONF will be sent by gigaset_skb_sent() only if "delivery
+        * confirmation" bit is set; otherwise we have to send it now
+        */
+       if (!(flags & CAPI_FLAGS_DELIVERY_CONFIRMATION))
+               send_data_b3_conf(cs, &iif->ctr, ap->id, msgid, channel, handle,
+                                 flags ? CapiFlagsNotSupportedByProtocol
+                                 : CAPI_NOERROR);
+}
+
+/*
+ * process RESET_B3_REQ message
+ * just always reply "not supported by current protocol"
+ */
+static void do_reset_b3_req(struct gigaset_capi_ctr *iif,
+                           struct gigaset_capi_appl *ap,
+                           struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+
+       /* decode message */
+       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+       send_conf(iif, ap, skb,
+                 CapiResetProcedureNotSupportedByCurrentProtocol);
+}
+
+/*
+ * unsupported CAPI message handler
+ */
+static void do_unsupported(struct gigaset_capi_ctr *iif,
+                          struct gigaset_capi_appl *ap,
+                          struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+
+       /* decode message */
+       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+       send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
+}
+
+/*
+ * CAPI message handler: no-op
+ */
+static void do_nothing(struct gigaset_capi_ctr *iif,
+                      struct gigaset_capi_appl *ap,
+                      struct sk_buff *skb)
+{
+       struct cardstate *cs = iif->ctr.driverdata;
+
+       /* decode message */
+       if (capi_message2cmsg(&iif->acmsg, skb->data)) {
+               dev_err(cs->dev, "%s: message parser failure\n", __func__);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+       dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+       dev_kfree_skb_any(skb);
+}
+
+static void do_data_b3_resp(struct gigaset_capi_ctr *iif,
+                           struct gigaset_capi_appl *ap,
+                           struct sk_buff *skb)
+{
+       dump_rawmsg(DEBUG_MCMD, __func__, skb->data);
+       dev_kfree_skb_any(skb);
+}
+
+/* table of outgoing CAPI message handlers with lookup function */
+typedef void (*capi_send_handler_t)(struct gigaset_capi_ctr *,
+                                   struct gigaset_capi_appl *,
+                                   struct sk_buff *);
+
+static struct {
+       u16 cmd;
+       capi_send_handler_t handler;
+} capi_send_handler_table[] = {
+       /* most frequent messages first for faster lookup */
+       { CAPI_DATA_B3_REQ, do_data_b3_req },
+       { CAPI_DATA_B3_RESP, do_data_b3_resp },
+
+       { CAPI_ALERT_REQ, do_alert_req },
+       { CAPI_CONNECT_ACTIVE_RESP, do_nothing },
+       { CAPI_CONNECT_B3_ACTIVE_RESP, do_nothing },
+       { CAPI_CONNECT_B3_REQ, do_connect_b3_req },
+       { CAPI_CONNECT_B3_RESP, do_connect_b3_resp },
+       { CAPI_CONNECT_B3_T90_ACTIVE_RESP, do_nothing },
+       { CAPI_CONNECT_REQ, do_connect_req },
+       { CAPI_CONNECT_RESP, do_connect_resp },
+       { CAPI_DISCONNECT_B3_REQ, do_disconnect_b3_req },
+       { CAPI_DISCONNECT_B3_RESP, do_nothing },
+       { CAPI_DISCONNECT_REQ, do_disconnect_req },
+       { CAPI_DISCONNECT_RESP, do_nothing },
+       { CAPI_FACILITY_REQ, do_facility_req },
+       { CAPI_FACILITY_RESP, do_nothing },
+       { CAPI_LISTEN_REQ, do_listen_req },
+       { CAPI_SELECT_B_PROTOCOL_REQ, do_unsupported },
+       { CAPI_RESET_B3_REQ, do_reset_b3_req },
+       { CAPI_RESET_B3_RESP, do_nothing },
+
+       /*
+        * ToDo: support overlap sending (requires ev-layer state
+        * machine extension to generate additional ATD commands)
+        */
+       { CAPI_INFO_REQ, do_unsupported },
+       { CAPI_INFO_RESP, do_nothing },
+
+       /*
+        * ToDo: what's the proper response for these?
+        */
+       { CAPI_MANUFACTURER_REQ, do_nothing },
+       { CAPI_MANUFACTURER_RESP, do_nothing },
+};
+
+/* look up handler */
+static inline capi_send_handler_t lookup_capi_send_handler(const u16 cmd)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(capi_send_handler_table); i++)
+               if (capi_send_handler_table[i].cmd == cmd)
+                       return capi_send_handler_table[i].handler;
+       return NULL;
+}
+
+
+/**
+ * gigaset_send_message() - accept a CAPI message from an application
+ * @ctr:       controller descriptor structure.
+ * @skb:       CAPI message.
+ *
+ * Return value: CAPI error code
+ * Note: capidrv (and probably others, too) only uses the return value to
+ * decide whether it has to free the skb (only if result != CAPI_NOERROR (0))
+ */
+static u16 gigaset_send_message(struct capi_ctr *ctr, struct sk_buff *skb)
+{
+       struct gigaset_capi_ctr *iif
+               = container_of(ctr, struct gigaset_capi_ctr, ctr);
+       struct cardstate *cs = ctr->driverdata;
+       struct gigaset_capi_appl *ap;
+       capi_send_handler_t handler;
+
+       /* can only handle linear sk_buffs */
+       if (skb_linearize(skb) < 0) {
+               dev_warn(cs->dev, "%s: skb_linearize failed\n", __func__);
+               return CAPI_MSGOSRESOURCEERR;
+       }
+
+       /* retrieve application data structure */
+       ap = get_appl(iif, CAPIMSG_APPID(skb->data));
+       if (!ap) {
+               dev_notice(cs->dev, "%s: application %u not registered\n",
+                          __func__, CAPIMSG_APPID(skb->data));
+               return CAPI_ILLAPPNR;
+       }
+
+       /* look up command */
+       handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data));
+       if (!handler) {
+               /* unknown/unsupported message type */
+               if (printk_ratelimit())
+                       dev_notice(cs->dev, "%s: unsupported message %u\n",
+                                  __func__, CAPIMSG_CMD(skb->data));
+               return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+       }
+
+       /* serialize */
+       if (atomic_add_return(1, &iif->sendqlen) > 1) {
+               /* queue behind other messages */
+               skb_queue_tail(&iif->sendqueue, skb);
+               return CAPI_NOERROR;
+       }
+
+       /* process message */
+       handler(iif, ap, skb);
+
+       /* process other messages arrived in the meantime */
+       while (atomic_sub_return(1, &iif->sendqlen) > 0) {
+               skb = skb_dequeue(&iif->sendqueue);
+               if (!skb) {
+                       /* should never happen */
+                       dev_err(cs->dev, "%s: send queue empty\n", __func__);
+                       continue;
+               }
+               ap = get_appl(iif, CAPIMSG_APPID(skb->data));
+               if (!ap) {
+                       /* could that happen? */
+                       dev_warn(cs->dev, "%s: application %u vanished\n",
+                                __func__, CAPIMSG_APPID(skb->data));
+                       continue;
+               }
+               handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data));
+               if (!handler) {
+                       /* should never happen */
+                       dev_err(cs->dev, "%s: handler %x vanished\n",
+                               __func__, CAPIMSG_CMD(skb->data));
+                       continue;
+               }
+               handler(iif, ap, skb);
+       }
+
+       return CAPI_NOERROR;
+}
+
+/**
+ * gigaset_procinfo() - build single line description for controller
+ * @ctr:       controller descriptor structure.
+ *
+ * Return value: pointer to generated string (null terminated)
+ */
+static char *gigaset_procinfo(struct capi_ctr *ctr)
+{
+       return ctr->name;       /* ToDo: more? */
+}
+
+static int gigaset_proc_show(struct seq_file *m, void *v)
+{
+       struct capi_ctr *ctr = m->private;
+       struct cardstate *cs = ctr->driverdata;
+       char *s;
+       int i;
+
+       seq_printf(m, "%-16s %s\n", "name", ctr->name);
+       seq_printf(m, "%-16s %s %s\n", "dev",
+                  dev_driver_string(cs->dev), dev_name(cs->dev));
+       seq_printf(m, "%-16s %d\n", "id", cs->myid);
+       if (cs->gotfwver)
+               seq_printf(m, "%-16s %d.%d.%d.%d\n", "firmware",
+                          cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]);
+       seq_printf(m, "%-16s %d\n", "channels", cs->channels);
+       seq_printf(m, "%-16s %s\n", "onechannel", cs->onechannel ? "yes" : "no");
+
+       switch (cs->mode) {
+       case M_UNKNOWN:
+               s = "unknown";
+               break;
+       case M_CONFIG:
+               s = "config";
+               break;
+       case M_UNIMODEM:
+               s = "Unimodem";
+               break;
+       case M_CID:
+               s = "CID";
+               break;
+       default:
+               s = "??";
+       }
+       seq_printf(m, "%-16s %s\n", "mode", s);
+
+       switch (cs->mstate) {
+       case MS_UNINITIALIZED:
+               s = "uninitialized";
+               break;
+       case MS_INIT:
+               s = "init";
+               break;
+       case MS_LOCKED:
+               s = "locked";
+               break;
+       case MS_SHUTDOWN:
+               s = "shutdown";
+               break;
+       case MS_RECOVER:
+               s = "recover";
+               break;
+       case MS_READY:
+               s = "ready";
+               break;
+       default:
+               s = "??";
+       }
+       seq_printf(m, "%-16s %s\n", "mstate", s);
+
+       seq_printf(m, "%-16s %s\n", "running", cs->running ? "yes" : "no");
+       seq_printf(m, "%-16s %s\n", "connected", cs->connected ? "yes" : "no");
+       seq_printf(m, "%-16s %s\n", "isdn_up", cs->isdn_up ? "yes" : "no");
+       seq_printf(m, "%-16s %s\n", "cidmode", cs->cidmode ? "yes" : "no");
+
+       for (i = 0; i < cs->channels; i++) {
+               seq_printf(m, "[%d]%-13s %d\n", i, "corrupted",
+                          cs->bcs[i].corrupted);
+               seq_printf(m, "[%d]%-13s %d\n", i, "trans_down",
+                          cs->bcs[i].trans_down);
+               seq_printf(m, "[%d]%-13s %d\n", i, "trans_up",
+                          cs->bcs[i].trans_up);
+               seq_printf(m, "[%d]%-13s %d\n", i, "chstate",
+                          cs->bcs[i].chstate);
+               switch (cs->bcs[i].proto2) {
+               case L2_BITSYNC:
+                       s = "bitsync";
+                       break;
+               case L2_HDLC:
+                       s = "HDLC";
+                       break;
+               case L2_VOICE:
+                       s = "voice";
+                       break;
+               default:
+                       s = "??";
+               }
+               seq_printf(m, "[%d]%-13s %s\n", i, "proto2", s);
+       }
+       return 0;
+}
+
+/**
+ * gigaset_isdn_regdev() - register device to LL
+ * @cs:                device descriptor structure.
+ * @isdnid:    device name.
+ *
+ * Return value: 0 on success, error code < 0 on failure
+ */
+int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
+{
+       struct gigaset_capi_ctr *iif;
+       int rc;
+
+       iif = kzalloc(sizeof(*iif), GFP_KERNEL);
+       if (!iif) {
+               pr_err("%s: out of memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       /* prepare controller structure */
+       iif->ctr.owner         = THIS_MODULE;
+       iif->ctr.driverdata    = cs;
+       strncpy(iif->ctr.name, isdnid, sizeof(iif->ctr.name) - 1);
+       iif->ctr.driver_name   = "gigaset";
+       iif->ctr.load_firmware = NULL;
+       iif->ctr.reset_ctr     = NULL;
+       iif->ctr.register_appl = gigaset_register_appl;
+       iif->ctr.release_appl  = gigaset_release_appl;
+       iif->ctr.send_message  = gigaset_send_message;
+       iif->ctr.procinfo      = gigaset_procinfo;
+       iif->ctr.proc_show     = gigaset_proc_show,
+       INIT_LIST_HEAD(&iif->appls);
+       skb_queue_head_init(&iif->sendqueue);
+       atomic_set(&iif->sendqlen, 0);
+
+       /* register controller with CAPI */
+       rc = attach_capi_ctr(&iif->ctr);
+       if (rc) {
+               pr_err("attach_capi_ctr failed (%d)\n", rc);
+               kfree(iif);
+               return rc;
+       }
+
+       cs->iif = iif;
+       cs->hw_hdr_len = CAPI_DATA_B3_REQ_LEN;
+       return 0;
+}
+
+/**
+ * gigaset_isdn_unregdev() - unregister device from LL
+ * @cs:                device descriptor structure.
+ */
+void gigaset_isdn_unregdev(struct cardstate *cs)
+{
+       struct gigaset_capi_ctr *iif = cs->iif;
+
+       detach_capi_ctr(&iif->ctr);
+       kfree(iif);
+       cs->iif = NULL;
+}
+
+static struct capi_driver capi_driver_gigaset = {
+       .name           = "gigaset",
+       .revision       = "1.0",
+};
+
+/**
+ * gigaset_isdn_regdrv() - register driver to LL
+ */
+void gigaset_isdn_regdrv(void)
+{
+       pr_info("Kernel CAPI interface\n");
+       register_capi_driver(&capi_driver_gigaset);
+}
+
+/**
+ * gigaset_isdn_unregdrv() - unregister driver from LL
+ */
+void gigaset_isdn_unregdrv(void)
+{
+       unregister_capi_driver(&capi_driver_gigaset);
+}
diff --git a/drivers/staging/isdn/gigaset/common.c b/drivers/staging/isdn/gigaset/common.c
new file mode 100644 (file)
index 0000000..76b5407
--- /dev/null
@@ -0,0 +1,1156 @@
+/*
+ * Stuff used by all variants of the driver
+ *
+ * Copyright (c) 2001 by Stefan Eilers,
+ *                       Hansjoerg Lipp <hjlipp@web.de>,
+ *                       Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ *     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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers"
+#define DRIVER_DESC "Driver for Gigaset 307x"
+
+#ifdef CONFIG_GIGASET_DEBUG
+#define DRIVER_DESC_DEBUG " (debug build)"
+#else
+#define DRIVER_DESC_DEBUG ""
+#endif
+
+/* Module parameters */
+int gigaset_debuglevel;
+EXPORT_SYMBOL_GPL(gigaset_debuglevel);
+module_param_named(debug, gigaset_debuglevel, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "debug level");
+
+/* driver state flags */
+#define VALID_MINOR    0x01
+#define VALID_ID       0x02
+
+/**
+ * gigaset_dbg_buffer() - dump data in ASCII and hex for debugging
+ * @level:     debugging level.
+ * @msg:       message prefix.
+ * @len:       number of bytes to dump.
+ * @buf:       data to dump.
+ *
+ * If the current debugging level includes one of the bits set in @level,
+ * @len bytes starting at @buf are logged to dmesg at KERN_DEBUG prio,
+ * prefixed by the text @msg.
+ */
+void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
+                       size_t len, const unsigned char *buf)
+{
+       unsigned char outbuf[80];
+       unsigned char c;
+       size_t space = sizeof outbuf - 1;
+       unsigned char *out = outbuf;
+       size_t numin = len;
+
+       while (numin--) {
+               c = *buf++;
+               if (c == '~' || c == '^' || c == '\\') {
+                       if (!space--)
+                               break;
+                       *out++ = '\\';
+               }
+               if (c & 0x80) {
+                       if (!space--)
+                               break;
+                       *out++ = '~';
+                       c ^= 0x80;
+               }
+               if (c < 0x20 || c == 0x7f) {
+                       if (!space--)
+                               break;
+                       *out++ = '^';
+                       c ^= 0x40;
+               }
+               if (!space--)
+                       break;
+               *out++ = c;
+       }
+       *out = 0;
+
+       gig_dbg(level, "%s (%u bytes): %s", msg, (unsigned) len, outbuf);
+}
+EXPORT_SYMBOL_GPL(gigaset_dbg_buffer);
+
+static int setflags(struct cardstate *cs, unsigned flags, unsigned delay)
+{
+       int r;
+
+       r = cs->ops->set_modem_ctrl(cs, cs->control_state, flags);
+       cs->control_state = flags;
+       if (r < 0)
+               return r;
+
+       if (delay) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(delay * HZ / 1000);
+       }
+
+       return 0;
+}
+
+int gigaset_enterconfigmode(struct cardstate *cs)
+{
+       int i, r;
+
+       cs->control_state = TIOCM_RTS;
+
+       r = setflags(cs, TIOCM_DTR, 200);
+       if (r < 0)
+               goto error;
+       r = setflags(cs, 0, 200);
+       if (r < 0)
+               goto error;
+       for (i = 0; i < 5; ++i) {
+               r = setflags(cs, TIOCM_RTS, 100);
+               if (r < 0)
+                       goto error;
+               r = setflags(cs, 0, 100);
+               if (r < 0)
+                       goto error;
+       }
+       r = setflags(cs, TIOCM_RTS | TIOCM_DTR, 800);
+       if (r < 0)
+               goto error;
+
+       return 0;
+
+error:
+       dev_err(cs->dev, "error %d on setuartbits\n", -r);
+       cs->control_state = TIOCM_RTS | TIOCM_DTR;
+       cs->ops->set_modem_ctrl(cs, 0, TIOCM_RTS | TIOCM_DTR);
+
+       return -1;
+}
+
+static int test_timeout(struct at_state_t *at_state)
+{
+       if (!at_state->timer_expires)
+               return 0;
+
+       if (--at_state->timer_expires) {
+               gig_dbg(DEBUG_MCMD, "decreased timer of %p to %lu",
+                       at_state, at_state->timer_expires);
+               return 0;
+       }
+
+       gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
+                         at_state->timer_index, NULL);
+       return 1;
+}
+
+static void timer_tick(struct timer_list *t)
+{
+       struct cardstate *cs = from_timer(cs, t, timer);
+       unsigned long flags;
+       unsigned channel;
+       struct at_state_t *at_state;
+       int timeout = 0;
+
+       spin_lock_irqsave(&cs->lock, flags);
+
+       for (channel = 0; channel < cs->channels; ++channel)
+               if (test_timeout(&cs->bcs[channel].at_state))
+                       timeout = 1;
+
+       if (test_timeout(&cs->at_state))
+               timeout = 1;
+
+       list_for_each_entry(at_state, &cs->temp_at_states, list)
+               if (test_timeout(at_state))
+                       timeout = 1;
+
+       if (cs->running) {
+               mod_timer(&cs->timer, jiffies + msecs_to_jiffies(GIG_TICK));
+               if (timeout) {
+                       gig_dbg(DEBUG_EVENT, "scheduling timeout");
+                       tasklet_schedule(&cs->event_tasklet);
+               }
+       }
+
+       spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+int gigaset_get_channel(struct bc_state *bcs)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&bcs->cs->lock, flags);
+       if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) {
+               gig_dbg(DEBUG_CHANNEL, "could not allocate channel %d",
+                       bcs->channel);
+               spin_unlock_irqrestore(&bcs->cs->lock, flags);
+               return -EBUSY;
+       }
+       ++bcs->use_count;
+       bcs->busy = 1;
+       gig_dbg(DEBUG_CHANNEL, "allocated channel %d", bcs->channel);
+       spin_unlock_irqrestore(&bcs->cs->lock, flags);
+       return 0;
+}
+
+struct bc_state *gigaset_get_free_channel(struct cardstate *cs)
+{
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (!try_module_get(cs->driver->owner)) {
+               gig_dbg(DEBUG_CHANNEL,
+                       "could not get module for allocating channel");
+               spin_unlock_irqrestore(&cs->lock, flags);
+               return NULL;
+       }
+       for (i = 0; i < cs->channels; ++i)
+               if (!cs->bcs[i].use_count) {
+                       ++cs->bcs[i].use_count;
+                       cs->bcs[i].busy = 1;
+                       spin_unlock_irqrestore(&cs->lock, flags);
+                       gig_dbg(DEBUG_CHANNEL, "allocated channel %d", i);
+                       return cs->bcs + i;
+               }
+       module_put(cs->driver->owner);
+       spin_unlock_irqrestore(&cs->lock, flags);
+       gig_dbg(DEBUG_CHANNEL, "no free channel");
+       return NULL;
+}
+
+void gigaset_free_channel(struct bc_state *bcs)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&bcs->cs->lock, flags);
+       if (!bcs->busy) {
+               gig_dbg(DEBUG_CHANNEL, "could not free channel %d",
+                       bcs->channel);
+               spin_unlock_irqrestore(&bcs->cs->lock, flags);
+               return;
+       }
+       --bcs->use_count;
+       bcs->busy = 0;
+       module_put(bcs->cs->driver->owner);
+       gig_dbg(DEBUG_CHANNEL, "freed channel %d", bcs->channel);
+       spin_unlock_irqrestore(&bcs->cs->lock, flags);
+}
+
+int gigaset_get_channels(struct cardstate *cs)
+{
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       for (i = 0; i < cs->channels; ++i)
+               if (cs->bcs[i].use_count) {
+                       spin_unlock_irqrestore(&cs->lock, flags);
+                       gig_dbg(DEBUG_CHANNEL,
+                               "could not allocate all channels");
+                       return -EBUSY;
+               }
+       for (i = 0; i < cs->channels; ++i)
+               ++cs->bcs[i].use_count;
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       gig_dbg(DEBUG_CHANNEL, "allocated all channels");
+
+       return 0;
+}
+
+void gigaset_free_channels(struct cardstate *cs)
+{
+       unsigned long flags;
+       int i;
+
+       gig_dbg(DEBUG_CHANNEL, "unblocking all channels");
+       spin_lock_irqsave(&cs->lock, flags);
+       for (i = 0; i < cs->channels; ++i)
+               --cs->bcs[i].use_count;
+       spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+void gigaset_block_channels(struct cardstate *cs)
+{
+       unsigned long flags;
+       int i;
+
+       gig_dbg(DEBUG_CHANNEL, "blocking all channels");
+       spin_lock_irqsave(&cs->lock, flags);
+       for (i = 0; i < cs->channels; ++i)
+               ++cs->bcs[i].use_count;
+       spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+static void clear_events(struct cardstate *cs)
+{
+       struct event_t *ev;
+       unsigned head, tail;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->ev_lock, flags);
+
+       head = cs->ev_head;
+       tail = cs->ev_tail;
+
+       while (tail != head) {
+               ev = cs->events + head;
+               kfree(ev->ptr);
+               head = (head + 1) % MAX_EVENTS;
+       }
+
+       cs->ev_head = tail;
+
+       spin_unlock_irqrestore(&cs->ev_lock, flags);
+}
+
+/**
+ * gigaset_add_event() - add event to device event queue
+ * @cs:                device descriptor structure.
+ * @at_state:  connection state structure.
+ * @type:      event type.
+ * @ptr:       pointer parameter for event.
+ * @parameter: integer parameter for event.
+ * @arg:       pointer parameter for event.
+ *
+ * Allocate an event queue entry from the device's event queue, and set it up
+ * with the parameters given.
+ *
+ * Return value: added event
+ */
+struct event_t *gigaset_add_event(struct cardstate *cs,
+                                 struct at_state_t *at_state, int type,
+                                 void *ptr, int parameter, void *arg)
+{
+       unsigned long flags;
+       unsigned next, tail;
+       struct event_t *event = NULL;
+
+       gig_dbg(DEBUG_EVENT, "queueing event %d", type);
+
+       spin_lock_irqsave(&cs->ev_lock, flags);
+
+       tail = cs->ev_tail;
+       next = (tail + 1) % MAX_EVENTS;
+       if (unlikely(next == cs->ev_head))
+               dev_err(cs->dev, "event queue full\n");
+       else {
+               event = cs->events + tail;
+               event->type = type;
+               event->at_state = at_state;
+               event->cid = -1;
+               event->ptr = ptr;
+               event->arg = arg;
+               event->parameter = parameter;
+               cs->ev_tail = next;
+       }
+
+       spin_unlock_irqrestore(&cs->ev_lock, flags);
+
+       return event;
+}
+EXPORT_SYMBOL_GPL(gigaset_add_event);
+
+static void clear_at_state(struct at_state_t *at_state)
+{
+       int i;
+
+       for (i = 0; i < STR_NUM; ++i) {
+               kfree(at_state->str_var[i]);
+               at_state->str_var[i] = NULL;
+       }
+}
+
+static void dealloc_temp_at_states(struct cardstate *cs)
+{
+       struct at_state_t *cur, *next;
+
+       list_for_each_entry_safe(cur, next, &cs->temp_at_states, list) {
+               list_del(&cur->list);
+               clear_at_state(cur);
+               kfree(cur);
+       }
+}
+
+static void gigaset_freebcs(struct bc_state *bcs)
+{
+       int i;
+
+       gig_dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel);
+       bcs->cs->ops->freebcshw(bcs);
+
+       gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel);
+       clear_at_state(&bcs->at_state);
+       gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel);
+       dev_kfree_skb(bcs->rx_skb);
+       bcs->rx_skb = NULL;
+
+       for (i = 0; i < AT_NUM; ++i) {
+               kfree(bcs->commands[i]);
+               bcs->commands[i] = NULL;
+       }
+}
+
+static struct cardstate *alloc_cs(struct gigaset_driver *drv)
+{
+       unsigned long flags;
+       unsigned i;
+       struct cardstate *cs;
+       struct cardstate *ret = NULL;
+
+       spin_lock_irqsave(&drv->lock, flags);
+       if (drv->blocked)
+               goto exit;
+       for (i = 0; i < drv->minors; ++i) {
+               cs = drv->cs + i;
+               if (!(cs->flags & VALID_MINOR)) {
+                       cs->flags = VALID_MINOR;
+                       ret = cs;
+                       break;
+               }
+       }
+exit:
+       spin_unlock_irqrestore(&drv->lock, flags);
+       return ret;
+}
+
+static void free_cs(struct cardstate *cs)
+{
+       cs->flags = 0;
+}
+
+static void make_valid(struct cardstate *cs, unsigned mask)
+{
+       unsigned long flags;
+       struct gigaset_driver *drv = cs->driver;
+       spin_lock_irqsave(&drv->lock, flags);
+       cs->flags |= mask;
+       spin_unlock_irqrestore(&drv->lock, flags);
+}
+
+static void make_invalid(struct cardstate *cs, unsigned mask)
+{
+       unsigned long flags;
+       struct gigaset_driver *drv = cs->driver;
+       spin_lock_irqsave(&drv->lock, flags);
+       cs->flags &= ~mask;
+       spin_unlock_irqrestore(&drv->lock, flags);
+}
+
+/**
+ * gigaset_freecs() - free all associated ressources of a device
+ * @cs:                device descriptor structure.
+ *
+ * Stops all tasklets and timers, unregisters the device from all
+ * subsystems it was registered to, deallocates the device structure
+ * @cs and all structures referenced from it.
+ * Operations on the device should be stopped before calling this.
+ */
+void gigaset_freecs(struct cardstate *cs)
+{
+       int i;
+       unsigned long flags;
+
+       if (!cs)
+               return;
+
+       mutex_lock(&cs->mutex);
+
+       spin_lock_irqsave(&cs->lock, flags);
+       cs->running = 0;
+       spin_unlock_irqrestore(&cs->lock, flags); /* event handler and timer are
+                                                    not rescheduled below */
+
+       tasklet_kill(&cs->event_tasklet);
+       del_timer_sync(&cs->timer);
+
+       switch (cs->cs_init) {
+       default:
+               /* clear B channel structures */
+               for (i = 0; i < cs->channels; ++i) {
+                       gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
+                       gigaset_freebcs(cs->bcs + i);
+               }
+
+               /* clear device sysfs */
+               gigaset_free_dev_sysfs(cs);
+
+               gigaset_if_free(cs);
+
+               gig_dbg(DEBUG_INIT, "clearing hw");
+               cs->ops->freecshw(cs);
+
+               /* fall through */
+       case 2: /* error in initcshw */
+               /* Deregister from LL */
+               make_invalid(cs, VALID_ID);
+               gigaset_isdn_unregdev(cs);
+
+               /* fall through */
+       case 1: /* error when registering to LL */
+               gig_dbg(DEBUG_INIT, "clearing at_state");
+               clear_at_state(&cs->at_state);
+               dealloc_temp_at_states(cs);
+               clear_events(cs);
+               tty_port_destroy(&cs->port);
+
+               /* fall through */
+       case 0: /* error in basic setup */
+               gig_dbg(DEBUG_INIT, "freeing inbuf");
+               kfree(cs->inbuf);
+               kfree(cs->bcs);
+       }
+
+       mutex_unlock(&cs->mutex);
+       free_cs(cs);
+}
+EXPORT_SYMBOL_GPL(gigaset_freecs);
+
+void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
+                    struct cardstate *cs, int cid)
+{
+       int i;
+
+       INIT_LIST_HEAD(&at_state->list);
+       at_state->waiting = 0;
+       at_state->getstring = 0;
+       at_state->pending_commands = 0;
+       at_state->timer_expires = 0;
+       at_state->timer_active = 0;
+       at_state->timer_index = 0;
+       at_state->seq_index = 0;
+       at_state->ConState = 0;
+       for (i = 0; i < STR_NUM; ++i)
+               at_state->str_var[i] = NULL;
+       at_state->int_var[VAR_ZDLE] = 0;
+       at_state->int_var[VAR_ZCTP] = -1;
+       at_state->int_var[VAR_ZSAU] = ZSAU_NULL;
+       at_state->cs = cs;
+       at_state->bcs = bcs;
+       at_state->cid = cid;
+       if (!cid)
+               at_state->replystruct = cs->tabnocid;
+       else
+               at_state->replystruct = cs->tabcid;
+}
+
+
+static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct cardstate *cs)
+/* inbuf->read must be allocated before! */
+{
+       inbuf->head = 0;
+       inbuf->tail = 0;
+       inbuf->cs = cs;
+       inbuf->inputstate = INS_command;
+}
+
+/**
+ * gigaset_fill_inbuf() - append received data to input buffer
+ * @inbuf:     buffer structure.
+ * @src:       received data.
+ * @numbytes:  number of bytes received.
+ *
+ * Return value: !=0 if some data was appended
+ */
+int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
+                      unsigned numbytes)
+{
+       unsigned n, head, tail, bytesleft;
+
+       gig_dbg(DEBUG_INTR, "received %u bytes", numbytes);
+
+       if (!numbytes)
+               return 0;
+
+       bytesleft = numbytes;
+       tail = inbuf->tail;
+       head = inbuf->head;
+       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
+
+       while (bytesleft) {
+               if (head > tail)
+                       n = head - 1 - tail;
+               else if (head == 0)
+                       n = (RBUFSIZE - 1) - tail;
+               else
+                       n = RBUFSIZE - tail;
+               if (!n) {
+                       dev_err(inbuf->cs->dev,
+                               "buffer overflow (%u bytes lost)\n",
+                               bytesleft);
+                       break;
+               }
+               if (n > bytesleft)
+                       n = bytesleft;
+               memcpy(inbuf->data + tail, src, n);
+               bytesleft -= n;
+               tail = (tail + n) % RBUFSIZE;
+               src += n;
+       }
+       gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
+       inbuf->tail = tail;
+       return numbytes != bytesleft;
+}
+EXPORT_SYMBOL_GPL(gigaset_fill_inbuf);
+
+/* Initialize the b-channel structure */
+static int gigaset_initbcs(struct bc_state *bcs, struct cardstate *cs,
+                          int channel)
+{
+       int i;
+
+       bcs->tx_skb = NULL;
+
+       skb_queue_head_init(&bcs->squeue);
+
+       bcs->corrupted = 0;
+       bcs->trans_down = 0;
+       bcs->trans_up = 0;
+
+       gig_dbg(DEBUG_INIT, "setting up bcs[%d]->at_state", channel);
+       gigaset_at_init(&bcs->at_state, bcs, cs, -1);
+
+#ifdef CONFIG_GIGASET_DEBUG
+       bcs->emptycount = 0;
+#endif
+
+       bcs->rx_bufsize = 0;
+       bcs->rx_skb = NULL;
+       bcs->rx_fcs = PPP_INITFCS;
+       bcs->inputstate = 0;
+       bcs->channel = channel;
+       bcs->cs = cs;
+
+       bcs->chstate = 0;
+       bcs->use_count = 1;
+       bcs->busy = 0;
+       bcs->ignore = cs->ignoreframes;
+
+       for (i = 0; i < AT_NUM; ++i)
+               bcs->commands[i] = NULL;
+
+       spin_lock_init(&bcs->aplock);
+       bcs->ap = NULL;
+       bcs->apconnstate = 0;
+
+       gig_dbg(DEBUG_INIT, "  setting up bcs[%d]->hw", channel);
+       return cs->ops->initbcshw(bcs);
+}
+
+/**
+ * gigaset_initcs() - initialize device structure
+ * @drv:       hardware driver the device belongs to
+ * @channels:  number of B channels supported by device
+ * @onechannel:        !=0 if B channel data and AT commands share one
+ *                 communication channel (M10x),
+ *             ==0 if B channels have separate communication channels (base)
+ * @ignoreframes:      number of frames to ignore after setting up B channel
+ * @cidmode:   !=0: start in CallID mode
+ * @modulename:        name of driver module for LL registration
+ *
+ * Allocate and initialize cardstate structure for Gigaset driver
+ * Calls hardware dependent gigaset_initcshw() function
+ * Calls B channel initialization function gigaset_initbcs() for each B channel
+ *
+ * Return value:
+ *     pointer to cardstate structure
+ */
+struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
+                                int onechannel, int ignoreframes,
+                                int cidmode, const char *modulename)
+{
+       struct cardstate *cs;
+       unsigned long flags;
+       int i;
+
+       gig_dbg(DEBUG_INIT, "allocating cs");
+       cs = alloc_cs(drv);
+       if (!cs) {
+               pr_err("maximum number of devices exceeded\n");
+               return NULL;
+       }
+
+       cs->cs_init = 0;
+       cs->channels = channels;
+       cs->onechannel = onechannel;
+       cs->ignoreframes = ignoreframes;
+       INIT_LIST_HEAD(&cs->temp_at_states);
+       cs->running = 0;
+       timer_setup(&cs->timer, timer_tick, 0);
+       spin_lock_init(&cs->ev_lock);
+       cs->ev_tail = 0;
+       cs->ev_head = 0;
+
+       tasklet_init(&cs->event_tasklet, gigaset_handle_event,
+                    (unsigned long) cs);
+       tty_port_init(&cs->port);
+       cs->commands_pending = 0;
+       cs->cur_at_seq = 0;
+       cs->gotfwver = -1;
+       cs->dev = NULL;
+       cs->tty_dev = NULL;
+       cs->cidmode = cidmode != 0;
+       cs->tabnocid = gigaset_tab_nocid;
+       cs->tabcid = gigaset_tab_cid;
+
+       init_waitqueue_head(&cs->waitqueue);
+       cs->waiting = 0;
+
+       cs->mode = M_UNKNOWN;
+       cs->mstate = MS_UNINITIALIZED;
+
+       cs->bcs = kmalloc_array(channels, sizeof(struct bc_state), GFP_KERNEL);
+       cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL);
+       if (!cs->bcs || !cs->inbuf) {
+               pr_err("out of memory\n");
+               goto error;
+       }
+       ++cs->cs_init;
+
+       gig_dbg(DEBUG_INIT, "setting up at_state");
+       spin_lock_init(&cs->lock);
+       gigaset_at_init(&cs->at_state, NULL, cs, 0);
+       cs->dle = 0;
+       cs->cbytes = 0;
+
+       gig_dbg(DEBUG_INIT, "setting up inbuf");
+       gigaset_inbuf_init(cs->inbuf, cs);
+
+       cs->connected = 0;
+       cs->isdn_up = 0;
+
+       gig_dbg(DEBUG_INIT, "setting up cmdbuf");
+       cs->cmdbuf = cs->lastcmdbuf = NULL;
+       spin_lock_init(&cs->cmdlock);
+       cs->curlen = 0;
+       cs->cmdbytes = 0;
+
+       gig_dbg(DEBUG_INIT, "setting up iif");
+       if (gigaset_isdn_regdev(cs, modulename) < 0) {
+               pr_err("error registering ISDN device\n");
+               goto error;
+       }
+
+       make_valid(cs, VALID_ID);
+       ++cs->cs_init;
+       gig_dbg(DEBUG_INIT, "setting up hw");
+       if (cs->ops->initcshw(cs) < 0)
+               goto error;
+
+       ++cs->cs_init;
+
+       /* set up character device */
+       gigaset_if_init(cs);
+
+       /* set up device sysfs */
+       gigaset_init_dev_sysfs(cs);
+
+       /* set up channel data structures */
+       for (i = 0; i < channels; ++i) {
+               gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i);
+               if (gigaset_initbcs(cs->bcs + i, cs, i) < 0) {
+                       pr_err("could not allocate channel %d data\n", i);
+                       goto error;
+               }
+       }
+
+       spin_lock_irqsave(&cs->lock, flags);
+       cs->running = 1;
+       spin_unlock_irqrestore(&cs->lock, flags);
+       cs->timer.expires = jiffies + msecs_to_jiffies(GIG_TICK);
+       add_timer(&cs->timer);
+
+       gig_dbg(DEBUG_INIT, "cs initialized");
+       return cs;
+
+error:
+       gig_dbg(DEBUG_INIT, "failed");
+       gigaset_freecs(cs);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(gigaset_initcs);
+
+/* ReInitialize the b-channel structure on hangup */
+void gigaset_bcs_reinit(struct bc_state *bcs)
+{
+       struct sk_buff *skb;
+       struct cardstate *cs = bcs->cs;
+       unsigned long flags;
+
+       while ((skb = skb_dequeue(&bcs->squeue)) != NULL)
+               dev_kfree_skb(skb);
+
+       spin_lock_irqsave(&cs->lock, flags);
+       clear_at_state(&bcs->at_state);
+       bcs->at_state.ConState = 0;
+       bcs->at_state.timer_active = 0;
+       bcs->at_state.timer_expires = 0;
+       bcs->at_state.cid = -1;                 /* No CID defined */
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       bcs->inputstate = 0;
+
+#ifdef CONFIG_GIGASET_DEBUG
+       bcs->emptycount = 0;
+#endif
+
+       bcs->rx_fcs = PPP_INITFCS;
+       bcs->chstate = 0;
+
+       bcs->ignore = cs->ignoreframes;
+       dev_kfree_skb(bcs->rx_skb);
+       bcs->rx_skb = NULL;
+
+       cs->ops->reinitbcshw(bcs);
+}
+
+static void cleanup_cs(struct cardstate *cs)
+{
+       struct cmdbuf_t *cb, *tcb;
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->lock, flags);
+
+       cs->mode = M_UNKNOWN;
+       cs->mstate = MS_UNINITIALIZED;
+
+       clear_at_state(&cs->at_state);
+       dealloc_temp_at_states(cs);
+       gigaset_at_init(&cs->at_state, NULL, cs, 0);
+
+       cs->inbuf->inputstate = INS_command;
+       cs->inbuf->head = 0;
+       cs->inbuf->tail = 0;
+
+       cb = cs->cmdbuf;
+       while (cb) {
+               tcb = cb;
+               cb = cb->next;
+               kfree(tcb);
+       }
+       cs->cmdbuf = cs->lastcmdbuf = NULL;
+       cs->curlen = 0;
+       cs->cmdbytes = 0;
+       cs->gotfwver = -1;
+       cs->dle = 0;
+       cs->cur_at_seq = 0;
+       cs->commands_pending = 0;
+       cs->cbytes = 0;
+
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       for (i = 0; i < cs->channels; ++i) {
+               gigaset_freebcs(cs->bcs + i);
+               if (gigaset_initbcs(cs->bcs + i, cs, i) < 0)
+                       pr_err("could not allocate channel %d data\n", i);
+       }
+
+       if (cs->waiting) {
+               cs->cmd_result = -ENODEV;
+               cs->waiting = 0;
+               wake_up_interruptible(&cs->waitqueue);
+       }
+}
+
+
+/**
+ * gigaset_start() - start device operations
+ * @cs:                device descriptor structure.
+ *
+ * Prepares the device for use by setting up communication parameters,
+ * scheduling an EV_START event to initiate device initialization, and
+ * waiting for completion of the initialization.
+ *
+ * Return value:
+ *     0 on success, error code < 0 on failure
+ */
+int gigaset_start(struct cardstate *cs)
+{
+       unsigned long flags;
+
+       if (mutex_lock_interruptible(&cs->mutex))
+               return -EBUSY;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       cs->connected = 1;
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       if (cs->mstate != MS_LOCKED) {
+               cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
+               cs->ops->baud_rate(cs, B115200);
+               cs->ops->set_line_ctrl(cs, CS8);
+               cs->control_state = TIOCM_DTR | TIOCM_RTS;
+       }
+
+       cs->waiting = 1;
+
+       if (!gigaset_add_event(cs, &cs->at_state, EV_START, NULL, 0, NULL)) {
+               cs->waiting = 0;
+               goto error;
+       }
+       gigaset_schedule_event(cs);
+
+       wait_event(cs->waitqueue, !cs->waiting);
+
+       mutex_unlock(&cs->mutex);
+       return 0;
+
+error:
+       mutex_unlock(&cs->mutex);
+       return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(gigaset_start);
+
+/**
+ * gigaset_shutdown() - shut down device operations
+ * @cs:                device descriptor structure.
+ *
+ * Deactivates the device by scheduling an EV_SHUTDOWN event and
+ * waiting for completion of the shutdown.
+ *
+ * Return value:
+ *     0 - success, -ENODEV - error (no device associated)
+ */
+int gigaset_shutdown(struct cardstate *cs)
+{
+       mutex_lock(&cs->mutex);
+
+       if (!(cs->flags & VALID_MINOR)) {
+               mutex_unlock(&cs->mutex);
+               return -ENODEV;
+       }
+
+       cs->waiting = 1;
+
+       if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL))
+               goto exit;
+       gigaset_schedule_event(cs);
+
+       wait_event(cs->waitqueue, !cs->waiting);
+
+       cleanup_cs(cs);
+
+exit:
+       mutex_unlock(&cs->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gigaset_shutdown);
+
+/**
+ * gigaset_stop() - stop device operations
+ * @cs:                device descriptor structure.
+ *
+ * Stops operations on the device by scheduling an EV_STOP event and
+ * waiting for completion of the shutdown.
+ */
+void gigaset_stop(struct cardstate *cs)
+{
+       mutex_lock(&cs->mutex);
+
+       cs->waiting = 1;
+
+       if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL))
+               goto exit;
+       gigaset_schedule_event(cs);
+
+       wait_event(cs->waitqueue, !cs->waiting);
+
+       cleanup_cs(cs);
+
+exit:
+       mutex_unlock(&cs->mutex);
+}
+EXPORT_SYMBOL_GPL(gigaset_stop);
+
+static LIST_HEAD(drivers);
+static DEFINE_SPINLOCK(driver_lock);
+
+struct cardstate *gigaset_get_cs_by_id(int id)
+{
+       unsigned long flags;
+       struct cardstate *ret = NULL;
+       struct cardstate *cs;
+       struct gigaset_driver *drv;
+       unsigned i;
+
+       spin_lock_irqsave(&driver_lock, flags);
+       list_for_each_entry(drv, &drivers, list) {
+               spin_lock(&drv->lock);
+               for (i = 0; i < drv->minors; ++i) {
+                       cs = drv->cs + i;
+                       if ((cs->flags & VALID_ID) && cs->myid == id) {
+                               ret = cs;
+                               break;
+                       }
+               }
+               spin_unlock(&drv->lock);
+               if (ret)
+                       break;
+       }
+       spin_unlock_irqrestore(&driver_lock, flags);
+       return ret;
+}
+
+static struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
+{
+       unsigned long flags;
+       struct cardstate *ret = NULL;
+       struct gigaset_driver *drv;
+       unsigned index;
+
+       spin_lock_irqsave(&driver_lock, flags);
+       list_for_each_entry(drv, &drivers, list) {
+               if (minor < drv->minor || minor >= drv->minor + drv->minors)
+                       continue;
+               index = minor - drv->minor;
+               spin_lock(&drv->lock);
+               if (drv->cs[index].flags & VALID_MINOR)
+                       ret = drv->cs + index;
+               spin_unlock(&drv->lock);
+               if (ret)
+                       break;
+       }
+       spin_unlock_irqrestore(&driver_lock, flags);
+       return ret;
+}
+
+struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty)
+{
+       return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start);
+}
+
+/**
+ * gigaset_freedriver() - free all associated ressources of a driver
+ * @drv:       driver descriptor structure.
+ *
+ * Unregisters the driver from the system and deallocates the driver
+ * structure @drv and all structures referenced from it.
+ * All devices should be shut down before calling this.
+ */
+void gigaset_freedriver(struct gigaset_driver *drv)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&driver_lock, flags);
+       list_del(&drv->list);
+       spin_unlock_irqrestore(&driver_lock, flags);
+
+       gigaset_if_freedriver(drv);
+
+       kfree(drv->cs);
+       kfree(drv);
+}
+EXPORT_SYMBOL_GPL(gigaset_freedriver);
+
+/**
+ * gigaset_initdriver() - initialize driver structure
+ * @minor:     First minor number
+ * @minors:    Number of minors this driver can handle
+ * @procname:  Name of the driver
+ * @devname:   Name of the device files (prefix without minor number)
+ *
+ * Allocate and initialize gigaset_driver structure. Initialize interface.
+ *
+ * Return value:
+ *     Pointer to the gigaset_driver structure on success, NULL on failure.
+ */
+struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
+                                         const char *procname,
+                                         const char *devname,
+                                         const struct gigaset_ops *ops,
+                                         struct module *owner)
+{
+       struct gigaset_driver *drv;
+       unsigned long flags;
+       unsigned i;
+
+       drv = kmalloc(sizeof *drv, GFP_KERNEL);
+       if (!drv)
+               return NULL;
+
+       drv->have_tty = 0;
+       drv->minor = minor;
+       drv->minors = minors;
+       spin_lock_init(&drv->lock);
+       drv->blocked = 0;
+       drv->ops = ops;
+       drv->owner = owner;
+       INIT_LIST_HEAD(&drv->list);
+
+       drv->cs = kmalloc_array(minors, sizeof(*drv->cs), GFP_KERNEL);
+       if (!drv->cs)
+               goto error;
+
+       for (i = 0; i < minors; ++i) {
+               drv->cs[i].flags = 0;
+               drv->cs[i].driver = drv;
+               drv->cs[i].ops = drv->ops;
+               drv->cs[i].minor_index = i;
+               mutex_init(&drv->cs[i].mutex);
+       }
+
+       gigaset_if_initdriver(drv, procname, devname);
+
+       spin_lock_irqsave(&driver_lock, flags);
+       list_add(&drv->list, &drivers);
+       spin_unlock_irqrestore(&driver_lock, flags);
+
+       return drv;
+
+error:
+       kfree(drv);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(gigaset_initdriver);
+
+/**
+ * gigaset_blockdriver() - block driver
+ * @drv:       driver descriptor structure.
+ *
+ * Prevents the driver from attaching new devices, in preparation for
+ * deregistration.
+ */
+void gigaset_blockdriver(struct gigaset_driver *drv)
+{
+       drv->blocked = 1;
+}
+EXPORT_SYMBOL_GPL(gigaset_blockdriver);
+
+static int __init gigaset_init_module(void)
+{
+       /* in accordance with the principle of least astonishment,
+        * setting the 'debug' parameter to 1 activates a sensible
+        * set of default debug levels
+        */
+       if (gigaset_debuglevel == 1)
+               gigaset_debuglevel = DEBUG_DEFAULT;
+
+       pr_info(DRIVER_DESC DRIVER_DESC_DEBUG "\n");
+       gigaset_isdn_regdrv();
+       return 0;
+}
+
+static void __exit gigaset_exit_module(void)
+{
+       gigaset_isdn_unregdrv();
+}
+
+module_init(gigaset_init_module);
+module_exit(gigaset_exit_module);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/isdn/gigaset/dummyll.c b/drivers/staging/isdn/gigaset/dummyll.c
new file mode 100644 (file)
index 0000000..570c2d5
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Dummy LL interface for the Gigaset driver
+ *
+ * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ *     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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include <linux/export.h>
+#include "gigaset.h"
+
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
+int gigaset_isdn_icall(struct at_state_t *at_state)
+{
+       return ICALL_IGNORE;
+}
+
+void gigaset_isdn_connD(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_start(struct cardstate *cs)
+{
+}
+
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+}
+
+int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
+{
+       return 0;
+}
+
+void gigaset_isdn_unregdev(struct cardstate *cs)
+{
+}
+
+void gigaset_isdn_regdrv(void)
+{
+       pr_info("no ISDN subsystem interface\n");
+}
+
+void gigaset_isdn_unregdrv(void)
+{
+}
diff --git a/drivers/staging/isdn/gigaset/ev-layer.c b/drivers/staging/isdn/gigaset/ev-layer.c
new file mode 100644 (file)
index 0000000..182826e
--- /dev/null
@@ -0,0 +1,1913 @@
+/*
+ * Stuff used by all variants of the driver
+ *
+ * Copyright (c) 2001 by Stefan Eilers,
+ *                       Hansjoerg Lipp <hjlipp@web.de>,
+ *                       Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ *     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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include <linux/export.h>
+#include "gigaset.h"
+
+/* ========================================================== */
+/* bit masks for pending commands */
+#define PC_DIAL                0x001
+#define PC_HUP         0x002
+#define PC_INIT                0x004
+#define PC_DLE0                0x008
+#define PC_DLE1                0x010
+#define PC_SHUTDOWN    0x020
+#define PC_ACCEPT      0x040
+#define PC_CID         0x080
+#define PC_NOCID       0x100
+#define PC_CIDMODE     0x200
+#define PC_UMMODE      0x400
+
+/* types of modem responses */
+#define RT_NOTHING     0
+#define RT_ZSAU                1
+#define RT_RING                2
+#define RT_NUMBER      3
+#define RT_STRING      4
+#define RT_ZCAU                6
+
+/* Possible ASCII responses */
+#define RSP_OK         0
+#define RSP_ERROR      1
+#define RSP_ZGCI       3
+#define RSP_RING       4
+#define RSP_ZVLS       5
+#define RSP_ZCAU       6
+
+/* responses with values to store in at_state */
+/* - numeric */
+#define RSP_VAR                100
+#define RSP_ZSAU       (RSP_VAR + VAR_ZSAU)
+#define RSP_ZDLE       (RSP_VAR + VAR_ZDLE)
+#define RSP_ZCTP       (RSP_VAR + VAR_ZCTP)
+/* - string */
+#define RSP_STR                (RSP_VAR + VAR_NUM)
+#define RSP_NMBR       (RSP_STR + STR_NMBR)
+#define RSP_ZCPN       (RSP_STR + STR_ZCPN)
+#define RSP_ZCON       (RSP_STR + STR_ZCON)
+#define RSP_ZBC                (RSP_STR + STR_ZBC)
+#define RSP_ZHLC       (RSP_STR + STR_ZHLC)
+
+#define RSP_WRONG_CID  -2      /* unknown cid in cmd */
+#define RSP_INVAL      -6      /* invalid response   */
+#define RSP_NODEV      -9      /* device not connected */
+
+#define RSP_NONE       -19
+#define RSP_STRING     -20
+#define RSP_NULL       -21
+#define RSP_INIT       -27
+#define RSP_ANY                -26
+#define RSP_LAST       -28
+
+/* actions for process_response */
+#define ACT_NOTHING            0
+#define ACT_SETDLE1            1
+#define ACT_SETDLE0            2
+#define ACT_FAILINIT           3
+#define ACT_HUPMODEM           4
+#define ACT_CONFIGMODE         5
+#define ACT_INIT               6
+#define ACT_DLE0               7
+#define ACT_DLE1               8
+#define ACT_FAILDLE0           9
+#define ACT_FAILDLE1           10
+#define ACT_RING               11
+#define ACT_CID                        12
+#define ACT_FAILCID            13
+#define ACT_SDOWN              14
+#define ACT_FAILSDOWN          15
+#define ACT_DEBUG              16
+#define ACT_WARN               17
+#define ACT_DIALING            18
+#define ACT_ABORTDIAL          19
+#define ACT_DISCONNECT         20
+#define ACT_CONNECT            21
+#define ACT_REMOTEREJECT       22
+#define ACT_CONNTIMEOUT                23
+#define ACT_REMOTEHUP          24
+#define ACT_ABORTHUP           25
+#define ACT_ICALL              26
+#define ACT_ACCEPTED           27
+#define ACT_ABORTACCEPT                28
+#define ACT_TIMEOUT            29
+#define ACT_GETSTRING          30
+#define ACT_SETVER             31
+#define ACT_FAILVER            32
+#define ACT_GOTVER             33
+#define ACT_TEST               34
+#define ACT_ERROR              35
+#define ACT_ABORTCID           36
+#define ACT_ZCAU               37
+#define ACT_NOTIFY_BC_DOWN     38
+#define ACT_NOTIFY_BC_UP       39
+#define ACT_DIAL               40
+#define ACT_ACCEPT             41
+#define ACT_HUP                        43
+#define ACT_IF_LOCK            44
+#define ACT_START              45
+#define ACT_STOP               46
+#define ACT_FAKEDLE0           47
+#define ACT_FAKEHUP            48
+#define ACT_FAKESDOWN          49
+#define ACT_SHUTDOWN           50
+#define ACT_PROC_CIDMODE       51
+#define ACT_UMODESET           52
+#define ACT_FAILUMODE          53
+#define ACT_CMODESET           54
+#define ACT_FAILCMODE          55
+#define ACT_IF_VER             56
+#define ACT_CMD                        100
+
+/* at command sequences */
+#define SEQ_NONE       0
+#define SEQ_INIT       100
+#define SEQ_DLE0       200
+#define SEQ_DLE1       250
+#define SEQ_CID                300
+#define SEQ_NOCID      350
+#define SEQ_HUP                400
+#define SEQ_DIAL       600
+#define SEQ_ACCEPT     720
+#define SEQ_SHUTDOWN   500
+#define SEQ_CIDMODE    10
+#define SEQ_UMMODE     11
+
+
+/* 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid),
+ * 400: hup, 500: reset, 600: dial, 700: ring */
+struct reply_t gigaset_tab_nocid[] =
+{
+/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
+ * action, command */
+
+/* initialize device, set cid mode if possible */
+       {RSP_INIT,       -1,  -1, SEQ_INIT,     100,  1, {ACT_TIMEOUT} },
+
+       {EV_TIMEOUT,    100, 100, -1,           101,  3, {0},   "Z\r"},
+       {RSP_OK,        101, 103, -1,           120,  5, {ACT_GETSTRING},
+                                                               "+GMR\r"},
+
+       {EV_TIMEOUT,    101, 101, -1,           102,  5, {0},   "Z\r"},
+       {RSP_ERROR,     101, 101, -1,           102,  5, {0},   "Z\r"},
+
+       {EV_TIMEOUT,    102, 102, -1,           108,  5, {ACT_SETDLE1},
+                                                               "^SDLE=0\r"},
+       {RSP_OK,        108, 108, -1,           104, -1},
+       {RSP_ZDLE,      104, 104,  0,           103,  5, {0},   "Z\r"},
+       {EV_TIMEOUT,    104, 104, -1,             0,  0, {ACT_FAILINIT} },
+       {RSP_ERROR,     108, 108, -1,             0,  0, {ACT_FAILINIT} },
+
+       {EV_TIMEOUT,    108, 108, -1,           105,  2, {ACT_SETDLE0,
+                                                         ACT_HUPMODEM,
+                                                         ACT_TIMEOUT} },
+       {EV_TIMEOUT,    105, 105, -1,           103,  5, {0},   "Z\r"},
+
+       {RSP_ERROR,     102, 102, -1,           107,  5, {0},   "^GETPRE\r"},
+       {RSP_OK,        107, 107, -1,             0,  0, {ACT_CONFIGMODE} },
+       {RSP_ERROR,     107, 107, -1,             0,  0, {ACT_FAILINIT} },
+       {EV_TIMEOUT,    107, 107, -1,             0,  0, {ACT_FAILINIT} },
+
+       {RSP_ERROR,     103, 103, -1,             0,  0, {ACT_FAILINIT} },
+       {EV_TIMEOUT,    103, 103, -1,             0,  0, {ACT_FAILINIT} },
+
+       {RSP_STRING,    120, 120, -1,           121, -1, {ACT_SETVER} },
+
+       {EV_TIMEOUT,    120, 121, -1,             0,  0, {ACT_FAILVER,
+                                                         ACT_INIT} },
+       {RSP_ERROR,     120, 121, -1,             0,  0, {ACT_FAILVER,
+                                                         ACT_INIT} },
+       {RSP_OK,        121, 121, -1,             0,  0, {ACT_GOTVER,
+                                                         ACT_INIT} },
+       {RSP_NONE,      121, 121, -1,           120,  0, {ACT_GETSTRING} },
+
+/* leave dle mode */
+       {RSP_INIT,        0,   0, SEQ_DLE0,     201,  5, {0},   "^SDLE=0\r"},
+       {RSP_OK,        201, 201, -1,           202, -1},
+       {RSP_ZDLE,      202, 202,  0,             0,  0, {ACT_DLE0} },
+       {RSP_NODEV,     200, 249, -1,             0,  0, {ACT_FAKEDLE0} },
+       {RSP_ERROR,     200, 249, -1,             0,  0, {ACT_FAILDLE0} },
+       {EV_TIMEOUT,    200, 249, -1,             0,  0, {ACT_FAILDLE0} },
+
+/* enter dle mode */
+       {RSP_INIT,        0,   0, SEQ_DLE1,     251,  5, {0},   "^SDLE=1\r"},
+       {RSP_OK,        251, 251, -1,           252, -1},
+       {RSP_ZDLE,      252, 252,  1,             0,  0, {ACT_DLE1} },
+       {RSP_ERROR,     250, 299, -1,             0,  0, {ACT_FAILDLE1} },
+       {EV_TIMEOUT,    250, 299, -1,             0,  0, {ACT_FAILDLE1} },
+
+/* incoming call */
+       {RSP_RING,       -1,  -1, -1,            -1, -1, {ACT_RING} },
+
+/* get cid */
+       {RSP_INIT,        0,   0, SEQ_CID,      301,  5, {0},   "^SGCI?\r"},
+       {RSP_OK,        301, 301, -1,           302, -1},
+       {RSP_ZGCI,      302, 302, -1,             0,  0, {ACT_CID} },
+       {RSP_ERROR,     301, 349, -1,             0,  0, {ACT_FAILCID} },
+       {EV_TIMEOUT,    301, 349, -1,             0,  0, {ACT_FAILCID} },
+
+/* enter cid mode */
+       {RSP_INIT,        0,   0, SEQ_CIDMODE,  150,  5, {0},   "^SGCI=1\r"},
+       {RSP_OK,        150, 150, -1,             0,  0, {ACT_CMODESET} },
+       {RSP_ERROR,     150, 150, -1,             0,  0, {ACT_FAILCMODE} },
+       {EV_TIMEOUT,    150, 150, -1,             0,  0, {ACT_FAILCMODE} },
+
+/* leave cid mode */
+       {RSP_INIT,        0,   0, SEQ_UMMODE,   160,  5, {0},   "Z\r"},
+       {RSP_OK,        160, 160, -1,             0,  0, {ACT_UMODESET} },
+       {RSP_ERROR,     160, 160, -1,             0,  0, {ACT_FAILUMODE} },
+       {EV_TIMEOUT,    160, 160, -1,             0,  0, {ACT_FAILUMODE} },
+
+/* abort getting cid */
+       {RSP_INIT,        0,   0, SEQ_NOCID,      0,  0, {ACT_ABORTCID} },
+
+/* reset */
+       {RSP_INIT,        0,   0, SEQ_SHUTDOWN, 504,  5, {0},   "Z\r"},
+       {RSP_OK,        504, 504, -1,             0,  0, {ACT_SDOWN} },
+       {RSP_ERROR,     501, 599, -1,             0,  0, {ACT_FAILSDOWN} },
+       {EV_TIMEOUT,    501, 599, -1,             0,  0, {ACT_FAILSDOWN} },
+       {RSP_NODEV,     501, 599, -1,             0,  0, {ACT_FAKESDOWN} },
+
+       {EV_PROC_CIDMODE, -1, -1, -1,            -1, -1, {ACT_PROC_CIDMODE} },
+       {EV_IF_LOCK,     -1,  -1, -1,            -1, -1, {ACT_IF_LOCK} },
+       {EV_IF_VER,      -1,  -1, -1,            -1, -1, {ACT_IF_VER} },
+       {EV_START,       -1,  -1, -1,            -1, -1, {ACT_START} },
+       {EV_STOP,        -1,  -1, -1,            -1, -1, {ACT_STOP} },
+       {EV_SHUTDOWN,    -1,  -1, -1,            -1, -1, {ACT_SHUTDOWN} },
+
+/* misc. */
+       {RSP_ERROR,      -1,  -1, -1,            -1, -1, {ACT_ERROR} },
+       {RSP_ZCAU,       -1,  -1, -1,            -1, -1, {ACT_ZCAU} },
+       {RSP_NONE,       -1,  -1, -1,            -1, -1, {ACT_DEBUG} },
+       {RSP_ANY,        -1,  -1, -1,            -1, -1, {ACT_WARN} },
+       {RSP_LAST}
+};
+
+/* 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring,
+ * 400: hup, 750: accepted icall */
+struct reply_t gigaset_tab_cid[] =
+{
+/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
+ * action, command */
+
+/* dial */
+       {EV_DIAL,        -1,  -1, -1,            -1, -1, {ACT_DIAL} },
+       {RSP_INIT,        0,   0, SEQ_DIAL,     601,  5, {ACT_CMD + AT_BC} },
+       {RSP_OK,        601, 601, -1,           603,  5, {ACT_CMD + AT_PROTO} },
+       {RSP_OK,        603, 603, -1,           604,  5, {ACT_CMD + AT_TYPE} },
+       {RSP_OK,        604, 604, -1,           605,  5, {ACT_CMD + AT_MSN} },
+       {RSP_NULL,      605, 605, -1,           606,  5, {ACT_CMD + AT_CLIP} },
+       {RSP_OK,        605, 605, -1,           606,  5, {ACT_CMD + AT_CLIP} },
+       {RSP_NULL,      606, 606, -1,           607,  5, {ACT_CMD + AT_ISO} },
+       {RSP_OK,        606, 606, -1,           607,  5, {ACT_CMD + AT_ISO} },
+       {RSP_OK,        607, 607, -1,           608,  5, {0},   "+VLS=17\r"},
+       {RSP_OK,        608, 608, -1,           609, -1},
+       {RSP_ZSAU,      609, 609, ZSAU_PROCEEDING, 610, 5, {ACT_CMD + AT_DIAL} },
+       {RSP_OK,        610, 610, -1,           650,  0, {ACT_DIALING} },
+
+       {RSP_ERROR,     601, 610, -1,             0,  0, {ACT_ABORTDIAL} },
+       {EV_TIMEOUT,    601, 610, -1,             0,  0, {ACT_ABORTDIAL} },
+
+/* optional dialing responses */
+       {EV_BC_OPEN,    650, 650, -1,           651, -1},
+       {RSP_ZVLS,      609, 651, 17,            -1, -1, {ACT_DEBUG} },
+       {RSP_ZCTP,      610, 651, -1,            -1, -1, {ACT_DEBUG} },
+       {RSP_ZCPN,      610, 651, -1,            -1, -1, {ACT_DEBUG} },
+       {RSP_ZSAU,      650, 651, ZSAU_CALL_DELIVERED, -1, -1, {ACT_DEBUG} },
+
+/* connect */
+       {RSP_ZSAU,      650, 650, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT} },
+       {RSP_ZSAU,      651, 651, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT,
+                                                         ACT_NOTIFY_BC_UP} },
+       {RSP_ZSAU,      750, 750, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT} },
+       {RSP_ZSAU,      751, 751, ZSAU_ACTIVE,  800, -1, {ACT_CONNECT,
+                                                         ACT_NOTIFY_BC_UP} },
+       {EV_BC_OPEN,    800, 800, -1,           800, -1, {ACT_NOTIFY_BC_UP} },
+
+/* remote hangup */
+       {RSP_ZSAU,      650, 651, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT} },
+       {RSP_ZSAU,      750, 751, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },
+       {RSP_ZSAU,      800, 800, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },
+
+/* hangup */
+       {EV_HUP,         -1,  -1, -1,            -1, -1, {ACT_HUP} },
+       {RSP_INIT,       -1,  -1, SEQ_HUP,      401,  5, {0},   "+VLS=0\r"},
+       {RSP_OK,        401, 401, -1,           402,  5},
+       {RSP_ZVLS,      402, 402,  0,           403,  5},
+       {RSP_ZSAU,      403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} },
+       {RSP_ZSAU,      403, 403, ZSAU_NULL,      0,  0, {ACT_DISCONNECT} },
+       {RSP_NODEV,     401, 403, -1,             0,  0, {ACT_FAKEHUP} },
+       {RSP_ERROR,     401, 401, -1,             0,  0, {ACT_ABORTHUP} },
+       {EV_TIMEOUT,    401, 403, -1,             0,  0, {ACT_ABORTHUP} },
+
+       {EV_BC_CLOSED,    0,   0, -1,             0, -1, {ACT_NOTIFY_BC_DOWN} },
+
+/* ring */
+       {RSP_ZBC,       700, 700, -1,            -1, -1, {0} },
+       {RSP_ZHLC,      700, 700, -1,            -1, -1, {0} },
+       {RSP_NMBR,      700, 700, -1,            -1, -1, {0} },
+       {RSP_ZCPN,      700, 700, -1,            -1, -1, {0} },
+       {RSP_ZCTP,      700, 700, -1,            -1, -1, {0} },
+       {EV_TIMEOUT,    700, 700, -1,           720, 720, {ACT_ICALL} },
+       {EV_BC_CLOSED,  720, 720, -1,             0, -1, {ACT_NOTIFY_BC_DOWN} },
+
+/*accept icall*/
+       {EV_ACCEPT,      -1,  -1, -1,            -1, -1, {ACT_ACCEPT} },
+       {RSP_INIT,      720, 720, SEQ_ACCEPT,   721,  5, {ACT_CMD + AT_PROTO} },
+       {RSP_OK,        721, 721, -1,           722,  5, {ACT_CMD + AT_ISO} },
+       {RSP_OK,        722, 722, -1,           723,  5, {0},   "+VLS=17\r"},
+       {RSP_OK,        723, 723, -1,           724,  5, {0} },
+       {RSP_ZVLS,      724, 724, 17,           750, 50, {ACT_ACCEPTED} },
+       {RSP_ERROR,     721, 729, -1,             0,  0, {ACT_ABORTACCEPT} },
+       {EV_TIMEOUT,    721, 729, -1,             0,  0, {ACT_ABORTACCEPT} },
+       {RSP_ZSAU,      700, 729, ZSAU_NULL,      0,  0, {ACT_ABORTACCEPT} },
+       {RSP_ZSAU,      700, 729, ZSAU_ACTIVE,    0,  0, {ACT_ABORTACCEPT} },
+       {RSP_ZSAU,      700, 729, ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT} },
+
+       {EV_BC_OPEN,    750, 750, -1,           751, -1},
+       {EV_TIMEOUT,    750, 751, -1,             0,  0, {ACT_CONNTIMEOUT} },
+
+/* B channel closed (general case) */
+       {EV_BC_CLOSED,   -1,  -1, -1,            -1, -1, {ACT_NOTIFY_BC_DOWN} },
+
+/* misc. */
+       {RSP_ZCON,       -1,  -1, -1,            -1, -1, {ACT_DEBUG} },
+       {RSP_ZCAU,       -1,  -1, -1,            -1, -1, {ACT_ZCAU} },
+       {RSP_NONE,       -1,  -1, -1,            -1, -1, {ACT_DEBUG} },
+       {RSP_ANY,        -1,  -1, -1,            -1, -1, {ACT_WARN} },
+       {RSP_LAST}
+};
+
+
+static const struct resp_type_t {
+       char    *response;
+       int     resp_code;
+       int     type;
+}
+resp_type[] =
+{
+       {"OK",          RSP_OK,         RT_NOTHING},
+       {"ERROR",       RSP_ERROR,      RT_NOTHING},
+       {"ZSAU",        RSP_ZSAU,       RT_ZSAU},
+       {"ZCAU",        RSP_ZCAU,       RT_ZCAU},
+       {"RING",        RSP_RING,       RT_RING},
+       {"ZGCI",        RSP_ZGCI,       RT_NUMBER},
+       {"ZVLS",        RSP_ZVLS,       RT_NUMBER},
+       {"ZCTP",        RSP_ZCTP,       RT_NUMBER},
+       {"ZDLE",        RSP_ZDLE,       RT_NUMBER},
+       {"ZHLC",        RSP_ZHLC,       RT_STRING},
+       {"ZBC",         RSP_ZBC,        RT_STRING},
+       {"NMBR",        RSP_NMBR,       RT_STRING},
+       {"ZCPN",        RSP_ZCPN,       RT_STRING},
+       {"ZCON",        RSP_ZCON,       RT_STRING},
+       {NULL,          0,              0}
+};
+
+static const struct zsau_resp_t {
+       char    *str;
+       int     code;
+}
+zsau_resp[] =
+{
+       {"OUTGOING_CALL_PROCEEDING",    ZSAU_PROCEEDING},
+       {"CALL_DELIVERED",              ZSAU_CALL_DELIVERED},
+       {"ACTIVE",                      ZSAU_ACTIVE},
+       {"DISCONNECT_IND",              ZSAU_DISCONNECT_IND},
+       {"NULL",                        ZSAU_NULL},
+       {"DISCONNECT_REQ",              ZSAU_DISCONNECT_REQ},
+       {NULL,                          ZSAU_UNKNOWN}
+};
+
+/* check for and remove fixed string prefix
+ * If s starts with prefix terminated by a non-alphanumeric character,
+ * return pointer to the first character after that, otherwise return NULL.
+ */
+static char *skip_prefix(char *s, const char *prefix)
+{
+       while (*prefix)
+               if (*s++ != *prefix++)
+                       return NULL;
+       if (isalnum(*s))
+               return NULL;
+       return s;
+}
+
+/* queue event with CID */
+static void add_cid_event(struct cardstate *cs, int cid, int type,
+                         void *ptr, int parameter)
+{
+       unsigned long flags;
+       unsigned next, tail;
+       struct event_t *event;
+
+       gig_dbg(DEBUG_EVENT, "queueing event %d for cid %d", type, cid);
+
+       spin_lock_irqsave(&cs->ev_lock, flags);
+
+       tail = cs->ev_tail;
+       next = (tail + 1) % MAX_EVENTS;
+       if (unlikely(next == cs->ev_head)) {
+               dev_err(cs->dev, "event queue full\n");
+               kfree(ptr);
+       } else {
+               event = cs->events + tail;
+               event->type = type;
+               event->cid = cid;
+               event->ptr = ptr;
+               event->arg = NULL;
+               event->parameter = parameter;
+               event->at_state = NULL;
+               cs->ev_tail = next;
+       }
+
+       spin_unlock_irqrestore(&cs->ev_lock, flags);
+}
+
+/**
+ * gigaset_handle_modem_response() - process received modem response
+ * @cs:                device descriptor structure.
+ *
+ * Called by asyncdata/isocdata if a block of data received from the
+ * device must be processed as a modem command response. The data is
+ * already in the cs structure.
+ */
+void gigaset_handle_modem_response(struct cardstate *cs)
+{
+       char *eoc, *psep, *ptr;
+       const struct resp_type_t *rt;
+       const struct zsau_resp_t *zr;
+       int cid, parameter;
+       u8 type, value;
+
+       if (!cs->cbytes) {
+               /* ignore additional LFs/CRs (M10x config mode or cx100) */
+               gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[0]);
+               return;
+       }
+       cs->respdata[cs->cbytes] = 0;
+
+       if (cs->at_state.getstring) {
+               /* state machine wants next line verbatim */
+               cs->at_state.getstring = 0;
+               ptr = kstrdup(cs->respdata, GFP_ATOMIC);
+               gig_dbg(DEBUG_EVENT, "string==%s", ptr ? ptr : "NULL");
+               add_cid_event(cs, 0, RSP_STRING, ptr, 0);
+               return;
+       }
+
+       /* look up response type */
+       for (rt = resp_type; rt->response; ++rt) {
+               eoc = skip_prefix(cs->respdata, rt->response);
+               if (eoc)
+                       break;
+       }
+       if (!rt->response) {
+               add_cid_event(cs, 0, RSP_NONE, NULL, 0);
+               gig_dbg(DEBUG_EVENT, "unknown modem response: '%s'\n",
+                       cs->respdata);
+               return;
+       }
+
+       /* check for CID */
+       psep = strrchr(cs->respdata, ';');
+       if (psep &&
+           !kstrtoint(psep + 1, 10, &cid) &&
+           cid >= 1 && cid <= 65535) {
+               /* valid CID: chop it off */
+               *psep = 0;
+       } else {
+               /* no valid CID: leave unchanged */
+               cid = 0;
+       }
+
+       gig_dbg(DEBUG_EVENT, "CMD received: %s", cs->respdata);
+       if (cid)
+               gig_dbg(DEBUG_EVENT, "CID: %d", cid);
+
+       switch (rt->type) {
+       case RT_NOTHING:
+               /* check parameter separator */
+               if (*eoc)
+                       goto bad_param; /* extra parameter */
+
+               add_cid_event(cs, cid, rt->resp_code, NULL, 0);
+               break;
+
+       case RT_RING:
+               /* check parameter separator */
+               if (!*eoc)
+                       eoc = NULL;     /* no parameter */
+               else if (*eoc++ != ',')
+                       goto bad_param;
+
+               add_cid_event(cs, 0, rt->resp_code, NULL, cid);
+
+               /* process parameters as individual responses */
+               while (eoc) {
+                       /* look up parameter type */
+                       psep = NULL;
+                       for (rt = resp_type; rt->response; ++rt) {
+                               psep = skip_prefix(eoc, rt->response);
+                               if (psep)
+                                       break;
+                       }
+
+                       /* all legal parameters are of type RT_STRING */
+                       if (!psep || rt->type != RT_STRING) {
+                               dev_warn(cs->dev,
+                                        "illegal RING parameter: '%s'\n",
+                                        eoc);
+                               return;
+                       }
+
+                       /* skip parameter value separator */
+                       if (*psep++ != '=')
+                               goto bad_param;
+
+                       /* look up end of parameter */
+                       eoc = strchr(psep, ',');
+                       if (eoc)
+                               *eoc++ = 0;
+
+                       /* retrieve parameter value */
+                       ptr = kstrdup(psep, GFP_ATOMIC);
+
+                       /* queue event */
+                       add_cid_event(cs, cid, rt->resp_code, ptr, 0);
+               }
+               break;
+
+       case RT_ZSAU:
+               /* check parameter separator */
+               if (!*eoc) {
+                       /* no parameter */
+                       add_cid_event(cs, cid, rt->resp_code, NULL, ZSAU_NONE);
+                       break;
+               }
+               if (*eoc++ != '=')
+                       goto bad_param;
+
+               /* look up parameter value */
+               for (zr = zsau_resp; zr->str; ++zr)
+                       if (!strcmp(eoc, zr->str))
+                               break;
+               if (!zr->str)
+                       goto bad_param;
+
+               add_cid_event(cs, cid, rt->resp_code, NULL, zr->code);
+               break;
+
+       case RT_STRING:
+               /* check parameter separator */
+               if (*eoc++ != '=')
+                       goto bad_param;
+
+               /* retrieve parameter value */
+               ptr = kstrdup(eoc, GFP_ATOMIC);
+
+               /* queue event */
+               add_cid_event(cs, cid, rt->resp_code, ptr, 0);
+               break;
+
+       case RT_ZCAU:
+               /* check parameter separators */
+               if (*eoc++ != '=')
+                       goto bad_param;
+               psep = strchr(eoc, ',');
+               if (!psep)
+                       goto bad_param;
+               *psep++ = 0;
+
+               /* decode parameter values */
+               if (kstrtou8(eoc, 16, &type) || kstrtou8(psep, 16, &value)) {
+                       *--psep = ',';
+                       goto bad_param;
+               }
+               parameter = (type << 8) | value;
+
+               add_cid_event(cs, cid, rt->resp_code, NULL, parameter);
+               break;
+
+       case RT_NUMBER:
+               /* check parameter separator */
+               if (*eoc++ != '=')
+                       goto bad_param;
+
+               /* decode parameter value */
+               if (kstrtoint(eoc, 10, &parameter))
+                       goto bad_param;
+
+               /* special case ZDLE: set flag before queueing event */
+               if (rt->resp_code == RSP_ZDLE)
+                       cs->dle = parameter;
+
+               add_cid_event(cs, cid, rt->resp_code, NULL, parameter);
+               break;
+
+bad_param:
+               /* parameter unexpected, incomplete or malformed */
+               dev_warn(cs->dev, "bad parameter in response '%s'\n",
+                        cs->respdata);
+               add_cid_event(cs, cid, rt->resp_code, NULL, -1);
+               break;
+
+       default:
+               dev_err(cs->dev, "%s: internal error on '%s'\n",
+                       __func__, cs->respdata);
+       }
+}
+EXPORT_SYMBOL_GPL(gigaset_handle_modem_response);
+
+/* disconnect_nobc
+ * process closing of connection associated with given AT state structure
+ * without B channel
+ */
+static void disconnect_nobc(struct at_state_t **at_state_p,
+                           struct cardstate *cs)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       ++(*at_state_p)->seq_index;
+
+       /* revert to selected idle mode */
+       if (!cs->cidmode) {
+               cs->at_state.pending_commands |= PC_UMMODE;
+               gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
+               cs->commands_pending = 1;
+       }
+
+       /* check for and deallocate temporary AT state */
+       if (!list_empty(&(*at_state_p)->list)) {
+               list_del(&(*at_state_p)->list);
+               kfree(*at_state_p);
+               *at_state_p = NULL;
+       }
+
+       spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+/* disconnect_bc
+ * process closing of connection associated with given AT state structure
+ * and B channel
+ */
+static void disconnect_bc(struct at_state_t *at_state,
+                         struct cardstate *cs, struct bc_state *bcs)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       ++at_state->seq_index;
+
+       /* revert to selected idle mode */
+       if (!cs->cidmode) {
+               cs->at_state.pending_commands |= PC_UMMODE;
+               gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
+               cs->commands_pending = 1;
+       }
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       /* invoke hardware specific handler */
+       cs->ops->close_bchannel(bcs);
+
+       /* notify LL */
+       if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
+               bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
+               gigaset_isdn_hupD(bcs);
+       }
+}
+
+/* get_free_channel
+ * get a free AT state structure: either one of those associated with the
+ * B channels of the Gigaset device, or if none of those is available,
+ * a newly allocated one with bcs=NULL
+ * The structure should be freed by calling disconnect_nobc() after use.
+ */
+static inline struct at_state_t *get_free_channel(struct cardstate *cs,
+                                                 int cid)
+/* cids: >0: siemens-cid
+ *        0: without cid
+ *       -1: no cid assigned yet
+ */
+{
+       unsigned long flags;
+       int i;
+       struct at_state_t *ret;
+
+       for (i = 0; i < cs->channels; ++i)
+               if (gigaset_get_channel(cs->bcs + i) >= 0) {
+                       ret = &cs->bcs[i].at_state;
+                       ret->cid = cid;
+                       return ret;
+               }
+
+       spin_lock_irqsave(&cs->lock, flags);
+       ret = kmalloc(sizeof(struct at_state_t), GFP_ATOMIC);
+       if (ret) {
+               gigaset_at_init(ret, NULL, cs, cid);
+               list_add(&ret->list, &cs->temp_at_states);
+       }
+       spin_unlock_irqrestore(&cs->lock, flags);
+       return ret;
+}
+
+static void init_failed(struct cardstate *cs, int mode)
+{
+       int i;
+       struct at_state_t *at_state;
+
+       cs->at_state.pending_commands &= ~PC_INIT;
+       cs->mode = mode;
+       cs->mstate = MS_UNINITIALIZED;
+       gigaset_free_channels(cs);
+       for (i = 0; i < cs->channels; ++i) {
+               at_state = &cs->bcs[i].at_state;
+               if (at_state->pending_commands & PC_CID) {
+                       at_state->pending_commands &= ~PC_CID;
+                       at_state->pending_commands |= PC_NOCID;
+                       cs->commands_pending = 1;
+               }
+       }
+}
+
+static void schedule_init(struct cardstate *cs, int state)
+{
+       if (cs->at_state.pending_commands & PC_INIT) {
+               gig_dbg(DEBUG_EVENT, "not scheduling PC_INIT again");
+               return;
+       }
+       cs->mstate = state;
+       cs->mode = M_UNKNOWN;
+       gigaset_block_channels(cs);
+       cs->at_state.pending_commands |= PC_INIT;
+       gig_dbg(DEBUG_EVENT, "Scheduling PC_INIT");
+       cs->commands_pending = 1;
+}
+
+/* send an AT command
+ * adding the "AT" prefix, cid and DLE encapsulation as appropriate
+ */
+static void send_command(struct cardstate *cs, const char *cmd,
+                        struct at_state_t *at_state)
+{
+       int cid = at_state->cid;
+       struct cmdbuf_t *cb;
+       size_t buflen;
+
+       buflen = strlen(cmd) + 12; /* DLE ( A T 1 2 3 4 5 <cmd> DLE ) \0 */
+       cb = kmalloc(sizeof(struct cmdbuf_t) + buflen, GFP_ATOMIC);
+       if (!cb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               return;
+       }
+       if (cid > 0 && cid <= 65535)
+               cb->len = snprintf(cb->buf, buflen,
+                                  cs->dle ? "\020(AT%d%s\020)" : "AT%d%s",
+                                  cid, cmd);
+       else
+               cb->len = snprintf(cb->buf, buflen,
+                                  cs->dle ? "\020(AT%s\020)" : "AT%s",
+                                  cmd);
+       cb->offset = 0;
+       cb->next = NULL;
+       cb->wake_tasklet = NULL;
+       cs->ops->write_cmd(cs, cb);
+}
+
+static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid)
+{
+       struct at_state_t *at_state;
+       int i;
+       unsigned long flags;
+
+       if (cid == 0)
+               return &cs->at_state;
+
+       for (i = 0; i < cs->channels; ++i)
+               if (cid == cs->bcs[i].at_state.cid)
+                       return &cs->bcs[i].at_state;
+
+       spin_lock_irqsave(&cs->lock, flags);
+
+       list_for_each_entry(at_state, &cs->temp_at_states, list)
+               if (cid == at_state->cid) {
+                       spin_unlock_irqrestore(&cs->lock, flags);
+                       return at_state;
+               }
+
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       return NULL;
+}
+
+static void bchannel_down(struct bc_state *bcs)
+{
+       if (bcs->chstate & CHS_B_UP) {
+               bcs->chstate &= ~CHS_B_UP;
+               gigaset_isdn_hupB(bcs);
+       }
+
+       if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
+               bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
+               gigaset_isdn_hupD(bcs);
+       }
+
+       gigaset_free_channel(bcs);
+
+       gigaset_bcs_reinit(bcs);
+}
+
+static void bchannel_up(struct bc_state *bcs)
+{
+       if (bcs->chstate & CHS_B_UP) {
+               dev_notice(bcs->cs->dev, "%s: B channel already up\n",
+                          __func__);
+               return;
+       }
+
+       bcs->chstate |= CHS_B_UP;
+       gigaset_isdn_connB(bcs);
+}
+
+static void start_dial(struct at_state_t *at_state, void *data,
+                      unsigned seq_index)
+{
+       struct bc_state *bcs = at_state->bcs;
+       struct cardstate *cs = at_state->cs;
+       char **commands = data;
+       unsigned long flags;
+       int i;
+
+       bcs->chstate |= CHS_NOTIFY_LL;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (at_state->seq_index != seq_index) {
+               spin_unlock_irqrestore(&cs->lock, flags);
+               goto error;
+       }
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       for (i = 0; i < AT_NUM; ++i) {
+               kfree(bcs->commands[i]);
+               bcs->commands[i] = commands[i];
+       }
+
+       at_state->pending_commands |= PC_CID;
+       gig_dbg(DEBUG_EVENT, "Scheduling PC_CID");
+       cs->commands_pending = 1;
+       return;
+
+error:
+       for (i = 0; i < AT_NUM; ++i) {
+               kfree(commands[i]);
+               commands[i] = NULL;
+       }
+       at_state->pending_commands |= PC_NOCID;
+       gig_dbg(DEBUG_EVENT, "Scheduling PC_NOCID");
+       cs->commands_pending = 1;
+       return;
+}
+
+static void start_accept(struct at_state_t *at_state)
+{
+       struct cardstate *cs = at_state->cs;
+       struct bc_state *bcs = at_state->bcs;
+       int i;
+
+       for (i = 0; i < AT_NUM; ++i) {
+               kfree(bcs->commands[i]);
+               bcs->commands[i] = NULL;
+       }
+
+       bcs->commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
+       bcs->commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
+       if (!bcs->commands[AT_PROTO] || !bcs->commands[AT_ISO]) {
+               dev_err(at_state->cs->dev, "out of memory\n");
+               /* error reset */
+               at_state->pending_commands |= PC_HUP;
+               gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP");
+               cs->commands_pending = 1;
+               return;
+       }
+
+       snprintf(bcs->commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+       snprintf(bcs->commands[AT_ISO], 9, "^SISO=%u\r", bcs->channel + 1);
+
+       at_state->pending_commands |= PC_ACCEPT;
+       gig_dbg(DEBUG_EVENT, "Scheduling PC_ACCEPT");
+       cs->commands_pending = 1;
+}
+
+static void do_start(struct cardstate *cs)
+{
+       gigaset_free_channels(cs);
+
+       if (cs->mstate != MS_LOCKED)
+               schedule_init(cs, MS_INIT);
+
+       cs->isdn_up = 1;
+       gigaset_isdn_start(cs);
+
+       cs->waiting = 0;
+       wake_up(&cs->waitqueue);
+}
+
+static void finish_shutdown(struct cardstate *cs)
+{
+       if (cs->mstate != MS_LOCKED) {
+               cs->mstate = MS_UNINITIALIZED;
+               cs->mode = M_UNKNOWN;
+       }
+
+       /* Tell the LL that the device is not available .. */
+       if (cs->isdn_up) {
+               cs->isdn_up = 0;
+               gigaset_isdn_stop(cs);
+       }
+
+       /* The rest is done by cleanup_cs() in process context. */
+
+       cs->cmd_result = -ENODEV;
+       cs->waiting = 0;
+       wake_up(&cs->waitqueue);
+}
+
+static void do_shutdown(struct cardstate *cs)
+{
+       gigaset_block_channels(cs);
+
+       if (cs->mstate == MS_READY) {
+               cs->mstate = MS_SHUTDOWN;
+               cs->at_state.pending_commands |= PC_SHUTDOWN;
+               gig_dbg(DEBUG_EVENT, "Scheduling PC_SHUTDOWN");
+               cs->commands_pending = 1;
+       } else
+               finish_shutdown(cs);
+}
+
+static void do_stop(struct cardstate *cs)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       cs->connected = 0;
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       do_shutdown(cs);
+}
+
+/* Entering cid mode or getting a cid failed:
+ * try to initialize the device and try again.
+ *
+ * channel >= 0: getting cid for the channel failed
+ * channel < 0:  entering cid mode failed
+ *
+ * returns 0 on success, <0 on failure
+ */
+static int reinit_and_retry(struct cardstate *cs, int channel)
+{
+       int i;
+
+       if (--cs->retry_count <= 0)
+               return -EFAULT;
+
+       for (i = 0; i < cs->channels; ++i)
+               if (cs->bcs[i].at_state.cid > 0)
+                       return -EBUSY;
+
+       if (channel < 0)
+               dev_warn(cs->dev,
+                        "Could not enter cid mode. Reinit device and try again.\n");
+       else {
+               dev_warn(cs->dev,
+                        "Could not get a call id. Reinit device and try again.\n");
+               cs->bcs[channel].at_state.pending_commands |= PC_CID;
+       }
+       schedule_init(cs, MS_INIT);
+       return 0;
+}
+
+static int at_state_invalid(struct cardstate *cs,
+                           struct at_state_t *test_ptr)
+{
+       unsigned long flags;
+       unsigned channel;
+       struct at_state_t *at_state;
+       int retval = 0;
+
+       spin_lock_irqsave(&cs->lock, flags);
+
+       if (test_ptr == &cs->at_state)
+               goto exit;
+
+       list_for_each_entry(at_state, &cs->temp_at_states, list)
+               if (at_state == test_ptr)
+                       goto exit;
+
+       for (channel = 0; channel < cs->channels; ++channel)
+               if (&cs->bcs[channel].at_state == test_ptr)
+                       goto exit;
+
+       retval = 1;
+exit:
+       spin_unlock_irqrestore(&cs->lock, flags);
+       return retval;
+}
+
+static void handle_icall(struct cardstate *cs, struct bc_state *bcs,
+                        struct at_state_t *at_state)
+{
+       int retval;
+
+       retval = gigaset_isdn_icall(at_state);
+       switch (retval) {
+       case ICALL_ACCEPT:
+               break;
+       default:
+               dev_err(cs->dev, "internal error: disposition=%d\n", retval);
+               /* fall through */
+       case ICALL_IGNORE:
+       case ICALL_REJECT:
+               /* hang up actively
+                * Device doc says that would reject the call.
+                * In fact it doesn't.
+                */
+               at_state->pending_commands |= PC_HUP;
+               cs->commands_pending = 1;
+               break;
+       }
+}
+
+static int do_lock(struct cardstate *cs)
+{
+       int mode;
+       int i;
+
+       switch (cs->mstate) {
+       case MS_UNINITIALIZED:
+       case MS_READY:
+               if (cs->cur_at_seq || !list_empty(&cs->temp_at_states) ||
+                   cs->at_state.pending_commands)
+                       return -EBUSY;
+
+               for (i = 0; i < cs->channels; ++i)
+                       if (cs->bcs[i].at_state.pending_commands)
+                               return -EBUSY;
+
+               if (gigaset_get_channels(cs) < 0)
+                       return -EBUSY;
+
+               break;
+       case MS_LOCKED:
+               break;
+       default:
+               return -EBUSY;
+       }
+
+       mode = cs->mode;
+       cs->mstate = MS_LOCKED;
+       cs->mode = M_UNKNOWN;
+
+       return mode;
+}
+
+static int do_unlock(struct cardstate *cs)
+{
+       if (cs->mstate != MS_LOCKED)
+               return -EINVAL;
+
+       cs->mstate = MS_UNINITIALIZED;
+       cs->mode = M_UNKNOWN;
+       gigaset_free_channels(cs);
+       if (cs->connected)
+               schedule_init(cs, MS_INIT);
+
+       return 0;
+}
+
+static void do_action(int action, struct cardstate *cs,
+                     struct bc_state *bcs,
+                     struct at_state_t **p_at_state, char **pp_command,
+                     int *p_genresp, int *p_resp_code,
+                     struct event_t *ev)
+{
+       struct at_state_t *at_state = *p_at_state;
+       struct bc_state *bcs2;
+       unsigned long flags;
+
+       int channel;
+
+       unsigned char *s, *e;
+       int i;
+       unsigned long val;
+
+       switch (action) {
+       case ACT_NOTHING:
+               break;
+       case ACT_TIMEOUT:
+               at_state->waiting = 1;
+               break;
+       case ACT_INIT:
+               cs->at_state.pending_commands &= ~PC_INIT;
+               cs->cur_at_seq = SEQ_NONE;
+               cs->mode = M_UNIMODEM;
+               spin_lock_irqsave(&cs->lock, flags);
+               if (!cs->cidmode) {
+                       spin_unlock_irqrestore(&cs->lock, flags);
+                       gigaset_free_channels(cs);
+                       cs->mstate = MS_READY;
+                       break;
+               }
+               spin_unlock_irqrestore(&cs->lock, flags);
+               cs->at_state.pending_commands |= PC_CIDMODE;
+               gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
+               cs->commands_pending = 1;
+               break;
+       case ACT_FAILINIT:
+               dev_warn(cs->dev, "Could not initialize the device.\n");
+               cs->dle = 0;
+               init_failed(cs, M_UNKNOWN);
+               cs->cur_at_seq = SEQ_NONE;
+               break;
+       case ACT_CONFIGMODE:
+               init_failed(cs, M_CONFIG);
+               cs->cur_at_seq = SEQ_NONE;
+               break;
+       case ACT_SETDLE1:
+               cs->dle = 1;
+               /* cs->inbuf[0].inputstate |= INS_command | INS_DLE_command; */
+               cs->inbuf[0].inputstate &=
+                       ~(INS_command | INS_DLE_command);
+               break;
+       case ACT_SETDLE0:
+               cs->dle = 0;
+               cs->inbuf[0].inputstate =
+                       (cs->inbuf[0].inputstate & ~INS_DLE_command)
+                       | INS_command;
+               break;
+       case ACT_CMODESET:
+               if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) {
+                       gigaset_free_channels(cs);
+                       cs->mstate = MS_READY;
+               }
+               cs->mode = M_CID;
+               cs->cur_at_seq = SEQ_NONE;
+               break;
+       case ACT_UMODESET:
+               cs->mode = M_UNIMODEM;
+               cs->cur_at_seq = SEQ_NONE;
+               break;
+       case ACT_FAILCMODE:
+               cs->cur_at_seq = SEQ_NONE;
+               if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) {
+                       init_failed(cs, M_UNKNOWN);
+                       break;
+               }
+               if (reinit_and_retry(cs, -1) < 0)
+                       schedule_init(cs, MS_RECOVER);
+               break;
+       case ACT_FAILUMODE:
+               cs->cur_at_seq = SEQ_NONE;
+               schedule_init(cs, MS_RECOVER);
+               break;
+       case ACT_HUPMODEM:
+               /* send "+++" (hangup in unimodem mode) */
+               if (cs->connected) {
+                       struct cmdbuf_t *cb;
+
+                       cb = kmalloc(sizeof(struct cmdbuf_t) + 3, GFP_ATOMIC);
+                       if (!cb) {
+                               dev_err(cs->dev, "%s: out of memory\n",
+                                       __func__);
+                               return;
+                       }
+                       memcpy(cb->buf, "+++", 3);
+                       cb->len = 3;
+                       cb->offset = 0;
+                       cb->next = NULL;
+                       cb->wake_tasklet = NULL;
+                       cs->ops->write_cmd(cs, cb);
+               }
+               break;
+       case ACT_RING:
+               /* get fresh AT state structure for new CID */
+               at_state = get_free_channel(cs, ev->parameter);
+               if (!at_state) {
+                       dev_warn(cs->dev,
+                                "RING ignored: could not allocate channel structure\n");
+                       break;
+               }
+
+               /* initialize AT state structure
+                * note that bcs may be NULL if no B channel is free
+                */
+               at_state->ConState = 700;
+               for (i = 0; i < STR_NUM; ++i) {
+                       kfree(at_state->str_var[i]);
+                       at_state->str_var[i] = NULL;
+               }
+               at_state->int_var[VAR_ZCTP] = -1;
+
+               spin_lock_irqsave(&cs->lock, flags);
+               at_state->timer_expires = RING_TIMEOUT;
+               at_state->timer_active = 1;
+               spin_unlock_irqrestore(&cs->lock, flags);
+               break;
+       case ACT_ICALL:
+               handle_icall(cs, bcs, at_state);
+               break;
+       case ACT_FAILSDOWN:
+               dev_warn(cs->dev, "Could not shut down the device.\n");
+               /* fall through */
+       case ACT_FAKESDOWN:
+       case ACT_SDOWN:
+               cs->cur_at_seq = SEQ_NONE;
+               finish_shutdown(cs);
+               break;
+       case ACT_CONNECT:
+               if (cs->onechannel) {
+                       at_state->pending_commands |= PC_DLE1;
+                       cs->commands_pending = 1;
+                       break;
+               }
+               bcs->chstate |= CHS_D_UP;
+               gigaset_isdn_connD(bcs);
+               cs->ops->init_bchannel(bcs);
+               break;
+       case ACT_DLE1:
+               cs->cur_at_seq = SEQ_NONE;
+               bcs = cs->bcs + cs->curchannel;
+
+               bcs->chstate |= CHS_D_UP;
+               gigaset_isdn_connD(bcs);
+               cs->ops->init_bchannel(bcs);
+               break;
+       case ACT_FAKEHUP:
+               at_state->int_var[VAR_ZSAU] = ZSAU_NULL;
+               /* fall through */
+       case ACT_DISCONNECT:
+               cs->cur_at_seq = SEQ_NONE;
+               at_state->cid = -1;
+               if (!bcs) {
+                       disconnect_nobc(p_at_state, cs);
+               } else if (cs->onechannel && cs->dle) {
+                       /* Check for other open channels not needed:
+                        * DLE only used for M10x with one B channel.
+                        */
+                       at_state->pending_commands |= PC_DLE0;
+                       cs->commands_pending = 1;
+               } else {
+                       disconnect_bc(at_state, cs, bcs);
+               }
+               break;
+       case ACT_FAKEDLE0:
+               at_state->int_var[VAR_ZDLE] = 0;
+               cs->dle = 0;
+               /* fall through */
+       case ACT_DLE0:
+               cs->cur_at_seq = SEQ_NONE;
+               bcs2 = cs->bcs + cs->curchannel;
+               disconnect_bc(&bcs2->at_state, cs, bcs2);
+               break;
+       case ACT_ABORTHUP:
+               cs->cur_at_seq = SEQ_NONE;
+               dev_warn(cs->dev, "Could not hang up.\n");
+               at_state->cid = -1;
+               if (!bcs)
+                       disconnect_nobc(p_at_state, cs);
+               else if (cs->onechannel)
+                       at_state->pending_commands |= PC_DLE0;
+               else
+                       disconnect_bc(at_state, cs, bcs);
+               schedule_init(cs, MS_RECOVER);
+               break;
+       case ACT_FAILDLE0:
+               cs->cur_at_seq = SEQ_NONE;
+               dev_warn(cs->dev, "Error leaving DLE mode.\n");
+               cs->dle = 0;
+               bcs2 = cs->bcs + cs->curchannel;
+               disconnect_bc(&bcs2->at_state, cs, bcs2);
+               schedule_init(cs, MS_RECOVER);
+               break;
+       case ACT_FAILDLE1:
+               cs->cur_at_seq = SEQ_NONE;
+               dev_warn(cs->dev,
+                        "Could not enter DLE mode. Trying to hang up.\n");
+               channel = cs->curchannel;
+               cs->bcs[channel].at_state.pending_commands |= PC_HUP;
+               cs->commands_pending = 1;
+               break;
+
+       case ACT_CID: /* got cid; start dialing */
+               cs->cur_at_seq = SEQ_NONE;
+               channel = cs->curchannel;
+               if (ev->parameter > 0 && ev->parameter <= 65535) {
+                       cs->bcs[channel].at_state.cid = ev->parameter;
+                       cs->bcs[channel].at_state.pending_commands |=
+                               PC_DIAL;
+                       cs->commands_pending = 1;
+                       break;
+               }
+               /* fall through - bad cid */
+       case ACT_FAILCID:
+               cs->cur_at_seq = SEQ_NONE;
+               channel = cs->curchannel;
+               if (reinit_and_retry(cs, channel) < 0) {
+                       dev_warn(cs->dev,
+                                "Could not get a call ID. Cannot dial.\n");
+                       bcs2 = cs->bcs + channel;
+                       disconnect_bc(&bcs2->at_state, cs, bcs2);
+               }
+               break;
+       case ACT_ABORTCID:
+               cs->cur_at_seq = SEQ_NONE;
+               bcs2 = cs->bcs + cs->curchannel;
+               disconnect_bc(&bcs2->at_state, cs, bcs2);
+               break;
+
+       case ACT_DIALING:
+       case ACT_ACCEPTED:
+               cs->cur_at_seq = SEQ_NONE;
+               break;
+
+       case ACT_ABORTACCEPT:   /* hangup/error/timeout during ICALL procssng */
+               if (bcs)
+                       disconnect_bc(at_state, cs, bcs);
+               else
+                       disconnect_nobc(p_at_state, cs);
+               break;
+
+       case ACT_ABORTDIAL:     /* error/timeout during dial preparation */
+               cs->cur_at_seq = SEQ_NONE;
+               at_state->pending_commands |= PC_HUP;
+               cs->commands_pending = 1;
+               break;
+
+       case ACT_REMOTEREJECT:  /* DISCONNECT_IND after dialling */
+       case ACT_CONNTIMEOUT:   /* timeout waiting for ZSAU=ACTIVE */
+       case ACT_REMOTEHUP:     /* DISCONNECT_IND with established connection */
+               at_state->pending_commands |= PC_HUP;
+               cs->commands_pending = 1;
+               break;
+       case ACT_GETSTRING: /* warning: RING, ZDLE, ...
+                              are not handled properly anymore */
+               at_state->getstring = 1;
+               break;
+       case ACT_SETVER:
+               if (!ev->ptr) {
+                       *p_genresp = 1;
+                       *p_resp_code = RSP_ERROR;
+                       break;
+               }
+               s = ev->ptr;
+
+               if (!strcmp(s, "OK")) {
+                       /* OK without version string: assume old response */
+                       *p_genresp = 1;
+                       *p_resp_code = RSP_NONE;
+                       break;
+               }
+
+               for (i = 0; i < 4; ++i) {
+                       val = simple_strtoul(s, (char **) &e, 10);
+                       if (val > INT_MAX || e == s)
+                               break;
+                       if (i == 3) {
+                               if (*e)
+                                       break;
+                       } else if (*e != '.')
+                               break;
+                       else
+                               s = e + 1;
+                       cs->fwver[i] = val;
+               }
+               if (i != 4) {
+                       *p_genresp = 1;
+                       *p_resp_code = RSP_ERROR;
+                       break;
+               }
+               cs->gotfwver = 0;
+               break;
+       case ACT_GOTVER:
+               if (cs->gotfwver == 0) {
+                       cs->gotfwver = 1;
+                       gig_dbg(DEBUG_EVENT,
+                               "firmware version %02d.%03d.%02d.%02d",
+                               cs->fwver[0], cs->fwver[1],
+                               cs->fwver[2], cs->fwver[3]);
+                       break;
+               }
+               /* fall through */
+       case ACT_FAILVER:
+               cs->gotfwver = -1;
+               dev_err(cs->dev, "could not read firmware version.\n");
+               break;
+       case ACT_ERROR:
+               gig_dbg(DEBUG_ANY, "%s: ERROR response in ConState %d",
+                       __func__, at_state->ConState);
+               cs->cur_at_seq = SEQ_NONE;
+               break;
+       case ACT_DEBUG:
+               gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d",
+                       __func__, ev->type, at_state->ConState);
+               break;
+       case ACT_WARN:
+               dev_warn(cs->dev, "%s: resp_code %d in ConState %d!\n",
+                        __func__, ev->type, at_state->ConState);
+               break;
+       case ACT_ZCAU:
+               dev_warn(cs->dev, "cause code %04x in connection state %d.\n",
+                        ev->parameter, at_state->ConState);
+               break;
+
+       /* events from the LL */
+
+       case ACT_DIAL:
+               if (!ev->ptr) {
+                       *p_genresp = 1;
+                       *p_resp_code = RSP_ERROR;
+                       break;
+               }
+               start_dial(at_state, ev->ptr, ev->parameter);
+               break;
+       case ACT_ACCEPT:
+               start_accept(at_state);
+               break;
+       case ACT_HUP:
+               at_state->pending_commands |= PC_HUP;
+               gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP");
+               cs->commands_pending = 1;
+               break;
+
+       /* hotplug events */
+
+       case ACT_STOP:
+               do_stop(cs);
+               break;
+       case ACT_START:
+               do_start(cs);
+               break;
+
+       /* events from the interface */
+
+       case ACT_IF_LOCK:
+               cs->cmd_result = ev->parameter ? do_lock(cs) : do_unlock(cs);
+               cs->waiting = 0;
+               wake_up(&cs->waitqueue);
+               break;
+       case ACT_IF_VER:
+               if (ev->parameter != 0)
+                       cs->cmd_result = -EINVAL;
+               else if (cs->gotfwver != 1) {
+                       cs->cmd_result = -ENOENT;
+               } else {
+                       memcpy(ev->arg, cs->fwver, sizeof cs->fwver);
+                       cs->cmd_result = 0;
+               }
+               cs->waiting = 0;
+               wake_up(&cs->waitqueue);
+               break;
+
+       /* events from the proc file system */
+
+       case ACT_PROC_CIDMODE:
+               spin_lock_irqsave(&cs->lock, flags);
+               if (ev->parameter != cs->cidmode) {
+                       cs->cidmode = ev->parameter;
+                       if (ev->parameter) {
+                               cs->at_state.pending_commands |= PC_CIDMODE;
+                               gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
+                       } else {
+                               cs->at_state.pending_commands |= PC_UMMODE;
+                               gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
+                       }
+                       cs->commands_pending = 1;
+               }
+               spin_unlock_irqrestore(&cs->lock, flags);
+               cs->waiting = 0;
+               wake_up(&cs->waitqueue);
+               break;
+
+       /* events from the hardware drivers */
+
+       case ACT_NOTIFY_BC_DOWN:
+               bchannel_down(bcs);
+               break;
+       case ACT_NOTIFY_BC_UP:
+               bchannel_up(bcs);
+               break;
+       case ACT_SHUTDOWN:
+               do_shutdown(cs);
+               break;
+
+
+       default:
+               if (action >= ACT_CMD && action < ACT_CMD + AT_NUM) {
+                       *pp_command = at_state->bcs->commands[action - ACT_CMD];
+                       if (!*pp_command) {
+                               *p_genresp = 1;
+                               *p_resp_code = RSP_NULL;
+                       }
+               } else
+                       dev_err(cs->dev, "%s: action==%d!\n", __func__, action);
+       }
+}
+
+/* State machine to do the calling and hangup procedure */
+static void process_event(struct cardstate *cs, struct event_t *ev)
+{
+       struct bc_state *bcs;
+       char *p_command = NULL;
+       struct reply_t *rep;
+       int rcode;
+       int genresp = 0;
+       int resp_code = RSP_ERROR;
+       struct at_state_t *at_state;
+       int index;
+       int curact;
+       unsigned long flags;
+
+       if (ev->cid >= 0) {
+               at_state = at_state_from_cid(cs, ev->cid);
+               if (!at_state) {
+                       gig_dbg(DEBUG_EVENT, "event %d for invalid cid %d",
+                               ev->type, ev->cid);
+                       gigaset_add_event(cs, &cs->at_state, RSP_WRONG_CID,
+                                         NULL, 0, NULL);
+                       return;
+               }
+       } else {
+               at_state = ev->at_state;
+               if (at_state_invalid(cs, at_state)) {
+                       gig_dbg(DEBUG_EVENT, "event for invalid at_state %p",
+                               at_state);
+                       return;
+               }
+       }
+
+       gig_dbg(DEBUG_EVENT, "connection state %d, event %d",
+               at_state->ConState, ev->type);
+
+       bcs = at_state->bcs;
+
+       /* Setting the pointer to the dial array */
+       rep = at_state->replystruct;
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (ev->type == EV_TIMEOUT) {
+               if (ev->parameter != at_state->timer_index
+                   || !at_state->timer_active) {
+                       ev->type = RSP_NONE; /* old timeout */
+                       gig_dbg(DEBUG_EVENT, "old timeout");
+               } else {
+                       if (at_state->waiting)
+                               gig_dbg(DEBUG_EVENT, "stopped waiting");
+                       else
+                               gig_dbg(DEBUG_EVENT, "timeout occurred");
+               }
+       }
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       /* if the response belongs to a variable in at_state->int_var[VAR_XXXX]
+          or at_state->str_var[STR_XXXX], set it */
+       if (ev->type >= RSP_VAR && ev->type < RSP_VAR + VAR_NUM) {
+               index = ev->type - RSP_VAR;
+               at_state->int_var[index] = ev->parameter;
+       } else if (ev->type >= RSP_STR && ev->type < RSP_STR + STR_NUM) {
+               index = ev->type - RSP_STR;
+               kfree(at_state->str_var[index]);
+               at_state->str_var[index] = ev->ptr;
+               ev->ptr = NULL; /* prevent process_events() from
+                                  deallocating ptr */
+       }
+
+       if (ev->type == EV_TIMEOUT || ev->type == RSP_STRING)
+               at_state->getstring = 0;
+
+       /* Search row in dial array which matches modem response and current
+          constate */
+       for (;; rep++) {
+               rcode = rep->resp_code;
+               if (rcode == RSP_LAST) {
+                       /* found nothing...*/
+                       dev_warn(cs->dev, "%s: rcode=RSP_LAST: "
+                                "resp_code %d in ConState %d!\n",
+                                __func__, ev->type, at_state->ConState);
+                       return;
+               }
+               if ((rcode == RSP_ANY || rcode == ev->type)
+                   && ((int) at_state->ConState >= rep->min_ConState)
+                   && (rep->max_ConState < 0
+                       || (int) at_state->ConState <= rep->max_ConState)
+                   && (rep->parameter < 0 || rep->parameter == ev->parameter))
+                       break;
+       }
+
+       p_command = rep->command;
+
+       at_state->waiting = 0;
+       for (curact = 0; curact < MAXACT; ++curact) {
+               /* The row tells us what we should do  ..
+                */
+               do_action(rep->action[curact], cs, bcs, &at_state, &p_command,
+                         &genresp, &resp_code, ev);
+               if (!at_state)
+                       /* at_state destroyed by disconnect */
+                       return;
+       }
+
+       /* Jump to the next con-state regarding the array */
+       if (rep->new_ConState >= 0)
+               at_state->ConState = rep->new_ConState;
+
+       if (genresp) {
+               spin_lock_irqsave(&cs->lock, flags);
+               at_state->timer_expires = 0;
+               at_state->timer_active = 0;
+               spin_unlock_irqrestore(&cs->lock, flags);
+               gigaset_add_event(cs, at_state, resp_code, NULL, 0, NULL);
+       } else {
+               /* Send command to modem if not NULL... */
+               if (p_command) {
+                       if (cs->connected)
+                               send_command(cs, p_command, at_state);
+                       else
+                               gigaset_add_event(cs, at_state, RSP_NODEV,
+                                                 NULL, 0, NULL);
+               }
+
+               spin_lock_irqsave(&cs->lock, flags);
+               if (!rep->timeout) {
+                       at_state->timer_expires = 0;
+                       at_state->timer_active = 0;
+               } else if (rep->timeout > 0) { /* new timeout */
+                       at_state->timer_expires = rep->timeout * 10;
+                       at_state->timer_active = 1;
+                       ++at_state->timer_index;
+               }
+               spin_unlock_irqrestore(&cs->lock, flags);
+       }
+}
+
+static void schedule_sequence(struct cardstate *cs,
+                             struct at_state_t *at_state, int sequence)
+{
+       cs->cur_at_seq = sequence;
+       gigaset_add_event(cs, at_state, RSP_INIT, NULL, sequence, NULL);
+}
+
+static void process_command_flags(struct cardstate *cs)
+{
+       struct at_state_t *at_state = NULL;
+       struct bc_state *bcs;
+       int i;
+       int sequence;
+       unsigned long flags;
+
+       cs->commands_pending = 0;
+
+       if (cs->cur_at_seq) {
+               gig_dbg(DEBUG_EVENT, "not searching scheduled commands: busy");
+               return;
+       }
+
+       gig_dbg(DEBUG_EVENT, "searching scheduled commands");
+
+       sequence = SEQ_NONE;
+
+       /* clear pending_commands and hangup channels on shutdown */
+       if (cs->at_state.pending_commands & PC_SHUTDOWN) {
+               cs->at_state.pending_commands &= ~PC_CIDMODE;
+               for (i = 0; i < cs->channels; ++i) {
+                       bcs = cs->bcs + i;
+                       at_state = &bcs->at_state;
+                       at_state->pending_commands &=
+                               ~(PC_DLE1 | PC_ACCEPT | PC_DIAL);
+                       if (at_state->cid > 0)
+                               at_state->pending_commands |= PC_HUP;
+                       if (at_state->pending_commands & PC_CID) {
+                               at_state->pending_commands |= PC_NOCID;
+                               at_state->pending_commands &= ~PC_CID;
+                       }
+               }
+       }
+
+       /* clear pending_commands and hangup channels on reset */
+       if (cs->at_state.pending_commands & PC_INIT) {
+               cs->at_state.pending_commands &= ~PC_CIDMODE;
+               for (i = 0; i < cs->channels; ++i) {
+                       bcs = cs->bcs + i;
+                       at_state = &bcs->at_state;
+                       at_state->pending_commands &=
+                               ~(PC_DLE1 | PC_ACCEPT | PC_DIAL);
+                       if (at_state->cid > 0)
+                               at_state->pending_commands |= PC_HUP;
+                       if (cs->mstate == MS_RECOVER) {
+                               if (at_state->pending_commands & PC_CID) {
+                                       at_state->pending_commands |= PC_NOCID;
+                                       at_state->pending_commands &= ~PC_CID;
+                               }
+                       }
+               }
+       }
+
+       /* only switch back to unimodem mode if no commands are pending and
+        * no channels are up */
+       spin_lock_irqsave(&cs->lock, flags);
+       if (cs->at_state.pending_commands == PC_UMMODE
+           && !cs->cidmode
+           && list_empty(&cs->temp_at_states)
+           && cs->mode == M_CID) {
+               sequence = SEQ_UMMODE;
+               at_state = &cs->at_state;
+               for (i = 0; i < cs->channels; ++i) {
+                       bcs = cs->bcs + i;
+                       if (bcs->at_state.pending_commands ||
+                           bcs->at_state.cid > 0) {
+                               sequence = SEQ_NONE;
+                               break;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&cs->lock, flags);
+       cs->at_state.pending_commands &= ~PC_UMMODE;
+       if (sequence != SEQ_NONE) {
+               schedule_sequence(cs, at_state, sequence);
+               return;
+       }
+
+       for (i = 0; i < cs->channels; ++i) {
+               bcs = cs->bcs + i;
+               if (bcs->at_state.pending_commands & PC_HUP) {
+                       if (cs->dle) {
+                               cs->curchannel = bcs->channel;
+                               schedule_sequence(cs, &cs->at_state, SEQ_DLE0);
+                               return;
+                       }
+                       bcs->at_state.pending_commands &= ~PC_HUP;
+                       if (bcs->at_state.pending_commands & PC_CID) {
+                               /* not yet dialing: PC_NOCID is sufficient */
+                               bcs->at_state.pending_commands |= PC_NOCID;
+                               bcs->at_state.pending_commands &= ~PC_CID;
+                       } else {
+                               schedule_sequence(cs, &bcs->at_state, SEQ_HUP);
+                               return;
+                       }
+               }
+               if (bcs->at_state.pending_commands & PC_NOCID) {
+                       bcs->at_state.pending_commands &= ~PC_NOCID;
+                       cs->curchannel = bcs->channel;
+                       schedule_sequence(cs, &cs->at_state, SEQ_NOCID);
+                       return;
+               } else if (bcs->at_state.pending_commands & PC_DLE0) {
+                       bcs->at_state.pending_commands &= ~PC_DLE0;
+                       cs->curchannel = bcs->channel;
+                       schedule_sequence(cs, &cs->at_state, SEQ_DLE0);
+                       return;
+               }
+       }
+
+       list_for_each_entry(at_state, &cs->temp_at_states, list)
+               if (at_state->pending_commands & PC_HUP) {
+                       at_state->pending_commands &= ~PC_HUP;
+                       schedule_sequence(cs, at_state, SEQ_HUP);
+                       return;
+               }
+
+       if (cs->at_state.pending_commands & PC_INIT) {
+               cs->at_state.pending_commands &= ~PC_INIT;
+               cs->dle = 0;
+               cs->inbuf->inputstate = INS_command;
+               schedule_sequence(cs, &cs->at_state, SEQ_INIT);
+               return;
+       }
+       if (cs->at_state.pending_commands & PC_SHUTDOWN) {
+               cs->at_state.pending_commands &= ~PC_SHUTDOWN;
+               schedule_sequence(cs, &cs->at_state, SEQ_SHUTDOWN);
+               return;
+       }
+       if (cs->at_state.pending_commands & PC_CIDMODE) {
+               cs->at_state.pending_commands &= ~PC_CIDMODE;
+               if (cs->mode == M_UNIMODEM) {
+                       cs->retry_count = 1;
+                       schedule_sequence(cs, &cs->at_state, SEQ_CIDMODE);
+                       return;
+               }
+       }
+
+       for (i = 0; i < cs->channels; ++i) {
+               bcs = cs->bcs + i;
+               if (bcs->at_state.pending_commands & PC_DLE1) {
+                       bcs->at_state.pending_commands &= ~PC_DLE1;
+                       cs->curchannel = bcs->channel;
+                       schedule_sequence(cs, &cs->at_state, SEQ_DLE1);
+                       return;
+               }
+               if (bcs->at_state.pending_commands & PC_ACCEPT) {
+                       bcs->at_state.pending_commands &= ~PC_ACCEPT;
+                       schedule_sequence(cs, &bcs->at_state, SEQ_ACCEPT);
+                       return;
+               }
+               if (bcs->at_state.pending_commands & PC_DIAL) {
+                       bcs->at_state.pending_commands &= ~PC_DIAL;
+                       schedule_sequence(cs, &bcs->at_state, SEQ_DIAL);
+                       return;
+               }
+               if (bcs->at_state.pending_commands & PC_CID) {
+                       switch (cs->mode) {
+                       case M_UNIMODEM:
+                               cs->at_state.pending_commands |= PC_CIDMODE;
+                               gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
+                               cs->commands_pending = 1;
+                               return;
+                       case M_UNKNOWN:
+                               schedule_init(cs, MS_INIT);
+                               return;
+                       }
+                       bcs->at_state.pending_commands &= ~PC_CID;
+                       cs->curchannel = bcs->channel;
+                       cs->retry_count = 2;
+                       schedule_sequence(cs, &cs->at_state, SEQ_CID);
+                       return;
+               }
+       }
+}
+
+static void process_events(struct cardstate *cs)
+{
+       struct event_t *ev;
+       unsigned head, tail;
+       int i;
+       int check_flags = 0;
+       int was_busy;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cs->ev_lock, flags);
+       head = cs->ev_head;
+
+       for (i = 0; i < 2 * MAX_EVENTS; ++i) {
+               tail = cs->ev_tail;
+               if (tail == head) {
+                       if (!check_flags && !cs->commands_pending)
+                               break;
+                       check_flags = 0;
+                       spin_unlock_irqrestore(&cs->ev_lock, flags);
+                       process_command_flags(cs);
+                       spin_lock_irqsave(&cs->ev_lock, flags);
+                       tail = cs->ev_tail;
+                       if (tail == head) {
+                               if (!cs->commands_pending)
+                                       break;
+                               continue;
+                       }
+               }
+
+               ev = cs->events + head;
+               was_busy = cs->cur_at_seq != SEQ_NONE;
+               spin_unlock_irqrestore(&cs->ev_lock, flags);
+               process_event(cs, ev);
+               spin_lock_irqsave(&cs->ev_lock, flags);
+               kfree(ev->ptr);
+               ev->ptr = NULL;
+               if (was_busy && cs->cur_at_seq == SEQ_NONE)
+                       check_flags = 1;
+
+               head = (head + 1) % MAX_EVENTS;
+               cs->ev_head = head;
+       }
+
+       spin_unlock_irqrestore(&cs->ev_lock, flags);
+
+       if (i == 2 * MAX_EVENTS) {
+               dev_err(cs->dev,
+                       "infinite loop in process_events; aborting.\n");
+       }
+}
+
+/* tasklet scheduled on any event received from the Gigaset device
+ * parameter:
+ *     data    ISDN controller state structure
+ */
+void gigaset_handle_event(unsigned long data)
+{
+       struct cardstate *cs = (struct cardstate *) data;
+
+       /* handle incoming data on control/common channel */
+       if (cs->inbuf->head != cs->inbuf->tail) {
+               gig_dbg(DEBUG_INTR, "processing new data");
+               cs->ops->handle_input(cs->inbuf);
+       }
+
+       process_events(cs);
+}
diff --git a/drivers/staging/isdn/gigaset/gigaset.h b/drivers/staging/isdn/gigaset/gigaset.h
new file mode 100644 (file)
index 0000000..166537e
--- /dev/null
@@ -0,0 +1,830 @@
+/*
+ * Siemens Gigaset 307x driver
+ * Common header file for all connection variants
+ *
+ * Written by Stefan Eilers
+ *        and Hansjoerg Lipp <hjlipp@web.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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#ifndef GIGASET_H
+#define GIGASET_H
+
+/* define global prefix for pr_ macros in linux/kernel.h */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ppp_defs.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/list.h>
+#include <linux/atomic.h>
+
+#define GIG_VERSION {0, 5, 0, 0}
+#define GIG_COMPAT  {0, 4, 0, 0}
+
+#define MAX_REC_PARAMS 10      /* Max. number of params in response string */
+#define MAX_RESP_SIZE 511      /* Max. size of a response string */
+
+#define MAX_EVENTS 64          /* size of event queue */
+
+#define RBUFSIZE 8192
+
+#define GIG_TICK 100           /* in milliseconds */
+
+/* timeout values (unit: 1 sec) */
+#define INIT_TIMEOUT 1
+
+/* timeout values (unit: 0.1 sec) */
+#define RING_TIMEOUT 3         /* for additional parameters to RING */
+#define BAS_TIMEOUT 20         /* for response to Base USB ops */
+#define ATRDY_TIMEOUT 3                /* for HD_READY_SEND_ATDATA */
+
+#define BAS_RETRY 3            /* max. retries for base USB ops */
+
+#define MAXACT 3
+
+extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */
+
+/* debug flags, combine by adding/bitwise OR */
+enum debuglevel {
+       DEBUG_INTR        = 0x00008, /* interrupt processing */
+       DEBUG_CMD         = 0x00020, /* sent/received LL commands */
+       DEBUG_STREAM      = 0x00040, /* application data stream I/O events */
+       DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */
+       DEBUG_LLDATA      = 0x00100, /* sent/received LL data */
+       DEBUG_EVENT       = 0x00200, /* event processing */
+       DEBUG_HDLC        = 0x00800, /* M10x HDLC processing */
+       DEBUG_CHANNEL     = 0x01000, /* channel allocation/deallocation */
+       DEBUG_TRANSCMD    = 0x02000, /* AT-COMMANDS+RESPONSES */
+       DEBUG_MCMD        = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */
+       DEBUG_INIT        = 0x08000, /* (de)allocation+initialization of data
+                                       structures */
+       DEBUG_SUSPEND     = 0x10000, /* suspend/resume processing */
+       DEBUG_OUTPUT      = 0x20000, /* output to device */
+       DEBUG_ISO         = 0x40000, /* isochronous transfers */
+       DEBUG_IF          = 0x80000, /* character device operations */
+       DEBUG_USBREQ      = 0x100000, /* USB communication (except payload
+                                        data) */
+       DEBUG_LOCKCMD     = 0x200000, /* AT commands and responses when
+                                        MS_LOCKED */
+
+       DEBUG_ANY         = 0x3fffff, /* print message if any of the others is
+                                        activated */
+};
+
+#ifdef CONFIG_GIGASET_DEBUG
+
+#define gig_dbg(level, format, arg...)                                 \
+       do {                                                            \
+               if (unlikely(((enum debuglevel)gigaset_debuglevel) & (level))) \
+                       printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \
+                              ## arg);                                 \
+       } while (0)
+#define DEBUG_DEFAULT (DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ)
+
+#else
+
+#define gig_dbg(level, format, arg...) do {} while (0)
+#define DEBUG_DEFAULT 0
+
+#endif
+
+void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
+                       size_t len, const unsigned char *buf);
+
+/* connection state */
+#define ZSAU_NONE                      0
+#define ZSAU_PROCEEDING                        1
+#define ZSAU_CALL_DELIVERED            2
+#define ZSAU_ACTIVE                    3
+#define ZSAU_DISCONNECT_IND            4
+#define ZSAU_NULL                      5
+#define ZSAU_DISCONNECT_REQ            6
+#define ZSAU_UNKNOWN                   -1
+
+/* USB control transfer requests */
+#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
+#define IN_VENDOR_REQ  (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
+
+/* interrupt pipe messages */
+#define HD_B1_FLOW_CONTROL             0x80
+#define HD_B2_FLOW_CONTROL             0x81
+#define HD_RECEIVEATDATA_ACK           (0x35)          /* 3070 */
+#define HD_READY_SEND_ATDATA           (0x36)          /* 3070 */
+#define HD_OPEN_ATCHANNEL_ACK          (0x37)          /* 3070 */
+#define HD_CLOSE_ATCHANNEL_ACK         (0x38)          /* 3070 */
+#define HD_DEVICE_INIT_OK              (0x11)          /* ISurf USB + 3070 */
+#define HD_OPEN_B1CHANNEL_ACK          (0x51)          /* ISurf USB + 3070 */
+#define HD_OPEN_B2CHANNEL_ACK          (0x52)          /* ISurf USB + 3070 */
+#define HD_CLOSE_B1CHANNEL_ACK         (0x53)          /* ISurf USB + 3070 */
+#define HD_CLOSE_B2CHANNEL_ACK         (0x54)          /* ISurf USB + 3070 */
+#define HD_SUSPEND_END                 (0x61)          /* ISurf USB */
+#define HD_RESET_INTERRUPT_PIPE_ACK    (0xFF)          /* ISurf USB + 3070 */
+
+/* control requests */
+#define        HD_OPEN_B1CHANNEL               (0x23)          /* ISurf USB + 3070 */
+#define        HD_CLOSE_B1CHANNEL              (0x24)          /* ISurf USB + 3070 */
+#define        HD_OPEN_B2CHANNEL               (0x25)          /* ISurf USB + 3070 */
+#define        HD_CLOSE_B2CHANNEL              (0x26)          /* ISurf USB + 3070 */
+#define HD_RESET_INTERRUPT_PIPE                (0x27)          /* ISurf USB + 3070 */
+#define        HD_DEVICE_INIT_ACK              (0x34)          /* ISurf USB + 3070 */
+#define        HD_WRITE_ATMESSAGE              (0x12)          /* 3070 */
+#define        HD_READ_ATMESSAGE               (0x13)          /* 3070 */
+#define        HD_OPEN_ATCHANNEL               (0x28)          /* 3070 */
+#define        HD_CLOSE_ATCHANNEL              (0x29)          /* 3070 */
+
+/* number of B channels supported by base driver */
+#define BAS_CHANNELS   2
+
+/* USB frames for isochronous transfer */
+#define BAS_FRAMETIME  1       /* number of milliseconds between frames */
+#define BAS_NUMFRAMES  8       /* number of frames per URB */
+#define BAS_MAXFRAME   16      /* allocated bytes per frame */
+#define BAS_NORMFRAME  8       /* send size without flow control */
+#define BAS_HIGHFRAME  10      /* "    "    with positive flow control */
+#define BAS_LOWFRAME   5       /* "    "    with negative flow control */
+#define BAS_CORRFRAMES 4       /* flow control multiplicator */
+
+#define BAS_INBUFSIZE  (BAS_MAXFRAME * BAS_NUMFRAMES)  /* size of isoc in buf
+                                                        * per URB */
+#define BAS_OUTBUFSIZE 4096            /* size of common isoc out buffer */
+#define BAS_OUTBUFPAD  BAS_MAXFRAME    /* size of pad area for isoc out buf */
+
+#define BAS_INURBS     3
+#define BAS_OUTURBS    3
+
+/* variable commands in struct bc_state */
+#define AT_ISO         0
+#define AT_DIAL                1
+#define AT_MSN         2
+#define AT_BC          3
+#define AT_PROTO       4
+#define AT_TYPE                5
+#define AT_CLIP                6
+/* total number */
+#define AT_NUM         7
+
+/* variables in struct at_state_t */
+/* - numeric */
+#define VAR_ZSAU       0
+#define VAR_ZDLE       1
+#define VAR_ZCTP       2
+/* total number */
+#define VAR_NUM                3
+/* - string */
+#define STR_NMBR       0
+#define STR_ZCPN       1
+#define STR_ZCON       2
+#define STR_ZBC                3
+#define STR_ZHLC       4
+/* total number */
+#define STR_NUM                5
+
+/* event types */
+#define EV_TIMEOUT     -105
+#define EV_IF_VER      -106
+#define EV_PROC_CIDMODE        -107
+#define EV_SHUTDOWN    -108
+#define EV_START       -110
+#define EV_STOP                -111
+#define EV_IF_LOCK     -112
+#define EV_ACCEPT      -114
+#define EV_DIAL                -115
+#define EV_HUP         -116
+#define EV_BC_OPEN     -117
+#define EV_BC_CLOSED   -118
+
+/* input state */
+#define INS_command    0x0001  /* receiving messages (not payload data) */
+#define INS_DLE_char   0x0002  /* DLE flag received (in DLE mode) */
+#define INS_byte_stuff 0x0004
+#define INS_have_data  0x0008
+#define INS_DLE_command        0x0020  /* DLE message start (<DLE> X) received */
+#define INS_flag_hunt  0x0040
+
+/* channel state */
+#define CHS_D_UP       0x01
+#define CHS_B_UP       0x02
+#define CHS_NOTIFY_LL  0x04
+
+#define ICALL_REJECT   0
+#define ICALL_ACCEPT   1
+#define ICALL_IGNORE   2
+
+/* device state */
+#define MS_UNINITIALIZED       0
+#define MS_INIT                        1
+#define MS_LOCKED              2
+#define MS_SHUTDOWN            3
+#define MS_RECOVER             4
+#define MS_READY               5
+
+/* mode */
+#define M_UNKNOWN      0
+#define M_CONFIG       1
+#define M_UNIMODEM     2
+#define M_CID          3
+
+/* start mode */
+#define SM_LOCKED      0
+#define SM_ISDN                1 /* default */
+
+/* layer 2 protocols (AT^SBPR=...) */
+#define L2_BITSYNC     0
+#define L2_HDLC                1
+#define L2_VOICE       2
+
+struct gigaset_ops;
+struct gigaset_driver;
+
+struct usb_cardstate;
+struct ser_cardstate;
+struct bas_cardstate;
+
+struct bc_state;
+struct usb_bc_state;
+struct ser_bc_state;
+struct bas_bc_state;
+
+struct reply_t {
+       int     resp_code;      /* RSP_XXXX */
+       int     min_ConState;   /* <0 => ignore */
+       int     max_ConState;   /* <0 => ignore */
+       int     parameter;      /* e.g. ZSAU_XXXX <0: ignore*/
+       int     new_ConState;   /* <0 => ignore */
+       int     timeout;        /* >0 => *HZ; <=0 => TOUT_XXXX*/
+       int     action[MAXACT]; /* ACT_XXXX */
+       char    *command;       /* NULL==none */
+};
+
+extern struct reply_t gigaset_tab_cid[];
+extern struct reply_t gigaset_tab_nocid[];
+
+struct inbuf_t {
+       struct cardstate        *cs;
+       int                     inputstate;
+       int                     head, tail;
+       unsigned char           data[RBUFSIZE];
+};
+
+/* isochronous write buffer structure
+ * circular buffer with pad area for extraction of complete USB frames
+ * - data[read..nextread-1] is valid data already submitted to the USB subsystem
+ * - data[nextread..write-1] is valid data yet to be sent
+ * - data[write] is the next byte to write to
+ *   - in byte-oriented L2 procotols, it is completely free
+ *   - in bit-oriented L2 procotols, it may contain a partial byte of valid data
+ * - data[write+1..read-1] is free
+ * - wbits is the number of valid data bits in data[write], starting at the LSB
+ * - writesem is the semaphore for writing to the buffer:
+ *   if writesem <= 0, data[write..read-1] is currently being written to
+ * - idle contains the byte value to repeat when the end of valid data is
+ *   reached; if nextread==write (buffer contains no data to send), either the
+ *   BAS_OUTBUFPAD bytes immediately before data[write] (if
+ *   write>=BAS_OUTBUFPAD) or those of the pad area (if write<BAS_OUTBUFPAD)
+ *   are also filled with that value
+ */
+struct isowbuf_t {
+       int             read;
+       int             nextread;
+       int             write;
+       atomic_t        writesem;
+       int             wbits;
+       unsigned char   data[BAS_OUTBUFSIZE + BAS_OUTBUFPAD];
+       unsigned char   idle;
+};
+
+/* isochronous write URB context structure
+ * data to be stored along with the URB and retrieved when it is returned
+ * as completed by the USB subsystem
+ * - urb: pointer to the URB itself
+ * - bcs: pointer to the B Channel control structure
+ * - limit: end of write buffer area covered by this URB
+ * - status: URB completion status
+ */
+struct isow_urbctx_t {
+       struct urb *urb;
+       struct bc_state *bcs;
+       int limit;
+       int status;
+};
+
+/* AT state structure
+ * data associated with the state of an ISDN connection, whether or not
+ * it is currently assigned a B channel
+ */
+struct at_state_t {
+       struct list_head        list;
+       int                     waiting;
+       int                     getstring;
+       unsigned                timer_index;
+       unsigned long           timer_expires;
+       int                     timer_active;
+       unsigned int            ConState;       /* State of connection */
+       struct reply_t          *replystruct;
+       int                     cid;
+       int                     int_var[VAR_NUM];       /* see VAR_XXXX */
+       char                    *str_var[STR_NUM];      /* see STR_XXXX */
+       unsigned                pending_commands;       /* see PC_XXXX */
+       unsigned                seq_index;
+
+       struct cardstate        *cs;
+       struct bc_state         *bcs;
+};
+
+struct event_t {
+       int type;
+       void *ptr, *arg;
+       int parameter;
+       int cid;
+       struct at_state_t *at_state;
+};
+
+/* This buffer holds all information about the used B-Channel */
+struct bc_state {
+       struct sk_buff *tx_skb;         /* Current transfer buffer to modem */
+       struct sk_buff_head squeue;     /* B-Channel send Queue */
+
+       /* Variables for debugging .. */
+       int corrupted;                  /* Counter for corrupted packages */
+       int trans_down;                 /* Counter of packages (downstream) */
+       int trans_up;                   /* Counter of packages (upstream) */
+
+       struct at_state_t at_state;
+
+       /* receive buffer */
+       unsigned rx_bufsize;            /* max size accepted by application */
+       struct sk_buff *rx_skb;
+       __u16 rx_fcs;
+       int inputstate;                 /* see INS_XXXX */
+
+       int channel;
+
+       struct cardstate *cs;
+
+       unsigned chstate;               /* bitmap (CHS_*) */
+       int ignore;
+       unsigned proto2;                /* layer 2 protocol (L2_*) */
+       char *commands[AT_NUM];         /* see AT_XXXX */
+
+#ifdef CONFIG_GIGASET_DEBUG
+       int emptycount;
+#endif
+       int busy;
+       int use_count;
+
+       /* private data of hardware drivers */
+       union {
+               struct ser_bc_state *ser;       /* serial hardware driver */
+               struct usb_bc_state *usb;       /* usb hardware driver (m105) */
+               struct bas_bc_state *bas;       /* usb hardware driver (base) */
+       } hw;
+
+       void *ap;                       /* associated LL application */
+       int apconnstate;                /* LL application connection state */
+       spinlock_t aplock;
+};
+
+struct cardstate {
+       struct gigaset_driver *driver;
+       unsigned minor_index;
+       struct device *dev;
+       struct device *tty_dev;
+       unsigned flags;
+
+       const struct gigaset_ops *ops;
+
+       /* Stuff to handle communication */
+       wait_queue_head_t waitqueue;
+       int waiting;
+       int mode;                       /* see M_XXXX */
+       int mstate;                     /* Modem state: see MS_XXXX */
+                                       /* only changed by the event layer */
+       int cmd_result;
+
+       int channels;
+       struct bc_state *bcs;           /* Array of struct bc_state */
+
+       int onechannel;                 /* data and commands transmitted in one
+                                          stream (M10x) */
+
+       spinlock_t lock;
+       struct at_state_t at_state;     /* at_state_t for cid == 0 */
+       struct list_head temp_at_states;/* list of temporary "struct
+                                          at_state_t"s without B channel */
+
+       struct inbuf_t *inbuf;
+
+       struct cmdbuf_t *cmdbuf, *lastcmdbuf;
+       spinlock_t cmdlock;
+       unsigned curlen, cmdbytes;
+
+       struct tty_port port;
+       struct tasklet_struct if_wake_tasklet;
+       unsigned control_state;
+
+       unsigned fwver[4];
+       int gotfwver;
+
+       unsigned running;               /* !=0 if events are handled */
+       unsigned connected;             /* !=0 if hardware is connected */
+       unsigned isdn_up;               /* !=0 after gigaset_isdn_start() */
+
+       unsigned cidmode;
+
+       int myid;                       /* id for communication with LL */
+       void *iif;                      /* LL interface structure */
+       unsigned short hw_hdr_len;      /* headroom needed in data skbs */
+
+       struct reply_t *tabnocid;
+       struct reply_t *tabcid;
+       int cs_init;
+       int ignoreframes;               /* frames to ignore after setting up the
+                                          B channel */
+       struct mutex mutex;             /* locks this structure:
+                                        *   connected is not changed,
+                                        *   hardware_up is not changed,
+                                        *   MState is not changed to or from
+                                        *   MS_LOCKED */
+
+       struct timer_list timer;
+       int retry_count;
+       int dle;                        /* !=0 if DLE mode is active
+                                          (ZDLE=1 received -- M10x only) */
+       int cur_at_seq;                 /* sequence of AT commands being
+                                          processed */
+       int curchannel;                 /* channel those commands are meant
+                                          for */
+       int commands_pending;           /* flag(s) in xxx.commands_pending have
+                                          been set */
+       struct tasklet_struct
+               event_tasklet;          /* tasklet for serializing AT commands.
+                                        * Scheduled
+                                        *   -> for modem reponses (and
+                                        *      incoming data for M10x)
+                                        *   -> on timeout
+                                        *   -> after setting bits in
+                                        *      xxx.at_state.pending_command
+                                        *      (e.g. command from LL) */
+       struct tasklet_struct
+               write_tasklet;          /* tasklet for serial output
+                                        * (not used in base driver) */
+
+       /* event queue */
+       struct event_t events[MAX_EVENTS];
+       unsigned ev_tail, ev_head;
+       spinlock_t ev_lock;
+
+       /* current modem response */
+       unsigned char respdata[MAX_RESP_SIZE + 1];
+       unsigned cbytes;
+
+       /* private data of hardware drivers */
+       union {
+               struct usb_cardstate *usb; /* USB hardware driver (m105) */
+               struct ser_cardstate *ser; /* serial hardware driver */
+               struct bas_cardstate *bas; /* USB hardware driver (base) */
+       } hw;
+};
+
+struct gigaset_driver {
+       struct list_head list;
+       spinlock_t lock;                /* locks minor tables and blocked */
+       struct tty_driver *tty;
+       unsigned have_tty;
+       unsigned minor;
+       unsigned minors;
+       struct cardstate *cs;
+       int blocked;
+
+       const struct gigaset_ops *ops;
+       struct module *owner;
+};
+
+struct cmdbuf_t {
+       struct cmdbuf_t *next, *prev;
+       int len, offset;
+       struct tasklet_struct *wake_tasklet;
+       unsigned char buf[0];
+};
+
+struct bas_bc_state {
+       /* isochronous output state */
+       int             running;
+       atomic_t        corrbytes;
+       spinlock_t      isooutlock;
+       struct isow_urbctx_t    isoouturbs[BAS_OUTURBS];
+       struct isow_urbctx_t    *isooutdone, *isooutfree, *isooutovfl;
+       struct isowbuf_t        *isooutbuf;
+       unsigned numsub;                /* submitted URB counter
+                                          (for diagnostic messages only) */
+       struct tasklet_struct   sent_tasklet;
+
+       /* isochronous input state */
+       spinlock_t isoinlock;
+       struct urb *isoinurbs[BAS_INURBS];
+       unsigned char isoinbuf[BAS_INBUFSIZE * BAS_INURBS];
+       struct urb *isoindone;          /* completed isoc read URB */
+       int isoinstatus;                /* status of completed URB */
+       int loststatus;                 /* status of dropped URB */
+       unsigned isoinlost;             /* number of bytes lost */
+       /* state of bit unstuffing algorithm
+          (in addition to BC_state.inputstate) */
+       unsigned seqlen;                /* number of '1' bits not yet
+                                          unstuffed */
+       unsigned inbyte, inbits;        /* collected bits for next byte */
+       /* statistics */
+       unsigned goodbytes;             /* bytes correctly received */
+       unsigned alignerrs;             /* frames with incomplete byte at end */
+       unsigned fcserrs;               /* FCS errors */
+       unsigned frameerrs;             /* framing errors */
+       unsigned giants;                /* long frames */
+       unsigned runts;                 /* short frames */
+       unsigned aborts;                /* HDLC aborts */
+       unsigned shared0s;              /* '0' bits shared between flags */
+       unsigned stolen0s;              /* '0' stuff bits also serving as
+                                          leading flag bits */
+       struct tasklet_struct rcvd_tasklet;
+};
+
+struct gigaset_ops {
+       /* Called from ev-layer.c/interface.c for sending AT commands to the
+          device */
+       int (*write_cmd)(struct cardstate *cs, struct cmdbuf_t *cb);
+
+       /* Called from interface.c for additional device control */
+       int (*write_room)(struct cardstate *cs);
+       int (*chars_in_buffer)(struct cardstate *cs);
+       int (*brkchars)(struct cardstate *cs, const unsigned char buf[6]);
+
+       /* Called from ev-layer.c after setting up connection
+        * Should call gigaset_bchannel_up(), when finished. */
+       int (*init_bchannel)(struct bc_state *bcs);
+
+       /* Called from ev-layer.c after hanging up
+        * Should call gigaset_bchannel_down(), when finished. */
+       int (*close_bchannel)(struct bc_state *bcs);
+
+       /* Called by gigaset_initcs() for setting up bcs->hw.xxx */
+       int (*initbcshw)(struct bc_state *bcs);
+
+       /* Called by gigaset_freecs() for freeing bcs->hw.xxx */
+       void (*freebcshw)(struct bc_state *bcs);
+
+       /* Called by gigaset_bchannel_down() for resetting bcs->hw.xxx */
+       void (*reinitbcshw)(struct bc_state *bcs);
+
+       /* Called by gigaset_initcs() for setting up cs->hw.xxx */
+       int (*initcshw)(struct cardstate *cs);
+
+       /* Called by gigaset_freecs() for freeing cs->hw.xxx */
+       void (*freecshw)(struct cardstate *cs);
+
+       /* Called from common.c/interface.c for additional serial port
+          control */
+       int (*set_modem_ctrl)(struct cardstate *cs, unsigned old_state,
+                             unsigned new_state);
+       int (*baud_rate)(struct cardstate *cs, unsigned cflag);
+       int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag);
+
+       /* Called from LL interface to put an skb into the send-queue.
+        * After sending is completed, gigaset_skb_sent() must be called
+        * with the skb's link layer header preserved. */
+       int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb);
+
+       /* Called from ev-layer.c to process a block of data
+        * received through the common/control channel. */
+       void (*handle_input)(struct inbuf_t *inbuf);
+
+};
+
+/* = Common structures and definitions =======================================
+ */
+
+/* Parser states for DLE-Event:
+ * <DLE-EVENT>: <DLE_FLAG> "X" <EVENT> <DLE_FLAG> "."
+ * <DLE_FLAG>:  0x10
+ * <EVENT>:     ((a-z)* | (A-Z)* | (0-10)*)+
+ */
+#define DLE_FLAG       0x10
+
+/* ===========================================================================
+ *  Functions implemented in asyncdata.c
+ */
+
+/* Called from LL interface to put an skb into the send queue. */
+int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb);
+
+/* Called from ev-layer.c to process a block of data
+ * received through the common/control channel. */
+void gigaset_m10x_input(struct inbuf_t *inbuf);
+
+/* ===========================================================================
+ *  Functions implemented in isocdata.c
+ */
+
+/* Called from LL interface to put an skb into the send queue. */
+int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb);
+
+/* Called from ev-layer.c to process a block of data
+ * received through the common/control channel. */
+void gigaset_isoc_input(struct inbuf_t *inbuf);
+
+/* Called from bas-gigaset.c to process a block of data
+ * received through the isochronous channel */
+void gigaset_isoc_receive(unsigned char *src, unsigned count,
+                         struct bc_state *bcs);
+
+/* Called from bas-gigaset.c to put a block of data
+ * into the isochronous output buffer */
+int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len);
+
+/* Called from bas-gigaset.c to initialize the isochronous output buffer */
+void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle);
+
+/* Called from bas-gigaset.c to retrieve a block of bytes for sending */
+int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size);
+
+/* ===========================================================================
+ *  Functions implemented in LL interface
+ */
+
+/* Called from common.c for setting up/shutting down with the ISDN subsystem */
+void gigaset_isdn_regdrv(void);
+void gigaset_isdn_unregdrv(void);
+int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid);
+void gigaset_isdn_unregdev(struct cardstate *cs);
+
+/* Called from hardware module to indicate completion of an skb */
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb);
+void gigaset_isdn_rcv_err(struct bc_state *bcs);
+
+/* Called from common.c/ev-layer.c to indicate events relevant to the LL */
+void gigaset_isdn_start(struct cardstate *cs);
+void gigaset_isdn_stop(struct cardstate *cs);
+int gigaset_isdn_icall(struct at_state_t *at_state);
+void gigaset_isdn_connD(struct bc_state *bcs);
+void gigaset_isdn_hupD(struct bc_state *bcs);
+void gigaset_isdn_connB(struct bc_state *bcs);
+void gigaset_isdn_hupB(struct bc_state *bcs);
+
+/* ===========================================================================
+ *  Functions implemented in ev-layer.c
+ */
+
+/* tasklet called from common.c to process queued events */
+void gigaset_handle_event(unsigned long data);
+
+/* called from isocdata.c / asyncdata.c
+ * when a complete modem response line has been received */
+void gigaset_handle_modem_response(struct cardstate *cs);
+
+/* ===========================================================================
+ *  Functions implemented in proc.c
+ */
+
+/* initialize sysfs for device */
+void gigaset_init_dev_sysfs(struct cardstate *cs);
+void gigaset_free_dev_sysfs(struct cardstate *cs);
+
+/* ===========================================================================
+ *  Functions implemented in common.c/gigaset.h
+ */
+
+void gigaset_bcs_reinit(struct bc_state *bcs);
+void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
+                    struct cardstate *cs, int cid);
+int gigaset_get_channel(struct bc_state *bcs);
+struct bc_state *gigaset_get_free_channel(struct cardstate *cs);
+void gigaset_free_channel(struct bc_state *bcs);
+int gigaset_get_channels(struct cardstate *cs);
+void gigaset_free_channels(struct cardstate *cs);
+void gigaset_block_channels(struct cardstate *cs);
+
+/* Allocate and initialize driver structure. */
+struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
+                                         const char *procname,
+                                         const char *devname,
+                                         const struct gigaset_ops *ops,
+                                         struct module *owner);
+
+/* Deallocate driver structure. */
+void gigaset_freedriver(struct gigaset_driver *drv);
+
+struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty);
+struct cardstate *gigaset_get_cs_by_id(int id);
+void gigaset_blockdriver(struct gigaset_driver *drv);
+
+/* Allocate and initialize card state. Calls hardware dependent
+   gigaset_init[b]cs(). */
+struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
+                                int onechannel, int ignoreframes,
+                                int cidmode, const char *modulename);
+
+/* Free card state. Calls hardware dependent gigaset_free[b]cs(). */
+void gigaset_freecs(struct cardstate *cs);
+
+/* Tell common.c that hardware and driver are ready. */
+int gigaset_start(struct cardstate *cs);
+
+/* Tell common.c that the device is not present any more. */
+void gigaset_stop(struct cardstate *cs);
+
+/* Tell common.c that the driver is being unloaded. */
+int gigaset_shutdown(struct cardstate *cs);
+
+/* Append event to the queue.
+ * Returns NULL on failure or a pointer to the event on success.
+ * ptr must be kmalloc()ed (and not be freed by the caller).
+ */
+struct event_t *gigaset_add_event(struct cardstate *cs,
+                                 struct at_state_t *at_state, int type,
+                                 void *ptr, int parameter, void *arg);
+
+/* Called on CONFIG1 command from frontend. */
+int gigaset_enterconfigmode(struct cardstate *cs);
+
+/* cs->lock must not be locked */
+static inline void gigaset_schedule_event(struct cardstate *cs)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&cs->lock, flags);
+       if (cs->running)
+               tasklet_schedule(&cs->event_tasklet);
+       spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+/* Tell common.c that B channel has been closed. */
+/* cs->lock must not be locked */
+static inline void gigaset_bchannel_down(struct bc_state *bcs)
+{
+       gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_CLOSED, NULL, 0, NULL);
+       gigaset_schedule_event(bcs->cs);
+}
+
+/* Tell common.c that B channel has been opened. */
+/* cs->lock must not be locked */
+static inline void gigaset_bchannel_up(struct bc_state *bcs)
+{
+       gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_OPEN, NULL, 0, NULL);
+       gigaset_schedule_event(bcs->cs);
+}
+
+/* set up next receive skb for data mode */
+static inline struct sk_buff *gigaset_new_rx_skb(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+       unsigned short hw_hdr_len = cs->hw_hdr_len;
+
+       if (bcs->ignore) {
+               bcs->rx_skb = NULL;
+       } else {
+               bcs->rx_skb = dev_alloc_skb(bcs->rx_bufsize + hw_hdr_len);
+               if (bcs->rx_skb == NULL)
+                       dev_warn(cs->dev, "could not allocate skb\n");
+               else
+                       skb_reserve(bcs->rx_skb, hw_hdr_len);
+       }
+       return bcs->rx_skb;
+}
+
+/* append received bytes to inbuf */
+int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
+                      unsigned numbytes);
+
+/* ===========================================================================
+ *  Functions implemented in interface.c
+ */
+
+/* initialize interface */
+void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
+                          const char *devname);
+/* release interface */
+void gigaset_if_freedriver(struct gigaset_driver *drv);
+/* add minor */
+void gigaset_if_init(struct cardstate *cs);
+/* remove minor */
+void gigaset_if_free(struct cardstate *cs);
+/* device received data */
+void gigaset_if_receive(struct cardstate *cs,
+                       unsigned char *buffer, size_t len);
+
+#endif
diff --git a/drivers/staging/isdn/gigaset/interface.c b/drivers/staging/isdn/gigaset/interface.c
new file mode 100644 (file)
index 0000000..d9a578a
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+ * interface to user space for the gigaset driver
+ *
+ * Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.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 of
+ *    the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/gigaset_dev.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+
+/*** our ioctls ***/
+
+static int if_lock(struct cardstate *cs, int *arg)
+{
+       int cmd = *arg;
+
+       gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
+
+       if (cmd > 1)
+               return -EINVAL;
+
+       if (cmd < 0) {
+               *arg = cs->mstate == MS_LOCKED;
+               return 0;
+       }
+
+       if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
+               cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
+               cs->ops->baud_rate(cs, B115200);
+               cs->ops->set_line_ctrl(cs, CS8);
+               cs->control_state = TIOCM_DTR | TIOCM_RTS;
+       }
+
+       cs->waiting = 1;
+       if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
+                              NULL, cmd, NULL)) {
+               cs->waiting = 0;
+               return -ENOMEM;
+       }
+       gigaset_schedule_event(cs);
+
+       wait_event(cs->waitqueue, !cs->waiting);
+
+       if (cs->cmd_result >= 0) {
+               *arg = cs->cmd_result;
+               return 0;
+       }
+
+       return cs->cmd_result;
+}
+
+static int if_version(struct cardstate *cs, unsigned arg[4])
+{
+       static const unsigned version[4] = GIG_VERSION;
+       static const unsigned compat[4] = GIG_COMPAT;
+       unsigned cmd = arg[0];
+
+       gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
+
+       switch (cmd) {
+       case GIGVER_DRIVER:
+               memcpy(arg, version, sizeof version);
+               return 0;
+       case GIGVER_COMPAT:
+               memcpy(arg, compat, sizeof compat);
+               return 0;
+       case GIGVER_FWBASE:
+               cs->waiting = 1;
+               if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
+                                      NULL, 0, arg)) {
+                       cs->waiting = 0;
+                       return -ENOMEM;
+               }
+               gigaset_schedule_event(cs);
+
+               wait_event(cs->waitqueue, !cs->waiting);
+
+               if (cs->cmd_result >= 0)
+                       return 0;
+
+               return cs->cmd_result;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int if_config(struct cardstate *cs, int *arg)
+{
+       gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
+
+       if (*arg != 1)
+               return -EINVAL;
+
+       if (cs->mstate != MS_LOCKED)
+               return -EBUSY;
+
+       if (!cs->connected) {
+               pr_err("%s: not connected\n", __func__);
+               return -ENODEV;
+       }
+
+       *arg = 0;
+       return gigaset_enterconfigmode(cs);
+}
+
+/*** the terminal driver ***/
+
+static int if_open(struct tty_struct *tty, struct file *filp)
+{
+       struct cardstate *cs;
+
+       gig_dbg(DEBUG_IF, "%d+%d: %s()",
+               tty->driver->minor_start, tty->index, __func__);
+
+       cs = gigaset_get_cs_by_tty(tty);
+       if (!cs || !try_module_get(cs->driver->owner))
+               return -ENODEV;
+
+       if (mutex_lock_interruptible(&cs->mutex)) {
+               module_put(cs->driver->owner);
+               return -ERESTARTSYS;
+       }
+       tty->driver_data = cs;
+
+       ++cs->port.count;
+
+       if (cs->port.count == 1) {
+               tty_port_tty_set(&cs->port, tty);
+               cs->port.low_latency = 1;
+       }
+
+       mutex_unlock(&cs->mutex);
+       return 0;
+}
+
+static void if_close(struct tty_struct *tty, struct file *filp)
+{
+       struct cardstate *cs = tty->driver_data;
+
+       if (!cs) { /* happens if we didn't find cs in open */
+               gig_dbg(DEBUG_IF, "%s: no cardstate", __func__);
+               return;
+       }
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       mutex_lock(&cs->mutex);
+
+       if (!cs->connected)
+               gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
+       else if (!cs->port.count)
+               dev_warn(cs->dev, "%s: device not opened\n", __func__);
+       else if (!--cs->port.count)
+               tty_port_tty_set(&cs->port, NULL);
+
+       mutex_unlock(&cs->mutex);
+
+       module_put(cs->driver->owner);
+}
+
+static int if_ioctl(struct tty_struct *tty,
+                   unsigned int cmd, unsigned long arg)
+{
+       struct cardstate *cs = tty->driver_data;
+       int retval = -ENODEV;
+       int int_arg;
+       unsigned char buf[6];
+       unsigned version[4];
+
+       gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
+
+       if (mutex_lock_interruptible(&cs->mutex))
+               return -ERESTARTSYS;
+
+       if (!cs->connected) {
+               gig_dbg(DEBUG_IF, "not connected");
+               retval = -ENODEV;
+       } else {
+               retval = 0;
+               switch (cmd) {
+               case GIGASET_REDIR:
+                       retval = get_user(int_arg, (int __user *) arg);
+                       if (retval >= 0)
+                               retval = if_lock(cs, &int_arg);
+                       if (retval >= 0)
+                               retval = put_user(int_arg, (int __user *) arg);
+                       break;
+               case GIGASET_CONFIG:
+                       retval = get_user(int_arg, (int __user *) arg);
+                       if (retval >= 0)
+                               retval = if_config(cs, &int_arg);
+                       if (retval >= 0)
+                               retval = put_user(int_arg, (int __user *) arg);
+                       break;
+               case GIGASET_BRKCHARS:
+                       retval = copy_from_user(&buf,
+                                               (const unsigned char __user *) arg, 6)
+                               ? -EFAULT : 0;
+                       if (retval >= 0) {
+                               gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
+                                                  6, buf);
+                               retval = cs->ops->brkchars(cs, buf);
+                       }
+                       break;
+               case GIGASET_VERSION:
+                       retval = copy_from_user(version,
+                                               (unsigned __user *) arg, sizeof version)
+                               ? -EFAULT : 0;
+                       if (retval >= 0)
+                               retval = if_version(cs, version);
+                       if (retval >= 0)
+                               retval = copy_to_user((unsigned __user *) arg,
+                                                     version, sizeof version)
+                                       ? -EFAULT : 0;
+                       break;
+               default:
+                       gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
+                               __func__, cmd);
+                       retval = -ENOIOCTLCMD;
+               }
+       }
+
+       mutex_unlock(&cs->mutex);
+
+       return retval;
+}
+
+#ifdef CONFIG_COMPAT
+static long if_compat_ioctl(struct tty_struct *tty,
+                   unsigned int cmd, unsigned long arg)
+{
+       return if_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static int if_tiocmget(struct tty_struct *tty)
+{
+       struct cardstate *cs = tty->driver_data;
+       int retval;
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       if (mutex_lock_interruptible(&cs->mutex))
+               return -ERESTARTSYS;
+
+       retval = cs->control_state & (TIOCM_RTS | TIOCM_DTR);
+
+       mutex_unlock(&cs->mutex);
+
+       return retval;
+}
+
+static int if_tiocmset(struct tty_struct *tty,
+                      unsigned int set, unsigned int clear)
+{
+       struct cardstate *cs = tty->driver_data;
+       int retval;
+       unsigned mc;
+
+       gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
+               cs->minor_index, __func__, set, clear);
+
+       if (mutex_lock_interruptible(&cs->mutex))
+               return -ERESTARTSYS;
+
+       if (!cs->connected) {
+               gig_dbg(DEBUG_IF, "not connected");
+               retval = -ENODEV;
+       } else {
+               mc = (cs->control_state | set) & ~clear & (TIOCM_RTS | TIOCM_DTR);
+               retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
+               cs->control_state = mc;
+       }
+
+       mutex_unlock(&cs->mutex);
+
+       return retval;
+}
+
+static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       struct cardstate *cs = tty->driver_data;
+       struct cmdbuf_t *cb;
+       int retval;
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       if (mutex_lock_interruptible(&cs->mutex))
+               return -ERESTARTSYS;
+
+       if (!cs->connected) {
+               gig_dbg(DEBUG_IF, "not connected");
+               retval = -ENODEV;
+               goto done;
+       }
+       if (cs->mstate != MS_LOCKED) {
+               dev_warn(cs->dev, "can't write to unlocked device\n");
+               retval = -EBUSY;
+               goto done;
+       }
+       if (count <= 0) {
+               /* nothing to do */
+               retval = 0;
+               goto done;
+       }
+
+       cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
+       if (!cb) {
+               dev_err(cs->dev, "%s: out of memory\n", __func__);
+               retval = -ENOMEM;
+               goto done;
+       }
+
+       memcpy(cb->buf, buf, count);
+       cb->len = count;
+       cb->offset = 0;
+       cb->next = NULL;
+       cb->wake_tasklet = &cs->if_wake_tasklet;
+       retval = cs->ops->write_cmd(cs, cb);
+done:
+       mutex_unlock(&cs->mutex);
+       return retval;
+}
+
+static int if_write_room(struct tty_struct *tty)
+{
+       struct cardstate *cs = tty->driver_data;
+       int retval;
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       if (mutex_lock_interruptible(&cs->mutex))
+               return -ERESTARTSYS;
+
+       if (!cs->connected) {
+               gig_dbg(DEBUG_IF, "not connected");
+               retval = -ENODEV;
+       } else if (cs->mstate != MS_LOCKED) {
+               dev_warn(cs->dev, "can't write to unlocked device\n");
+               retval = -EBUSY;
+       } else
+               retval = cs->ops->write_room(cs);
+
+       mutex_unlock(&cs->mutex);
+
+       return retval;
+}
+
+static int if_chars_in_buffer(struct tty_struct *tty)
+{
+       struct cardstate *cs = tty->driver_data;
+       int retval = 0;
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       mutex_lock(&cs->mutex);
+
+       if (!cs->connected)
+               gig_dbg(DEBUG_IF, "not connected");
+       else if (cs->mstate != MS_LOCKED)
+               dev_warn(cs->dev, "can't write to unlocked device\n");
+       else
+               retval = cs->ops->chars_in_buffer(cs);
+
+       mutex_unlock(&cs->mutex);
+
+       return retval;
+}
+
+static void if_throttle(struct tty_struct *tty)
+{
+       struct cardstate *cs = tty->driver_data;
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       mutex_lock(&cs->mutex);
+
+       if (!cs->connected)
+               gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
+       else
+               gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
+
+       mutex_unlock(&cs->mutex);
+}
+
+static void if_unthrottle(struct tty_struct *tty)
+{
+       struct cardstate *cs = tty->driver_data;
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       mutex_lock(&cs->mutex);
+
+       if (!cs->connected)
+               gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
+       else
+               gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
+
+       mutex_unlock(&cs->mutex);
+}
+
+static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+       struct cardstate *cs = tty->driver_data;
+       unsigned int iflag;
+       unsigned int cflag;
+       unsigned int old_cflag;
+       unsigned int control_state, new_state;
+
+       gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+       mutex_lock(&cs->mutex);
+
+       if (!cs->connected) {
+               gig_dbg(DEBUG_IF, "not connected");
+               goto out;
+       }
+
+       iflag = tty->termios.c_iflag;
+       cflag = tty->termios.c_cflag;
+       old_cflag = old ? old->c_cflag : cflag;
+       gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
+               cs->minor_index, iflag, cflag, old_cflag);
+
+       /* get a local copy of the current port settings */
+       control_state = cs->control_state;
+
+       /*
+        * Update baud rate.
+        * Do not attempt to cache old rates and skip settings,
+        * disconnects screw such tricks up completely.
+        * Premature optimization is the root of all evil.
+        */
+
+       /* reassert DTR and (maybe) RTS on transition from B0 */
+       if ((old_cflag & CBAUD) == B0) {
+               new_state = control_state | TIOCM_DTR;
+               /* don't set RTS if using hardware flow control */
+               if (!(old_cflag & CRTSCTS))
+                       new_state |= TIOCM_RTS;
+               gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
+                       cs->minor_index,
+                       (new_state & TIOCM_RTS) ? " only" : "/RTS");
+               cs->ops->set_modem_ctrl(cs, control_state, new_state);
+               control_state = new_state;
+       }
+
+       cs->ops->baud_rate(cs, cflag & CBAUD);
+
+       if ((cflag & CBAUD) == B0) {
+               /* Drop RTS and DTR */
+               gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
+               new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
+               cs->ops->set_modem_ctrl(cs, control_state, new_state);
+               control_state = new_state;
+       }
+
+       /*
+        * Update line control register (LCR)
+        */
+
+       cs->ops->set_line_ctrl(cs, cflag);
+
+       /* save off the modified port settings */
+       cs->control_state = control_state;
+
+out:
+       mutex_unlock(&cs->mutex);
+}
+
+static const struct tty_operations if_ops = {
+       .open =                 if_open,
+       .close =                if_close,
+       .ioctl =                if_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl =         if_compat_ioctl,
+#endif
+       .write =                if_write,
+       .write_room =           if_write_room,
+       .chars_in_buffer =      if_chars_in_buffer,
+       .set_termios =          if_set_termios,
+       .throttle =             if_throttle,
+       .unthrottle =           if_unthrottle,
+       .tiocmget =             if_tiocmget,
+       .tiocmset =             if_tiocmset,
+};
+
+
+/* wakeup tasklet for the write operation */
+static void if_wake(unsigned long data)
+{
+       struct cardstate *cs = (struct cardstate *)data;
+
+       tty_port_tty_wakeup(&cs->port);
+}
+
+/*** interface to common ***/
+
+void gigaset_if_init(struct cardstate *cs)
+{
+       struct gigaset_driver *drv;
+
+       drv = cs->driver;
+       if (!drv->have_tty)
+               return;
+
+       tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
+
+       mutex_lock(&cs->mutex);
+       cs->tty_dev = tty_port_register_device(&cs->port, drv->tty,
+                       cs->minor_index, NULL);
+
+       if (!IS_ERR(cs->tty_dev))
+               dev_set_drvdata(cs->tty_dev, cs);
+       else {
+               pr_warning("could not register device to the tty subsystem\n");
+               cs->tty_dev = NULL;
+       }
+       mutex_unlock(&cs->mutex);
+}
+
+void gigaset_if_free(struct cardstate *cs)
+{
+       struct gigaset_driver *drv;
+
+       drv = cs->driver;
+       if (!drv->have_tty)
+               return;
+
+       tasklet_disable(&cs->if_wake_tasklet);
+       tasklet_kill(&cs->if_wake_tasklet);
+       cs->tty_dev = NULL;
+       tty_unregister_device(drv->tty, cs->minor_index);
+}
+
+/**
+ * gigaset_if_receive() - pass a received block of data to the tty device
+ * @cs:                device descriptor structure.
+ * @buffer:    received data.
+ * @len:       number of bytes received.
+ *
+ * Called by asyncdata/isocdata if a block of data received from the
+ * device must be sent to userspace through the ttyG* device.
+ */
+void gigaset_if_receive(struct cardstate *cs,
+                       unsigned char *buffer, size_t len)
+{
+       tty_insert_flip_string(&cs->port, buffer, len);
+       tty_flip_buffer_push(&cs->port);
+}
+EXPORT_SYMBOL_GPL(gigaset_if_receive);
+
+/* gigaset_if_initdriver
+ * Initialize tty interface.
+ * parameters:
+ *     drv             Driver
+ *     procname        Name of the driver (e.g. for /proc/tty/drivers)
+ *     devname         Name of the device files (prefix without minor number)
+ */
+void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
+                          const char *devname)
+{
+       int ret;
+       struct tty_driver *tty;
+
+       drv->have_tty = 0;
+
+       drv->tty = tty = alloc_tty_driver(drv->minors);
+       if (tty == NULL)
+               goto enomem;
+
+       tty->type =             TTY_DRIVER_TYPE_SERIAL;
+       tty->subtype =          SERIAL_TYPE_NORMAL;
+       tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+
+       tty->driver_name =      procname;
+       tty->name =             devname;
+       tty->minor_start =      drv->minor;
+
+       tty->init_termios          = tty_std_termios;
+       tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       tty_set_operations(tty, &if_ops);
+
+       ret = tty_register_driver(tty);
+       if (ret < 0) {
+               pr_err("error %d registering tty driver\n", ret);
+               goto error;
+       }
+       gig_dbg(DEBUG_IF, "tty driver initialized");
+       drv->have_tty = 1;
+       return;
+
+enomem:
+       pr_err("out of memory\n");
+error:
+       if (drv->tty)
+               put_tty_driver(drv->tty);
+}
+
+void gigaset_if_freedriver(struct gigaset_driver *drv)
+{
+       if (!drv->have_tty)
+               return;
+
+       drv->have_tty = 0;
+       tty_unregister_driver(drv->tty);
+       put_tty_driver(drv->tty);
+}
diff --git a/drivers/staging/isdn/gigaset/isocdata.c b/drivers/staging/isdn/gigaset/isocdata.c
new file mode 100644 (file)
index 0000000..f9264ba
--- /dev/null
@@ -0,0 +1,1009 @@
+/*
+ * Common data handling layer for bas_gigaset
+ *
+ * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
+ *                       Hansjoerg Lipp <hjlipp@web.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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/crc-ccitt.h>
+#include <linux/bitrev.h>
+
+/* access methods for isowbuf_t */
+/* ============================ */
+
+/* initialize buffer structure
+ */
+void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle)
+{
+       iwb->read = 0;
+       iwb->nextread = 0;
+       iwb->write = 0;
+       atomic_set(&iwb->writesem, 1);
+       iwb->wbits = 0;
+       iwb->idle = idle;
+       memset(iwb->data + BAS_OUTBUFSIZE, idle, BAS_OUTBUFPAD);
+}
+
+/* compute number of bytes which can be appended to buffer
+ * so that there is still room to append a maximum frame of flags
+ */
+static inline int isowbuf_freebytes(struct isowbuf_t *iwb)
+{
+       int read, write, freebytes;
+
+       read = iwb->read;
+       write = iwb->write;
+       freebytes = read - write;
+       if (freebytes > 0) {
+               /* no wraparound: need padding space within regular area */
+               return freebytes - BAS_OUTBUFPAD;
+       } else if (read < BAS_OUTBUFPAD) {
+               /* wraparound: can use space up to end of regular area */
+               return BAS_OUTBUFSIZE - write;
+       } else {
+               /* following the wraparound yields more space */
+               return freebytes + BAS_OUTBUFSIZE - BAS_OUTBUFPAD;
+       }
+}
+
+/* start writing
+ * acquire the write semaphore
+ * return 0 if acquired, <0 if busy
+ */
+static inline int isowbuf_startwrite(struct isowbuf_t *iwb)
+{
+       if (!atomic_dec_and_test(&iwb->writesem)) {
+               atomic_inc(&iwb->writesem);
+               gig_dbg(DEBUG_ISO, "%s: couldn't acquire iso write semaphore",
+                       __func__);
+               return -EBUSY;
+       }
+       gig_dbg(DEBUG_ISO,
+               "%s: acquired iso write semaphore, data[write]=%02x, nbits=%d",
+               __func__, iwb->data[iwb->write], iwb->wbits);
+       return 0;
+}
+
+/* finish writing
+ * release the write semaphore
+ * returns the current write position
+ */
+static inline int isowbuf_donewrite(struct isowbuf_t *iwb)
+{
+       int write = iwb->write;
+       atomic_inc(&iwb->writesem);
+       return write;
+}
+
+/* append bits to buffer without any checks
+ * - data contains bits to append, starting at LSB
+ * - nbits is number of bits to append (0..24)
+ * must be called with the write semaphore held
+ * If more than nbits bits are set in data, the extraneous bits are set in the
+ * buffer too, but the write position is only advanced by nbits.
+ */
+static inline void isowbuf_putbits(struct isowbuf_t *iwb, u32 data, int nbits)
+{
+       int write = iwb->write;
+       data <<= iwb->wbits;
+       data |= iwb->data[write];
+       nbits += iwb->wbits;
+       while (nbits >= 8) {
+               iwb->data[write++] = data & 0xff;
+               write %= BAS_OUTBUFSIZE;
+               data >>= 8;
+               nbits -= 8;
+       }
+       iwb->wbits = nbits;
+       iwb->data[write] = data & 0xff;
+       iwb->write = write;
+}
+
+/* put final flag on HDLC bitstream
+ * also sets the idle fill byte to the correspondingly shifted flag pattern
+ * must be called with the write semaphore held
+ */
+static inline void isowbuf_putflag(struct isowbuf_t *iwb)
+{
+       int write;
+
+       /* add two flags, thus reliably covering one byte */
+       isowbuf_putbits(iwb, 0x7e7e, 8);
+       /* recover the idle flag byte */
+       write = iwb->write;
+       iwb->idle = iwb->data[write];
+       gig_dbg(DEBUG_ISO, "idle fill byte %02x", iwb->idle);
+       /* mask extraneous bits in buffer */
+       iwb->data[write] &= (1 << iwb->wbits) - 1;
+}
+
+/* retrieve a block of bytes for sending
+ * The requested number of bytes is provided as a contiguous block.
+ * If necessary, the frame is filled to the requested number of bytes
+ * with the idle value.
+ * returns offset to frame, < 0 on busy or error
+ */
+int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size)
+{
+       int read, write, limit, src, dst;
+       unsigned char pbyte;
+
+       read = iwb->nextread;
+       write = iwb->write;
+       if (likely(read == write)) {
+               /* return idle frame */
+               return read < BAS_OUTBUFPAD ?
+                       BAS_OUTBUFSIZE : read - BAS_OUTBUFPAD;
+       }
+
+       limit = read + size;
+       gig_dbg(DEBUG_STREAM, "%s: read=%d write=%d limit=%d",
+               __func__, read, write, limit);
+#ifdef CONFIG_GIGASET_DEBUG
+       if (unlikely(size < 0 || size > BAS_OUTBUFPAD)) {
+               pr_err("invalid size %d\n", size);
+               return -EINVAL;
+       }
+#endif
+
+       if (read < write) {
+               /* no wraparound in valid data */
+               if (limit >= write) {
+                       /* append idle frame */
+                       if (isowbuf_startwrite(iwb) < 0)
+                               return -EBUSY;
+                       /* write position could have changed */
+                       write = iwb->write;
+                       if (limit >= write) {
+                               pbyte = iwb->data[write]; /* save
+                                                            partial byte */
+                               limit = write + BAS_OUTBUFPAD;
+                               gig_dbg(DEBUG_STREAM,
+                                       "%s: filling %d->%d with %02x",
+                                       __func__, write, limit, iwb->idle);
+                               if (write + BAS_OUTBUFPAD < BAS_OUTBUFSIZE)
+                                       memset(iwb->data + write, iwb->idle,
+                                              BAS_OUTBUFPAD);
+                               else {
+                                       /* wraparound, fill entire pad area */
+                                       memset(iwb->data + write, iwb->idle,
+                                              BAS_OUTBUFSIZE + BAS_OUTBUFPAD
+                                              - write);
+                                       limit = 0;
+                               }
+                               gig_dbg(DEBUG_STREAM,
+                                       "%s: restoring %02x at %d",
+                                       __func__, pbyte, limit);
+                               iwb->data[limit] = pbyte; /* restore
+                                                            partial byte */
+                               iwb->write = limit;
+                       }
+                       isowbuf_donewrite(iwb);
+               }
+       } else {
+               /* valid data wraparound */
+               if (limit >= BAS_OUTBUFSIZE) {
+                       /* copy wrapped part into pad area */
+                       src = 0;
+                       dst = BAS_OUTBUFSIZE;
+                       while (dst < limit && src < write)
+                               iwb->data[dst++] = iwb->data[src++];
+                       if (dst <= limit) {
+                               /* fill pad area with idle byte */
+                               memset(iwb->data + dst, iwb->idle,
+                                      BAS_OUTBUFSIZE + BAS_OUTBUFPAD - dst);
+                       }
+                       limit = src;
+               }
+       }
+       iwb->nextread = limit;
+       return read;
+}
+
+/* dump_bytes
+ * write hex bytes to syslog for debugging
+ */
+static inline void dump_bytes(enum debuglevel level, const char *tag,
+                             unsigned char *bytes, int count)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+       unsigned char c;
+       static char dbgline[3 * 32 + 1];
+       int i = 0;
+
+       if (!(gigaset_debuglevel & level))
+               return;
+
+       while (count-- > 0) {
+               if (i > sizeof(dbgline) - 4) {
+                       dbgline[i] = '\0';
+                       gig_dbg(level, "%s:%s", tag, dbgline);
+                       i = 0;
+               }
+               c = *bytes++;
+               dbgline[i] = (i && !(i % 12)) ? '-' : ' ';
+               i++;
+               dbgline[i++] = hex_asc_hi(c);
+               dbgline[i++] = hex_asc_lo(c);
+       }
+       dbgline[i] = '\0';
+       gig_dbg(level, "%s:%s", tag, dbgline);
+#endif
+}
+
+/*============================================================================*/
+
+/* bytewise HDLC bitstuffing via table lookup
+ * lookup table: 5 subtables for 0..4 preceding consecutive '1' bits
+ * index: 256*(number of preceding '1' bits) + (next byte to stuff)
+ * value: bit  9.. 0 = result bits
+ *        bit 12..10 = number of trailing '1' bits in result
+ *        bit 14..13 = number of bits added by stuffing
+ */
+static const u16 stufftab[5 * 256] = {
+/* previous 1s = 0: */
+       0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+       0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x201f,
+       0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x205f,
+       0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x209f,
+       0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20df,
+       0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f,
+       0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x251f,
+       0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af,
+       0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x255f,
+       0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x08c7, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x08cf,
+       0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x08d7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x299f,
+       0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x0cef,
+       0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x2ddf,
+
+/* previous 1s = 1: */
+       0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x200f,
+       0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x202f,
+       0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x204f,
+       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x206f,
+       0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x208f,
+       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x20af,
+       0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x20cf,
+       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20ef,
+       0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x250f,
+       0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x252f,
+       0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x254f,
+       0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x256f,
+       0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x08c7, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x298f,
+       0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x08d7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x29af,
+       0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dcf,
+       0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x31ef,
+
+/* previous 1s = 2: */
+       0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x2007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x2017,
+       0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x2027, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x2037,
+       0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x2047, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x2057,
+       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x2067, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x2077,
+       0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x2087, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x2097,
+       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x20a7, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x20b7,
+       0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x20c7, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x20d7,
+       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x20e7, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20f7,
+       0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x2507, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x2517,
+       0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x2527, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x2537,
+       0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x2547, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x2557,
+       0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x2567, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x2577,
+       0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x2987, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x2997,
+       0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x29a7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x29b7,
+       0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dc7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dd7,
+       0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x31e7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x41f7,
+
+/* previous 1s = 3: */
+       0x0000, 0x0001, 0x0002, 0x2003, 0x0004, 0x0005, 0x0006, 0x200b, 0x0008, 0x0009, 0x000a, 0x2013, 0x000c, 0x000d, 0x000e, 0x201b,
+       0x0010, 0x0011, 0x0012, 0x2023, 0x0014, 0x0015, 0x0016, 0x202b, 0x0018, 0x0019, 0x001a, 0x2033, 0x001c, 0x001d, 0x001e, 0x203b,
+       0x0020, 0x0021, 0x0022, 0x2043, 0x0024, 0x0025, 0x0026, 0x204b, 0x0028, 0x0029, 0x002a, 0x2053, 0x002c, 0x002d, 0x002e, 0x205b,
+       0x0030, 0x0031, 0x0032, 0x2063, 0x0034, 0x0035, 0x0036, 0x206b, 0x0038, 0x0039, 0x003a, 0x2073, 0x003c, 0x003d, 0x203e, 0x207b,
+       0x0040, 0x0041, 0x0042, 0x2083, 0x0044, 0x0045, 0x0046, 0x208b, 0x0048, 0x0049, 0x004a, 0x2093, 0x004c, 0x004d, 0x004e, 0x209b,
+       0x0050, 0x0051, 0x0052, 0x20a3, 0x0054, 0x0055, 0x0056, 0x20ab, 0x0058, 0x0059, 0x005a, 0x20b3, 0x005c, 0x005d, 0x005e, 0x20bb,
+       0x0060, 0x0061, 0x0062, 0x20c3, 0x0064, 0x0065, 0x0066, 0x20cb, 0x0068, 0x0069, 0x006a, 0x20d3, 0x006c, 0x006d, 0x006e, 0x20db,
+       0x0070, 0x0071, 0x0072, 0x20e3, 0x0074, 0x0075, 0x0076, 0x20eb, 0x0078, 0x0079, 0x007a, 0x20f3, 0x207c, 0x207d, 0x20be, 0x40fb,
+       0x0480, 0x0481, 0x0482, 0x2503, 0x0484, 0x0485, 0x0486, 0x250b, 0x0488, 0x0489, 0x048a, 0x2513, 0x048c, 0x048d, 0x048e, 0x251b,
+       0x0490, 0x0491, 0x0492, 0x2523, 0x0494, 0x0495, 0x0496, 0x252b, 0x0498, 0x0499, 0x049a, 0x2533, 0x049c, 0x049d, 0x049e, 0x253b,
+       0x04a0, 0x04a1, 0x04a2, 0x2543, 0x04a4, 0x04a5, 0x04a6, 0x254b, 0x04a8, 0x04a9, 0x04aa, 0x2553, 0x04ac, 0x04ad, 0x04ae, 0x255b,
+       0x04b0, 0x04b1, 0x04b2, 0x2563, 0x04b4, 0x04b5, 0x04b6, 0x256b, 0x04b8, 0x04b9, 0x04ba, 0x2573, 0x04bc, 0x04bd, 0x253e, 0x257b,
+       0x08c0, 0x08c1, 0x08c2, 0x2983, 0x08c4, 0x08c5, 0x08c6, 0x298b, 0x08c8, 0x08c9, 0x08ca, 0x2993, 0x08cc, 0x08cd, 0x08ce, 0x299b,
+       0x08d0, 0x08d1, 0x08d2, 0x29a3, 0x08d4, 0x08d5, 0x08d6, 0x29ab, 0x08d8, 0x08d9, 0x08da, 0x29b3, 0x08dc, 0x08dd, 0x08de, 0x29bb,
+       0x0ce0, 0x0ce1, 0x0ce2, 0x2dc3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dcb, 0x0ce8, 0x0ce9, 0x0cea, 0x2dd3, 0x0cec, 0x0ced, 0x0cee, 0x2ddb,
+       0x10f0, 0x10f1, 0x10f2, 0x31e3, 0x10f4, 0x10f5, 0x10f6, 0x31eb, 0x20f8, 0x20f9, 0x20fa, 0x41f3, 0x257c, 0x257d, 0x29be, 0x46fb,
+
+/* previous 1s = 4: */
+       0x0000, 0x2001, 0x0002, 0x2005, 0x0004, 0x2009, 0x0006, 0x200d, 0x0008, 0x2011, 0x000a, 0x2015, 0x000c, 0x2019, 0x000e, 0x201d,
+       0x0010, 0x2021, 0x0012, 0x2025, 0x0014, 0x2029, 0x0016, 0x202d, 0x0018, 0x2031, 0x001a, 0x2035, 0x001c, 0x2039, 0x001e, 0x203d,
+       0x0020, 0x2041, 0x0022, 0x2045, 0x0024, 0x2049, 0x0026, 0x204d, 0x0028, 0x2051, 0x002a, 0x2055, 0x002c, 0x2059, 0x002e, 0x205d,
+       0x0030, 0x2061, 0x0032, 0x2065, 0x0034, 0x2069, 0x0036, 0x206d, 0x0038, 0x2071, 0x003a, 0x2075, 0x003c, 0x2079, 0x203e, 0x407d,
+       0x0040, 0x2081, 0x0042, 0x2085, 0x0044, 0x2089, 0x0046, 0x208d, 0x0048, 0x2091, 0x004a, 0x2095, 0x004c, 0x2099, 0x004e, 0x209d,
+       0x0050, 0x20a1, 0x0052, 0x20a5, 0x0054, 0x20a9, 0x0056, 0x20ad, 0x0058, 0x20b1, 0x005a, 0x20b5, 0x005c, 0x20b9, 0x005e, 0x20bd,
+       0x0060, 0x20c1, 0x0062, 0x20c5, 0x0064, 0x20c9, 0x0066, 0x20cd, 0x0068, 0x20d1, 0x006a, 0x20d5, 0x006c, 0x20d9, 0x006e, 0x20dd,
+       0x0070, 0x20e1, 0x0072, 0x20e5, 0x0074, 0x20e9, 0x0076, 0x20ed, 0x0078, 0x20f1, 0x007a, 0x20f5, 0x207c, 0x40f9, 0x20be, 0x417d,
+       0x0480, 0x2501, 0x0482, 0x2505, 0x0484, 0x2509, 0x0486, 0x250d, 0x0488, 0x2511, 0x048a, 0x2515, 0x048c, 0x2519, 0x048e, 0x251d,
+       0x0490, 0x2521, 0x0492, 0x2525, 0x0494, 0x2529, 0x0496, 0x252d, 0x0498, 0x2531, 0x049a, 0x2535, 0x049c, 0x2539, 0x049e, 0x253d,
+       0x04a0, 0x2541, 0x04a2, 0x2545, 0x04a4, 0x2549, 0x04a6, 0x254d, 0x04a8, 0x2551, 0x04aa, 0x2555, 0x04ac, 0x2559, 0x04ae, 0x255d,
+       0x04b0, 0x2561, 0x04b2, 0x2565, 0x04b4, 0x2569, 0x04b6, 0x256d, 0x04b8, 0x2571, 0x04ba, 0x2575, 0x04bc, 0x2579, 0x253e, 0x467d,
+       0x08c0, 0x2981, 0x08c2, 0x2985, 0x08c4, 0x2989, 0x08c6, 0x298d, 0x08c8, 0x2991, 0x08ca, 0x2995, 0x08cc, 0x2999, 0x08ce, 0x299d,
+       0x08d0, 0x29a1, 0x08d2, 0x29a5, 0x08d4, 0x29a9, 0x08d6, 0x29ad, 0x08d8, 0x29b1, 0x08da, 0x29b5, 0x08dc, 0x29b9, 0x08de, 0x29bd,
+       0x0ce0, 0x2dc1, 0x0ce2, 0x2dc5, 0x0ce4, 0x2dc9, 0x0ce6, 0x2dcd, 0x0ce8, 0x2dd1, 0x0cea, 0x2dd5, 0x0cec, 0x2dd9, 0x0cee, 0x2ddd,
+       0x10f0, 0x31e1, 0x10f2, 0x31e5, 0x10f4, 0x31e9, 0x10f6, 0x31ed, 0x20f8, 0x41f1, 0x20fa, 0x41f5, 0x257c, 0x46f9, 0x29be, 0x4b7d
+};
+
+/* hdlc_bitstuff_byte
+ * perform HDLC bitstuffing for one input byte (8 bits, LSB first)
+ * parameters:
+ *     cin     input byte
+ *     ones    number of trailing '1' bits in result before this step
+ *     iwb     pointer to output buffer structure
+ *             (write semaphore must be held)
+ * return value:
+ *     number of trailing '1' bits in result after this step
+ */
+
+static inline int hdlc_bitstuff_byte(struct isowbuf_t *iwb, unsigned char cin,
+                                    int ones)
+{
+       u16 stuff;
+       int shiftinc, newones;
+
+       /* get stuffing information for input byte
+        * value: bit  9.. 0 = result bits
+        *        bit 12..10 = number of trailing '1' bits in result
+        *        bit 14..13 = number of bits added by stuffing
+        */
+       stuff = stufftab[256 * ones + cin];
+       shiftinc = (stuff >> 13) & 3;
+       newones = (stuff >> 10) & 7;
+       stuff &= 0x3ff;
+
+       /* append stuffed byte to output stream */
+       isowbuf_putbits(iwb, stuff, 8 + shiftinc);
+       return newones;
+}
+
+/* hdlc_buildframe
+ * Perform HDLC framing with bitstuffing on a byte buffer
+ * The input buffer is regarded as a sequence of bits, starting with the least
+ * significant bit of the first byte and ending with the most significant bit
+ * of the last byte. A 16 bit FCS is appended as defined by RFC 1662.
+ * Whenever five consecutive '1' bits appear in the resulting bit sequence, a
+ * '0' bit is inserted after them.
+ * The resulting bit string and a closing flag pattern (PPP_FLAG, '01111110')
+ * are appended to the output buffer starting at the given bit position, which
+ * is assumed to already contain a leading flag.
+ * The output buffer must have sufficient length; count + count/5 + 6 bytes
+ * starting at *out are safe and are verified to be present.
+ * parameters:
+ *     in      input buffer
+ *     count   number of bytes in input buffer
+ *     iwb     pointer to output buffer structure
+ *             (write semaphore must be held)
+ * return value:
+ *     position of end of packet in output buffer on success,
+ *     -EAGAIN if write semaphore busy or buffer full
+ */
+
+static inline int hdlc_buildframe(struct isowbuf_t *iwb,
+                                 unsigned char *in, int count)
+{
+       int ones;
+       u16 fcs;
+       int end;
+       unsigned char c;
+
+       if (isowbuf_freebytes(iwb) < count + count / 5 + 6 ||
+           isowbuf_startwrite(iwb) < 0) {
+               gig_dbg(DEBUG_ISO, "%s: %d bytes free -> -EAGAIN",
+                       __func__, isowbuf_freebytes(iwb));
+               return -EAGAIN;
+       }
+
+       dump_bytes(DEBUG_STREAM_DUMP, "snd data", in, count);
+
+       /* bitstuff and checksum input data */
+       fcs = PPP_INITFCS;
+       ones = 0;
+       while (count-- > 0) {
+               c = *in++;
+               ones = hdlc_bitstuff_byte(iwb, c, ones);
+               fcs = crc_ccitt_byte(fcs, c);
+       }
+
+       /* bitstuff and append FCS
+        * (complemented, least significant byte first) */
+       fcs ^= 0xffff;
+       ones = hdlc_bitstuff_byte(iwb, fcs & 0x00ff, ones);
+       ones = hdlc_bitstuff_byte(iwb, (fcs >> 8) & 0x00ff, ones);
+
+       /* put closing flag and repeat byte for flag idle */
+       isowbuf_putflag(iwb);
+       end = isowbuf_donewrite(iwb);
+       return end;
+}
+
+/* trans_buildframe
+ * Append a block of 'transparent' data to the output buffer,
+ * inverting the bytes.
+ * The output buffer must have sufficient length; count bytes
+ * starting at *out are safe and are verified to be present.
+ * parameters:
+ *     in      input buffer
+ *     count   number of bytes in input buffer
+ *     iwb     pointer to output buffer structure
+ *             (write semaphore must be held)
+ * return value:
+ *     position of end of packet in output buffer on success,
+ *     -EAGAIN if write semaphore busy or buffer full
+ */
+
+static inline int trans_buildframe(struct isowbuf_t *iwb,
+                                  unsigned char *in, int count)
+{
+       int write;
+       unsigned char c;
+
+       if (unlikely(count <= 0))
+               return iwb->write;
+
+       if (isowbuf_freebytes(iwb) < count ||
+           isowbuf_startwrite(iwb) < 0) {
+               gig_dbg(DEBUG_ISO, "can't put %d bytes", count);
+               return -EAGAIN;
+       }
+
+       gig_dbg(DEBUG_STREAM, "put %d bytes", count);
+       dump_bytes(DEBUG_STREAM_DUMP, "snd data", in, count);
+
+       write = iwb->write;
+       do {
+               c = bitrev8(*in++);
+               iwb->data[write++] = c;
+               write %= BAS_OUTBUFSIZE;
+       } while (--count > 0);
+       iwb->write = write;
+       iwb->idle = c;
+
+       return isowbuf_donewrite(iwb);
+}
+
+int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len)
+{
+       int result;
+
+       switch (bcs->proto2) {
+       case L2_HDLC:
+               result = hdlc_buildframe(bcs->hw.bas->isooutbuf, in, len);
+               gig_dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d",
+                       __func__, len, result);
+               break;
+       default:                        /* assume transparent */
+               result = trans_buildframe(bcs->hw.bas->isooutbuf, in, len);
+               gig_dbg(DEBUG_ISO, "%s: %d bytes trans -> %d",
+                       __func__, len, result);
+       }
+       return result;
+}
+
+/* hdlc_putbyte
+ * append byte c to current skb of B channel structure *bcs, updating fcs
+ */
+static inline void hdlc_putbyte(unsigned char c, struct bc_state *bcs)
+{
+       bcs->rx_fcs = crc_ccitt_byte(bcs->rx_fcs, c);
+       if (bcs->rx_skb == NULL)
+               /* skipping */
+               return;
+       if (bcs->rx_skb->len >= bcs->rx_bufsize) {
+               dev_warn(bcs->cs->dev, "received oversized packet discarded\n");
+               bcs->hw.bas->giants++;
+               dev_kfree_skb_any(bcs->rx_skb);
+               bcs->rx_skb = NULL;
+               return;
+       }
+       __skb_put_u8(bcs->rx_skb, c);
+}
+
+/* hdlc_flush
+ * drop partial HDLC data packet
+ */
+static inline void hdlc_flush(struct bc_state *bcs)
+{
+       /* clear skb or allocate new if not skipping */
+       if (bcs->rx_skb != NULL)
+               skb_trim(bcs->rx_skb, 0);
+       else
+               gigaset_new_rx_skb(bcs);
+
+       /* reset packet state */
+       bcs->rx_fcs = PPP_INITFCS;
+}
+
+/* hdlc_done
+ * process completed HDLC data packet
+ */
+static inline void hdlc_done(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+       struct sk_buff *procskb;
+       unsigned int len;
+
+       if (unlikely(bcs->ignore)) {
+               bcs->ignore--;
+               hdlc_flush(bcs);
+               return;
+       }
+       procskb = bcs->rx_skb;
+       if (procskb == NULL) {
+               /* previous error */
+               gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__);
+               gigaset_isdn_rcv_err(bcs);
+       } else if (procskb->len < 2) {
+               dev_notice(cs->dev, "received short frame (%d octets)\n",
+                          procskb->len);
+               bcs->hw.bas->runts++;
+               dev_kfree_skb_any(procskb);
+               gigaset_isdn_rcv_err(bcs);
+       } else if (bcs->rx_fcs != PPP_GOODFCS) {
+               dev_notice(cs->dev, "frame check error\n");
+               bcs->hw.bas->fcserrs++;
+               dev_kfree_skb_any(procskb);
+               gigaset_isdn_rcv_err(bcs);
+       } else {
+               len = procskb->len;
+               __skb_trim(procskb, len -= 2);  /* subtract FCS */
+               gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", __func__, len);
+               dump_bytes(DEBUG_STREAM_DUMP,
+                          "rcv data", procskb->data, len);
+               bcs->hw.bas->goodbytes += len;
+               gigaset_skb_rcvd(bcs, procskb);
+       }
+       gigaset_new_rx_skb(bcs);
+       bcs->rx_fcs = PPP_INITFCS;
+}
+
+/* hdlc_frag
+ * drop HDLC data packet with non-integral last byte
+ */
+static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits)
+{
+       if (unlikely(bcs->ignore)) {
+               bcs->ignore--;
+               hdlc_flush(bcs);
+               return;
+       }
+
+       dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits);
+       bcs->hw.bas->alignerrs++;
+       gigaset_isdn_rcv_err(bcs);
+       __skb_trim(bcs->rx_skb, 0);
+       bcs->rx_fcs = PPP_INITFCS;
+}
+
+/* bit counts lookup table for HDLC bit unstuffing
+ * index: input byte
+ * value: bit 0..3 = number of consecutive '1' bits starting from LSB
+ *        bit 4..6 = number of consecutive '1' bits starting from MSB
+ *                  (replacing 8 by 7 to make it fit; the algorithm won't care)
+ *        bit 7 set if there are 5 or more "interior" consecutive '1' bits
+ */
+static const unsigned char bitcounts[256] = {
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x80, 0x06,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+       0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x80, 0x81, 0x80, 0x07,
+       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x14,
+       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x15,
+       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x14,
+       0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x90, 0x16,
+       0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x24,
+       0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x25,
+       0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30, 0x34,
+       0x40, 0x41, 0x40, 0x42, 0x40, 0x41, 0x40, 0x43, 0x50, 0x51, 0x50, 0x52, 0x60, 0x61, 0x70, 0x78
+};
+
+/* hdlc_unpack
+ * perform HDLC frame processing (bit unstuffing, flag detection, FCS
+ * calculation) on a sequence of received data bytes (8 bits each, LSB first)
+ * pass on successfully received, complete frames as SKBs via gigaset_skb_rcvd
+ * notify of errors via gigaset_isdn_rcv_err
+ * tally frames, errors etc. in BC structure counters
+ * parameters:
+ *     src     received data
+ *     count   number of received bytes
+ *     bcs     receiving B channel structure
+ */
+static inline void hdlc_unpack(unsigned char *src, unsigned count,
+                              struct bc_state *bcs)
+{
+       struct bas_bc_state *ubc = bcs->hw.bas;
+       int inputstate;
+       unsigned seqlen, inbyte, inbits;
+
+       /* load previous state:
+        * inputstate = set of flag bits:
+        * - INS_flag_hunt: no complete opening flag received since connection
+        *                  setup or last abort
+        * - INS_have_data: at least one complete data byte received since last
+        *                  flag
+        * seqlen = number of consecutive '1' bits in last 7 input stream bits
+        *          (0..7)
+        * inbyte = accumulated partial data byte (if !INS_flag_hunt)
+        * inbits = number of valid bits in inbyte, starting at LSB (0..6)
+        */
+       inputstate = bcs->inputstate;
+       seqlen = ubc->seqlen;
+       inbyte = ubc->inbyte;
+       inbits = ubc->inbits;
+
+       /* bit unstuffing a byte a time
+        * Take your time to understand this; it's straightforward but tedious.
+        * The "bitcounts" lookup table is used to speed up the counting of
+        * leading and trailing '1' bits.
+        */
+       while (count--) {
+               unsigned char c = *src++;
+               unsigned char tabentry = bitcounts[c];
+               unsigned lead1 = tabentry & 0x0f;
+               unsigned trail1 = (tabentry >> 4) & 0x0f;
+
+               seqlen += lead1;
+
+               if (unlikely(inputstate & INS_flag_hunt)) {
+                       if (c == PPP_FLAG) {
+                               /* flag-in-one */
+                               inputstate &= ~(INS_flag_hunt | INS_have_data);
+                               inbyte = 0;
+                               inbits = 0;
+                       } else if (seqlen == 6 && trail1 != 7) {
+                               /* flag completed & not followed by abort */
+                               inputstate &= ~(INS_flag_hunt | INS_have_data);
+                               inbyte = c >> (lead1 + 1);
+                               inbits = 7 - lead1;
+                               if (trail1 >= 8) {
+                                       /* interior stuffing:
+                                        * omitting the MSB handles most cases,
+                                        * correct the incorrectly handled
+                                        * cases individually */
+                                       inbits--;
+                                       switch (c) {
+                                       case 0xbe:
+                                               inbyte = 0x3f;
+                                               break;
+                                       }
+                               }
+                       }
+                       /* else: continue flag-hunting */
+               } else if (likely(seqlen < 5 && trail1 < 7)) {
+                       /* streamlined case: 8 data bits, no stuffing */
+                       inbyte |= c << inbits;
+                       hdlc_putbyte(inbyte & 0xff, bcs);
+                       inputstate |= INS_have_data;
+                       inbyte >>= 8;
+                       /* inbits unchanged */
+               } else if (likely(seqlen == 6 && inbits == 7 - lead1 &&
+                                 trail1 + 1 == inbits &&
+                                 !(inputstate & INS_have_data))) {
+                       /* streamlined case: flag idle - state unchanged */
+               } else if (unlikely(seqlen > 6)) {
+                       /* abort sequence */
+                       ubc->aborts++;
+                       hdlc_flush(bcs);
+                       inputstate |= INS_flag_hunt;
+               } else if (seqlen == 6) {
+                       /* closing flag, including (6 - lead1) '1's
+                        * and one '0' from inbits */
+                       if (inbits > 7 - lead1) {
+                               hdlc_frag(bcs, inbits + lead1 - 7);
+                               inputstate &= ~INS_have_data;
+                       } else {
+                               if (inbits < 7 - lead1)
+                                       ubc->stolen0s++;
+                               if (inputstate & INS_have_data) {
+                                       hdlc_done(bcs);
+                                       inputstate &= ~INS_have_data;
+                               }
+                       }
+
+                       if (c == PPP_FLAG) {
+                               /* complete flag, LSB overlaps preceding flag */
+                               ubc->shared0s++;
+                               inbits = 0;
+                               inbyte = 0;
+                       } else if (trail1 != 7) {
+                               /* remaining bits */
+                               inbyte = c >> (lead1 + 1);
+                               inbits = 7 - lead1;
+                               if (trail1 >= 8) {
+                                       /* interior stuffing:
+                                        * omitting the MSB handles most cases,
+                                        * correct the incorrectly handled
+                                        * cases individually */
+                                       inbits--;
+                                       switch (c) {
+                                       case 0xbe:
+                                               inbyte = 0x3f;
+                                               break;
+                                       }
+                               }
+                       } else {
+                               /* abort sequence follows,
+                                * skb already empty anyway */
+                               ubc->aborts++;
+                               inputstate |= INS_flag_hunt;
+                       }
+               } else { /* (seqlen < 6) && (seqlen == 5 || trail1 >= 7) */
+
+                       if (c == PPP_FLAG) {
+                               /* complete flag */
+                               if (seqlen == 5)
+                                       ubc->stolen0s++;
+                               if (inbits) {
+                                       hdlc_frag(bcs, inbits);
+                                       inbits = 0;
+                                       inbyte = 0;
+                               } else if (inputstate & INS_have_data)
+                                       hdlc_done(bcs);
+                               inputstate &= ~INS_have_data;
+                       } else if (trail1 == 7) {
+                               /* abort sequence */
+                               ubc->aborts++;
+                               hdlc_flush(bcs);
+                               inputstate |= INS_flag_hunt;
+                       } else {
+                               /* stuffed data */
+                               if (trail1 < 7) { /* => seqlen == 5 */
+                                       /* stuff bit at position lead1,
+                                        * no interior stuffing */
+                                       unsigned char mask = (1 << lead1) - 1;
+                                       c = (c & mask) | ((c & ~mask) >> 1);
+                                       inbyte |= c << inbits;
+                                       inbits += 7;
+                               } else if (seqlen < 5) { /* trail1 >= 8 */
+                                       /* interior stuffing:
+                                        * omitting the MSB handles most cases,
+                                        * correct the incorrectly handled
+                                        * cases individually */
+                                       switch (c) {
+                                       case 0xbe:
+                                               c = 0x7e;
+                                               break;
+                                       }
+                                       inbyte |= c << inbits;
+                                       inbits += 7;
+                               } else { /* seqlen == 5 && trail1 >= 8 */
+
+                                       /* stuff bit at lead1 *and* interior
+                                        * stuffing -- unstuff individually */
+                                       switch (c) {
+                                       case 0x7d:
+                                               c = 0x3f;
+                                               break;
+                                       case 0xbe:
+                                               c = 0x3f;
+                                               break;
+                                       case 0x3e:
+                                               c = 0x1f;
+                                               break;
+                                       case 0x7c:
+                                               c = 0x3e;
+                                               break;
+                                       }
+                                       inbyte |= c << inbits;
+                                       inbits += 6;
+                               }
+                               if (inbits >= 8) {
+                                       inbits -= 8;
+                                       hdlc_putbyte(inbyte & 0xff, bcs);
+                                       inputstate |= INS_have_data;
+                                       inbyte >>= 8;
+                               }
+                       }
+               }
+               seqlen = trail1 & 7;
+       }
+
+       /* save new state */
+       bcs->inputstate = inputstate;
+       ubc->seqlen = seqlen;
+       ubc->inbyte = inbyte;
+       ubc->inbits = inbits;
+}
+
+/* trans_receive
+ * pass on received USB frame transparently as SKB via gigaset_skb_rcvd
+ * invert bytes
+ * tally frames, errors etc. in BC structure counters
+ * parameters:
+ *     src     received data
+ *     count   number of received bytes
+ *     bcs     receiving B channel structure
+ */
+static inline void trans_receive(unsigned char *src, unsigned count,
+                                struct bc_state *bcs)
+{
+       struct sk_buff *skb;
+       int dobytes;
+       unsigned char *dst;
+
+       if (unlikely(bcs->ignore)) {
+               bcs->ignore--;
+               return;
+       }
+       skb = bcs->rx_skb;
+       if (skb == NULL) {
+               skb = gigaset_new_rx_skb(bcs);
+               if (skb == NULL)
+                       return;
+       }
+       dobytes = bcs->rx_bufsize - skb->len;
+       while (count > 0) {
+               dst = skb_put(skb, count < dobytes ? count : dobytes);
+               while (count > 0 && dobytes > 0) {
+                       *dst++ = bitrev8(*src++);
+                       count--;
+                       dobytes--;
+               }
+               if (dobytes == 0) {
+                       dump_bytes(DEBUG_STREAM_DUMP,
+                                  "rcv data", skb->data, skb->len);
+                       bcs->hw.bas->goodbytes += skb->len;
+                       gigaset_skb_rcvd(bcs, skb);
+                       skb = gigaset_new_rx_skb(bcs);
+                       if (skb == NULL)
+                               return;
+                       dobytes = bcs->rx_bufsize;
+               }
+       }
+}
+
+void gigaset_isoc_receive(unsigned char *src, unsigned count,
+                         struct bc_state *bcs)
+{
+       switch (bcs->proto2) {
+       case L2_HDLC:
+               hdlc_unpack(src, count, bcs);
+               break;
+       default:                /* assume transparent */
+               trans_receive(src, count, bcs);
+       }
+}
+
+/* == data input =========================================================== */
+
+/* process a block of received bytes in command mode (mstate != MS_LOCKED)
+ * Append received bytes to the command response buffer and forward them
+ * line by line to the response handler.
+ * Note: Received lines may be terminated by CR, LF, or CR LF, which will be
+ * removed before passing the line to the response handler.
+ */
+static void cmd_loop(unsigned char *src, int numbytes, struct inbuf_t *inbuf)
+{
+       struct cardstate *cs = inbuf->cs;
+       unsigned cbytes      = cs->cbytes;
+       unsigned char c;
+
+       while (numbytes--) {
+               c = *src++;
+               switch (c) {
+               case '\n':
+                       if (cbytes == 0 && cs->respdata[0] == '\r') {
+                               /* collapse LF with preceding CR */
+                               cs->respdata[0] = 0;
+                               break;
+                       }
+                       /* fall through */
+               case '\r':
+                       /* end of message line, pass to response handler */
+                       if (cbytes >= MAX_RESP_SIZE) {
+                               dev_warn(cs->dev, "response too large (%d)\n",
+                                        cbytes);
+                               cbytes = MAX_RESP_SIZE;
+                       }
+                       cs->cbytes = cbytes;
+                       gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
+                                          cbytes, cs->respdata);
+                       gigaset_handle_modem_response(cs);
+                       cbytes = 0;
+
+                       /* store EOL byte for CRLF collapsing */
+                       cs->respdata[0] = c;
+                       break;
+               default:
+                       /* append to line buffer if possible */
+                       if (cbytes < MAX_RESP_SIZE)
+                               cs->respdata[cbytes] = c;
+                       cbytes++;
+               }
+       }
+
+       /* save state */
+       cs->cbytes = cbytes;
+}
+
+
+/* process a block of data received through the control channel
+ */
+void gigaset_isoc_input(struct inbuf_t *inbuf)
+{
+       struct cardstate *cs = inbuf->cs;
+       unsigned tail, head, numbytes;
+       unsigned char *src;
+
+       head = inbuf->head;
+       while (head != (tail = inbuf->tail)) {
+               gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
+               if (head > tail)
+                       tail = RBUFSIZE;
+               src = inbuf->data + head;
+               numbytes = tail - head;
+               gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
+
+               if (cs->mstate == MS_LOCKED) {
+                       gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
+                                          numbytes, src);
+                       gigaset_if_receive(inbuf->cs, src, numbytes);
+               } else {
+                       cmd_loop(src, numbytes, inbuf);
+               }
+
+               head += numbytes;
+               if (head == RBUFSIZE)
+                       head = 0;
+               gig_dbg(DEBUG_INTR, "setting head to %u", head);
+               inbuf->head = head;
+       }
+}
+
+
+/* == data output ========================================================== */
+
+/**
+ * gigaset_isoc_send_skb() - queue an skb for sending
+ * @bcs:       B channel descriptor structure.
+ * @skb:       data to send.
+ *
+ * Called by LL to queue an skb for sending, and start transmission if
+ * necessary.
+ * Once the payload data has been transmitted completely, gigaset_skb_sent()
+ * will be called with the skb's link layer header preserved.
+ *
+ * Return value:
+ *     number of bytes accepted for sending (skb->len) if ok,
+ *     error code < 0 (eg. -ENODEV) on error
+ */
+int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb)
+{
+       int len = skb->len;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bcs->cs->lock, flags);
+       if (!bcs->cs->connected) {
+               spin_unlock_irqrestore(&bcs->cs->lock, flags);
+               return -ENODEV;
+       }
+
+       skb_queue_tail(&bcs->squeue, skb);
+       gig_dbg(DEBUG_ISO, "%s: skb queued, qlen=%d",
+               __func__, skb_queue_len(&bcs->squeue));
+
+       /* tasklet submits URB if necessary */
+       tasklet_schedule(&bcs->hw.bas->sent_tasklet);
+       spin_unlock_irqrestore(&bcs->cs->lock, flags);
+
+       return len;     /* ok so far */
+}
diff --git a/drivers/staging/isdn/gigaset/proc.c b/drivers/staging/isdn/gigaset/proc.c
new file mode 100644 (file)
index 0000000..e3f9d0f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Stuff used by all variants of the driver
+ *
+ * Copyright (c) 2001 by Stefan Eilers,
+ *                       Hansjoerg Lipp <hjlipp@web.de>,
+ *                       Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ *     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 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+
+static ssize_t show_cidmode(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       struct cardstate *cs = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", cs->cidmode);
+}
+
+static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       struct cardstate *cs = dev_get_drvdata(dev);
+       long int value;
+       char *end;
+
+       value = simple_strtol(buf, &end, 0);
+       while (*end)
+               if (!isspace(*end++))
+                       return -EINVAL;
+       if (value < 0 || value > 1)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&cs->mutex))
+               return -ERESTARTSYS;
+
+       cs->waiting = 1;
+       if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE,
+                              NULL, value, NULL)) {
+               cs->waiting = 0;
+               mutex_unlock(&cs->mutex);
+               return -ENOMEM;
+       }
+       gigaset_schedule_event(cs);
+
+       wait_event(cs->waitqueue, !cs->waiting);
+
+       mutex_unlock(&cs->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(cidmode, S_IRUGO | S_IWUSR, show_cidmode, set_cidmode);
+
+/* free sysfs for device */
+void gigaset_free_dev_sysfs(struct cardstate *cs)
+{
+       if (!cs->tty_dev)
+               return;
+
+       gig_dbg(DEBUG_INIT, "removing sysfs entries");
+       device_remove_file(cs->tty_dev, &dev_attr_cidmode);
+}
+
+/* initialize sysfs for device */
+void gigaset_init_dev_sysfs(struct cardstate *cs)
+{
+       if (!cs->tty_dev)
+               return;
+
+       gig_dbg(DEBUG_INIT, "setting up sysfs");
+       if (device_create_file(cs->tty_dev, &dev_attr_cidmode))
+               pr_err("could not create sysfs attribute\n");
+}
diff --git a/drivers/staging/isdn/gigaset/ser-gigaset.c b/drivers/staging/isdn/gigaset/ser-gigaset.c
new file mode 100644 (file)
index 0000000..e1de8b1
--- /dev/null
@@ -0,0 +1,799 @@
+/* This is the serial hardware link layer (HLL) for the Gigaset 307x isdn
+ * DECT base (aka Sinus 45 isdn) using the RS232 DECT data module M101,
+ * written as a line discipline.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Tilman Schmidt"
+#define DRIVER_DESC "Serial Driver for Gigaset 307x using Siemens M101"
+
+#define GIGASET_MINORS     1
+#define GIGASET_MINOR      0
+#define GIGASET_MODULENAME "ser_gigaset"
+#define GIGASET_DEVNAME    "ttyGS"
+
+/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
+#define IF_WRITEBUF 264
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_GIGASET_M101);
+
+static int startmode = SM_ISDN;
+module_param(startmode, int, S_IRUGO);
+MODULE_PARM_DESC(startmode, "initial operation mode");
+static int cidmode = 1;
+module_param(cidmode, int, S_IRUGO);
+MODULE_PARM_DESC(cidmode, "stay in CID mode when idle");
+
+static struct gigaset_driver *driver;
+
+struct ser_cardstate {
+       struct platform_device  dev;
+       struct tty_struct       *tty;
+       atomic_t                refcnt;
+       struct completion       dead_cmp;
+};
+
+static struct platform_driver device_driver = {
+       .driver = {
+               .name = GIGASET_MODULENAME,
+       },
+};
+
+static void flush_send_queue(struct cardstate *);
+
+/* transmit data from current open skb
+ * result: number of bytes sent or error code < 0
+ */
+static int write_modem(struct cardstate *cs)
+{
+       struct tty_struct *tty = cs->hw.ser->tty;
+       struct bc_state *bcs = &cs->bcs[0];     /* only one channel */
+       struct sk_buff *skb = bcs->tx_skb;
+       int sent = -EOPNOTSUPP;
+
+       WARN_ON(!tty || !tty->ops || !skb);
+
+       if (!skb->len) {
+               dev_kfree_skb_any(skb);
+               bcs->tx_skb = NULL;
+               return -EINVAL;
+       }
+
+       set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+       if (tty->ops->write)
+               sent = tty->ops->write(tty, skb->data, skb->len);
+       gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent);
+       if (sent < 0) {
+               /* error */
+               flush_send_queue(cs);
+               return sent;
+       }
+       skb_pull(skb, sent);
+       if (!skb->len) {
+               /* skb sent completely */
+               gigaset_skb_sent(bcs, skb);
+
+               gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
+                       (unsigned long) skb);
+               dev_kfree_skb_any(skb);
+               bcs->tx_skb = NULL;
+       }
+       return sent;
+}
+
+/*
+ * transmit first queued command buffer
+ * result: number of bytes sent or error code < 0
+ */
+static int send_cb(struct cardstate *cs)
+{
+       struct tty_struct *tty = cs->hw.ser->tty;
+       struct cmdbuf_t *cb, *tcb;
+       unsigned long flags;
+       int sent = 0;
+
+       WARN_ON(!tty || !tty->ops);
+
+       cb = cs->cmdbuf;
+       if (!cb)
+               return 0;       /* nothing to do */
+
+       if (cb->len) {
+               set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+               sent = tty->ops->write(tty, cb->buf + cb->offset, cb->len);
+               if (sent < 0) {
+                       /* error */
+                       gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent);
+                       flush_send_queue(cs);
+                       return sent;
+               }
+               cb->offset += sent;
+               cb->len -= sent;
+               gig_dbg(DEBUG_OUTPUT, "send_cb: sent %d, left %u, queued %u",
+                       sent, cb->len, cs->cmdbytes);
+       }
+
+       while (cb && !cb->len) {
+               spin_lock_irqsave(&cs->cmdlock, flags);
+               cs->cmdbytes -= cs->curlen;
+               tcb = cb;
+               cs->cmdbuf = cb = cb->next;
+               if (cb) {
+                       cb->prev = NULL;
+                       cs->curlen = cb->len;
+               } else {
+                       cs->lastcmdbuf = NULL;
+                       cs->curlen = 0;
+               }
+               spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+               if (tcb->wake_tasklet)
+                       tasklet_schedule(tcb->wake_tasklet);
+               kfree(tcb);
+       }
+       return sent;
+}
+
+/*
+ * send queue tasklet
+ * If there is already a skb opened, put data to the transfer buffer
+ * by calling "write_modem".
+ * Otherwise take a new skb out of the queue.
+ */
+static void gigaset_modem_fill(unsigned long data)
+{
+       struct cardstate *cs = (struct cardstate *) data;
+       struct bc_state *bcs;
+       struct sk_buff *nextskb;
+       int sent = 0;
+
+       if (!cs) {
+               gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
+               return;
+       }
+       bcs = cs->bcs;
+       if (!bcs) {
+               gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
+               return;
+       }
+       if (!bcs->tx_skb) {
+               /* no skb is being sent; send command if any */
+               sent = send_cb(cs);
+               gig_dbg(DEBUG_OUTPUT, "%s: send_cb -> %d", __func__, sent);
+               if (sent)
+                       /* something sent or error */
+                       return;
+
+               /* no command to send; get skb */
+               nextskb = skb_dequeue(&bcs->squeue);
+               if (!nextskb)
+                       /* no skb either, nothing to do */
+                       return;
+               bcs->tx_skb = nextskb;
+
+               gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)",
+                       (unsigned long) bcs->tx_skb);
+       }
+
+       /* send skb */
+       gig_dbg(DEBUG_OUTPUT, "%s: tx_skb", __func__);
+       if (write_modem(cs) < 0)
+               gig_dbg(DEBUG_OUTPUT, "%s: write_modem failed", __func__);
+}
+
+/*
+ * throw away all data queued for sending
+ */
+static void flush_send_queue(struct cardstate *cs)
+{
+       struct sk_buff *skb;
+       struct cmdbuf_t *cb;
+       unsigned long flags;
+
+       /* command queue */
+       spin_lock_irqsave(&cs->cmdlock, flags);
+       while ((cb = cs->cmdbuf) != NULL) {
+               cs->cmdbuf = cb->next;
+               if (cb->wake_tasklet)
+                       tasklet_schedule(cb->wake_tasklet);
+               kfree(cb);
+       }
+       cs->cmdbuf = cs->lastcmdbuf = NULL;
+       cs->cmdbytes = cs->curlen = 0;
+       spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+       /* data queue */
+       if (cs->bcs->tx_skb)
+               dev_kfree_skb_any(cs->bcs->tx_skb);
+       while ((skb = skb_dequeue(&cs->bcs->squeue)) != NULL)
+               dev_kfree_skb_any(skb);
+}
+
+
+/* Gigaset Driver Interface */
+/* ======================== */
+
+/*
+ * queue an AT command string for transmission to the Gigaset device
+ * parameters:
+ *     cs              controller state structure
+ *     buf             buffer containing the string to send
+ *     len             number of characters to send
+ *     wake_tasklet    tasklet to run when transmission is complete, or NULL
+ * return value:
+ *     number of bytes queued, or error code < 0
+ */
+static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
+{
+       unsigned long flags;
+
+       gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
+                          DEBUG_TRANSCMD : DEBUG_LOCKCMD,
+                          "CMD Transmit", cb->len, cb->buf);
+
+       spin_lock_irqsave(&cs->cmdlock, flags);
+       cb->prev = cs->lastcmdbuf;
+       if (cs->lastcmdbuf)
+               cs->lastcmdbuf->next = cb;
+       else {
+               cs->cmdbuf = cb;
+               cs->curlen = cb->len;
+       }
+       cs->cmdbytes += cb->len;
+       cs->lastcmdbuf = cb;
+       spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (cs->connected)
+               tasklet_schedule(&cs->write_tasklet);
+       spin_unlock_irqrestore(&cs->lock, flags);
+       return cb->len;
+}
+
+/*
+ * tty_driver.write_room interface routine
+ * return number of characters the driver will accept to be written
+ * parameter:
+ *     controller state structure
+ * return value:
+ *     number of characters
+ */
+static int gigaset_write_room(struct cardstate *cs)
+{
+       unsigned bytes;
+
+       bytes = cs->cmdbytes;
+       return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
+}
+
+/*
+ * tty_driver.chars_in_buffer interface routine
+ * return number of characters waiting to be sent
+ * parameter:
+ *     controller state structure
+ * return value:
+ *     number of characters
+ */
+static int gigaset_chars_in_buffer(struct cardstate *cs)
+{
+       return cs->cmdbytes;
+}
+
+/*
+ * implementation of ioctl(GIGASET_BRKCHARS)
+ * parameter:
+ *     controller state structure
+ * return value:
+ *     -EINVAL (unimplemented function)
+ */
+static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
+{
+       /* not implemented */
+       return -EINVAL;
+}
+
+/*
+ * Open B channel
+ * Called by "do_action" in ev-layer.c
+ */
+static int gigaset_init_bchannel(struct bc_state *bcs)
+{
+       /* nothing to do for M10x */
+       gigaset_bchannel_up(bcs);
+       return 0;
+}
+
+/*
+ * Close B channel
+ * Called by "do_action" in ev-layer.c
+ */
+static int gigaset_close_bchannel(struct bc_state *bcs)
+{
+       /* nothing to do for M10x */
+       gigaset_bchannel_down(bcs);
+       return 0;
+}
+
+/*
+ * Set up B channel structure
+ * This is called by "gigaset_initcs" in common.c
+ */
+static int gigaset_initbcshw(struct bc_state *bcs)
+{
+       /* unused */
+       bcs->hw.ser = NULL;
+       return 0;
+}
+
+/*
+ * Free B channel structure
+ * Called by "gigaset_freebcs" in common.c
+ */
+static void gigaset_freebcshw(struct bc_state *bcs)
+{
+       /* unused */
+}
+
+/*
+ * Reinitialize B channel structure
+ * This is called by "bcs_reinit" in common.c
+ */
+static void gigaset_reinitbcshw(struct bc_state *bcs)
+{
+       /* nothing to do for M10x */
+}
+
+/*
+ * Free hardware specific device data
+ * This will be called by "gigaset_freecs" in common.c
+ */
+static void gigaset_freecshw(struct cardstate *cs)
+{
+       tasklet_kill(&cs->write_tasklet);
+       if (!cs->hw.ser)
+               return;
+       platform_device_unregister(&cs->hw.ser->dev);
+}
+
+static void gigaset_device_release(struct device *dev)
+{
+       kfree(container_of(dev, struct ser_cardstate, dev.dev));
+}
+
+/*
+ * Set up hardware specific device data
+ * This is called by "gigaset_initcs" in common.c
+ */
+static int gigaset_initcshw(struct cardstate *cs)
+{
+       int rc;
+       struct ser_cardstate *scs;
+
+       scs = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL);
+       if (!scs) {
+               pr_err("out of memory\n");
+               return -ENOMEM;
+       }
+       cs->hw.ser = scs;
+
+       cs->hw.ser->dev.name = GIGASET_MODULENAME;
+       cs->hw.ser->dev.id = cs->minor_index;
+       cs->hw.ser->dev.dev.release = gigaset_device_release;
+       rc = platform_device_register(&cs->hw.ser->dev);
+       if (rc != 0) {
+               pr_err("error %d registering platform device\n", rc);
+               kfree(cs->hw.ser);
+               cs->hw.ser = NULL;
+               return rc;
+       }
+
+       tasklet_init(&cs->write_tasklet,
+                    gigaset_modem_fill, (unsigned long) cs);
+       return 0;
+}
+
+/*
+ * set modem control lines
+ * Parameters:
+ *     card state structure
+ *     modem control line state ([TIOCM_DTR]|[TIOCM_RTS])
+ * Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c
+ * and by "if_lock" and "if_termios" in interface.c
+ */
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
+                                 unsigned new_state)
+{
+       struct tty_struct *tty = cs->hw.ser->tty;
+       unsigned int set, clear;
+
+       WARN_ON(!tty || !tty->ops);
+       /* tiocmset is an optional tty driver method */
+       if (!tty->ops->tiocmset)
+               return -EINVAL;
+       set = new_state & ~old_state;
+       clear = old_state & ~new_state;
+       if (!set && !clear)
+               return 0;
+       gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear);
+       return tty->ops->tiocmset(tty, set, clear);
+}
+
+static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
+{
+       return -EINVAL;
+}
+
+static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
+{
+       return -EINVAL;
+}
+
+static const struct gigaset_ops ops = {
+       .write_cmd = gigaset_write_cmd,
+       .write_room = gigaset_write_room,
+       .chars_in_buffer = gigaset_chars_in_buffer,
+       .brkchars = gigaset_brkchars,
+       .init_bchannel = gigaset_init_bchannel,
+       .close_bchannel = gigaset_close_bchannel,
+       .initbcshw = gigaset_initbcshw,
+       .freebcshw = gigaset_freebcshw,
+       .reinitbcshw = gigaset_reinitbcshw,
+       .initcshw = gigaset_initcshw,
+       .freecshw = gigaset_freecshw,
+       .set_modem_ctrl = gigaset_set_modem_ctrl,
+       .baud_rate = gigaset_baud_rate,
+       .set_line_ctrl = gigaset_set_line_ctrl,
+       .send_skb = gigaset_m10x_send_skb,      /* asyncdata.c */
+       .handle_input = gigaset_m10x_input,     /* asyncdata.c */
+};
+
+
+/* Line Discipline Interface */
+/* ========================= */
+
+/* helper functions for cardstate refcounting */
+static struct cardstate *cs_get(struct tty_struct *tty)
+{
+       struct cardstate *cs = tty->disc_data;
+
+       if (!cs || !cs->hw.ser) {
+               gig_dbg(DEBUG_ANY, "%s: no cardstate", __func__);
+               return NULL;
+       }
+       atomic_inc(&cs->hw.ser->refcnt);
+       return cs;
+}
+
+static void cs_put(struct cardstate *cs)
+{
+       if (atomic_dec_and_test(&cs->hw.ser->refcnt))
+               complete(&cs->hw.ser->dead_cmp);
+}
+
+/*
+ * Called by the tty driver when the line discipline is pushed onto the tty.
+ * Called in process context.
+ */
+static int
+gigaset_tty_open(struct tty_struct *tty)
+{
+       struct cardstate *cs;
+       int rc;
+
+       gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101");
+
+       pr_info(DRIVER_DESC "\n");
+
+       if (!driver) {
+               pr_err("%s: no driver structure\n", __func__);
+               return -ENODEV;
+       }
+
+       /* allocate memory for our device state and initialize it */
+       cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
+       if (!cs) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       cs->dev = &cs->hw.ser->dev.dev;
+       cs->hw.ser->tty = tty;
+       atomic_set(&cs->hw.ser->refcnt, 1);
+       init_completion(&cs->hw.ser->dead_cmp);
+       tty->disc_data = cs;
+
+       /* Set the amount of data we're willing to receive per call
+        * from the hardware driver to half of the input buffer size
+        * to leave some reserve.
+        * Note: We don't do flow control towards the hardware driver.
+        * If more data is received than will fit into the input buffer,
+        * it will be dropped and an error will be logged. This should
+        * never happen as the device is slow and the buffer size ample.
+        */
+       tty->receive_room = RBUFSIZE/2;
+
+       /* OK.. Initialization of the datastructures and the HW is done.. Now
+        * startup system and notify the LL that we are ready to run
+        */
+       if (startmode == SM_LOCKED)
+               cs->mstate = MS_LOCKED;
+       rc = gigaset_start(cs);
+       if (rc < 0) {
+               tasklet_kill(&cs->write_tasklet);
+               goto error;
+       }
+
+       gig_dbg(DEBUG_INIT, "Startup of HLL done");
+       return 0;
+
+error:
+       gig_dbg(DEBUG_INIT, "Startup of HLL failed");
+       tty->disc_data = NULL;
+       gigaset_freecs(cs);
+       return rc;
+}
+
+/*
+ * Called by the tty driver when the line discipline is removed.
+ * Called from process context.
+ */
+static void
+gigaset_tty_close(struct tty_struct *tty)
+{
+       struct cardstate *cs = tty->disc_data;
+
+       gig_dbg(DEBUG_INIT, "Stopping HLL for Gigaset M101");
+
+       if (!cs) {
+               gig_dbg(DEBUG_INIT, "%s: no cardstate", __func__);
+               return;
+       }
+
+       /* prevent other callers from entering ldisc methods */
+       tty->disc_data = NULL;
+
+       if (!cs->hw.ser)
+               pr_err("%s: no hw cardstate\n", __func__);
+       else {
+               /* wait for running methods to finish */
+               if (!atomic_dec_and_test(&cs->hw.ser->refcnt))
+                       wait_for_completion(&cs->hw.ser->dead_cmp);
+       }
+
+       /* stop operations */
+       gigaset_stop(cs);
+       tasklet_kill(&cs->write_tasklet);
+       flush_send_queue(cs);
+       cs->dev = NULL;
+       gigaset_freecs(cs);
+
+       gig_dbg(DEBUG_INIT, "Shutdown of HLL done");
+}
+
+/*
+ * Called by the tty driver when the tty line is hung up.
+ * Wait for I/O to driver to complete and unregister ISDN device.
+ * This is already done by the close routine, so just call that.
+ * Called from process context.
+ */
+static int gigaset_tty_hangup(struct tty_struct *tty)
+{
+       gigaset_tty_close(tty);
+       return 0;
+}
+
+/*
+ * Ioctl on the tty.
+ * Called in process context only.
+ * May be re-entered by multiple ioctl calling threads.
+ */
+static int
+gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
+                 unsigned int cmd, unsigned long arg)
+{
+       struct cardstate *cs = cs_get(tty);
+       int rc, val;
+       int __user *p = (int __user *)arg;
+
+       if (!cs)
+               return -ENXIO;
+
+       switch (cmd) {
+
+       case FIONREAD:
+               /* unused, always return zero */
+               val = 0;
+               rc = put_user(val, p);
+               break;
+
+       case TCFLSH:
+               /* flush our buffers and the serial port's buffer */
+               switch (arg) {
+               case TCIFLUSH:
+                       /* no own input buffer to flush */
+                       break;
+               case TCIOFLUSH:
+               case TCOFLUSH:
+                       flush_send_queue(cs);
+                       break;
+               }
+               /* fall through */
+
+       default:
+               /* pass through to underlying serial device */
+               rc = n_tty_ioctl_helper(tty, file, cmd, arg);
+               break;
+       }
+       cs_put(cs);
+       return rc;
+}
+
+/*
+ * Called by the tty driver when a block of data has been received.
+ * Will not be re-entered while running but other ldisc functions
+ * may be called in parallel.
+ * Can be called from hard interrupt level as well as soft interrupt
+ * level or mainline.
+ * Parameters:
+ *     tty     tty structure
+ *     buf     buffer containing received characters
+ *     cflags  buffer containing error flags for received characters (ignored)
+ *     count   number of received characters
+ */
+static void
+gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
+                   char *cflags, int count)
+{
+       struct cardstate *cs = cs_get(tty);
+       unsigned tail, head, n;
+       struct inbuf_t *inbuf;
+
+       if (!cs)
+               return;
+       inbuf = cs->inbuf;
+       if (!inbuf) {
+               dev_err(cs->dev, "%s: no inbuf\n", __func__);
+               cs_put(cs);
+               return;
+       }
+
+       tail = inbuf->tail;
+       head = inbuf->head;
+       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes",
+               head, tail, count);
+
+       if (head <= tail) {
+               /* possible buffer wraparound */
+               n = min_t(unsigned, count, RBUFSIZE - tail);
+               memcpy(inbuf->data + tail, buf, n);
+               tail = (tail + n) % RBUFSIZE;
+               buf += n;
+               count -= n;
+       }
+
+       if (count > 0) {
+               /* tail < head and some data left */
+               n = head - tail - 1;
+               if (count > n) {
+                       dev_err(cs->dev,
+                               "inbuf overflow, discarding %d bytes\n",
+                               count - n);
+                       count = n;
+               }
+               memcpy(inbuf->data + tail, buf, count);
+               tail += count;
+       }
+
+       gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
+       inbuf->tail = tail;
+
+       /* Everything was received .. Push data into handler */
+       gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+       gigaset_schedule_event(cs);
+       cs_put(cs);
+}
+
+/*
+ * Called by the tty driver when there's room for more data to send.
+ */
+static void
+gigaset_tty_wakeup(struct tty_struct *tty)
+{
+       struct cardstate *cs = cs_get(tty);
+
+       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+       if (!cs)
+               return;
+       tasklet_schedule(&cs->write_tasklet);
+       cs_put(cs);
+}
+
+static struct tty_ldisc_ops gigaset_ldisc = {
+       .owner          = THIS_MODULE,
+       .magic          = TTY_LDISC_MAGIC,
+       .name           = "ser_gigaset",
+       .open           = gigaset_tty_open,
+       .close          = gigaset_tty_close,
+       .hangup         = gigaset_tty_hangup,
+       .ioctl          = gigaset_tty_ioctl,
+       .receive_buf    = gigaset_tty_receive,
+       .write_wakeup   = gigaset_tty_wakeup,
+};
+
+
+/* Initialization / Shutdown */
+/* ========================= */
+
+static int __init ser_gigaset_init(void)
+{
+       int rc;
+
+       gig_dbg(DEBUG_INIT, "%s", __func__);
+       rc = platform_driver_register(&device_driver);
+       if (rc != 0) {
+               pr_err("error %d registering platform driver\n", rc);
+               return rc;
+       }
+
+       /* allocate memory for our driver state and initialize it */
+       driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+                                   GIGASET_MODULENAME, GIGASET_DEVNAME,
+                                   &ops, THIS_MODULE);
+       if (!driver) {
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
+       if (rc != 0) {
+               pr_err("error %d registering line discipline\n", rc);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       if (driver) {
+               gigaset_freedriver(driver);
+               driver = NULL;
+       }
+       platform_driver_unregister(&device_driver);
+       return rc;
+}
+
+static void __exit ser_gigaset_exit(void)
+{
+       int rc;
+
+       gig_dbg(DEBUG_INIT, "%s", __func__);
+
+       if (driver) {
+               gigaset_freedriver(driver);
+               driver = NULL;
+       }
+
+       rc = tty_unregister_ldisc(N_GIGASET_M101);
+       if (rc != 0)
+               pr_err("error %d unregistering line discipline\n", rc);
+
+       platform_driver_unregister(&device_driver);
+}
+
+module_init(ser_gigaset_init);
+module_exit(ser_gigaset_exit);
diff --git a/drivers/staging/isdn/gigaset/usb-gigaset.c b/drivers/staging/isdn/gigaset/usb-gigaset.c
new file mode 100644 (file)
index 0000000..eade36d
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ * USB driver for Gigaset 307x directly or using M105 Data.
+ *
+ * Copyright (c) 2001 by Stefan Eilers
+ *                   and Hansjoerg Lipp <hjlipp@web.de>.
+ *
+ * This driver was derived from the USB skeleton driver by
+ * Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * =====================================================================
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
+#define DRIVER_DESC "USB Driver for Gigaset 307x using M105"
+
+/* Module parameters */
+
+static int startmode = SM_ISDN;
+static int cidmode = 1;
+
+module_param(startmode, int, S_IRUGO);
+module_param(cidmode, int, S_IRUGO);
+MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
+MODULE_PARM_DESC(cidmode, "Call-ID mode");
+
+#define GIGASET_MINORS     1
+#define GIGASET_MINOR      8
+#define GIGASET_MODULENAME "usb_gigaset"
+#define GIGASET_DEVNAME    "ttyGU"
+
+/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
+#define IF_WRITEBUF 264
+
+/* Values for the Gigaset M105 Data */
+#define USB_M105_VENDOR_ID     0x0681
+#define USB_M105_PRODUCT_ID    0x0009
+
+/* table of devices that work with this driver */
+static const struct usb_device_id gigaset_table[] = {
+       { USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, gigaset_table);
+
+/*
+ * Control requests (empty fields: 00)
+ *
+ *       RT|RQ|VALUE|INDEX|LEN  |DATA
+ * In:
+ *       C1 08             01
+ *            Get flags (1 byte). Bits: 0=dtr,1=rts,3-7:?
+ *       C1 0F             ll ll
+ *            Get device information/status (llll: 0x200 and 0x40 seen).
+ *            Real size: I only saw MIN(llll,0x64).
+ *            Contents: seems to be always the same...
+ *              offset 0x00: Length of this structure (0x64) (len: 1,2,3 bytes)
+ *              offset 0x3c: String (16 bit chars): "MCCI USB Serial V2.0"
+ *              rest:        ?
+ * Out:
+ *       41 11
+ *            Initialize/reset device ?
+ *       41 00 xx 00
+ *            ? (xx=00 or 01; 01 on start, 00 on close)
+ *       41 07 vv mm
+ *            Set/clear flags vv=value, mm=mask (see RQ 08)
+ *       41 12 xx
+ *            Used before the following configuration requests are issued
+ *            (with xx=0x0f). I've seen other values<0xf, though.
+ *       41 01 xx xx
+ *            Set baud rate. xxxx=ceil(0x384000/rate)=trunc(0x383fff/rate)+1.
+ *       41 03 ps bb
+ *            Set byte size and parity. p:  0x20=even,0x10=odd,0x00=no parity
+ *                                     [    0x30: m, 0x40: s           ]
+ *                                     [s:  0: 1 stop bit; 1: 1.5; 2: 2]
+ *                                      bb: bits/byte (seen 7 and 8)
+ *       41 13 -- -- -- -- 10 00 ww 00 00 00 xx 00 00 00 yy 00 00 00 zz 00 00 00
+ *            ??
+ *            Initialization: 01, 40, 00, 00
+ *            Open device:    00  40, 00, 00
+ *            yy and zz seem to be equal, either 0x00 or 0x0a
+ *            (ww,xx) pairs seen: (00,00), (00,40), (01,40), (09,80), (19,80)
+ *       41 19 -- -- -- -- 06 00 00 00 00 xx 11 13
+ *            Used after every "configuration sequence" (RQ 12, RQs 01/03/13).
+ *            xx is usually 0x00 but was 0x7e before starting data transfer
+ *            in unimodem mode. So, this might be an array of characters that
+ *            need special treatment ("commit all bufferd data"?), 11=^Q, 13=^S.
+ *
+ * Unimodem mode: use "modprobe ppp_async flag_time=0" as the device _needs_ two
+ * flags per packet.
+ */
+
+/* functions called if a device of this driver is connected/disconnected */
+static int gigaset_probe(struct usb_interface *interface,
+                        const struct usb_device_id *id);
+static void gigaset_disconnect(struct usb_interface *interface);
+
+/* functions called before/after suspend */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
+static int gigaset_resume(struct usb_interface *intf);
+static int gigaset_pre_reset(struct usb_interface *intf);
+
+static struct gigaset_driver *driver;
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver gigaset_usb_driver = {
+       .name =         GIGASET_MODULENAME,
+       .probe =        gigaset_probe,
+       .disconnect =   gigaset_disconnect,
+       .id_table =     gigaset_table,
+       .suspend =      gigaset_suspend,
+       .resume =       gigaset_resume,
+       .reset_resume = gigaset_resume,
+       .pre_reset =    gigaset_pre_reset,
+       .post_reset =   gigaset_resume,
+       .disable_hub_initiated_lpm = 1,
+};
+
+struct usb_cardstate {
+       struct usb_device       *udev;          /* usb device pointer */
+       struct usb_interface    *interface;     /* interface for this device */
+       int                     busy;           /* bulk output in progress */
+
+       /* Output buffer */
+       unsigned char           *bulk_out_buffer;
+       int                     bulk_out_size;
+       int                     bulk_out_epnum;
+       struct urb              *bulk_out_urb;
+
+       /* Input buffer */
+       unsigned char           *rcvbuf;
+       int                     rcvbuf_size;
+       struct urb              *read_urb;
+
+       char                    bchars[6];              /* for request 0x19 */
+};
+
+static inline unsigned tiocm_to_gigaset(unsigned state)
+{
+       return ((state & TIOCM_DTR) ? 1 : 0) | ((state & TIOCM_RTS) ? 2 : 0);
+}
+
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
+                                 unsigned new_state)
+{
+       struct usb_device *udev = cs->hw.usb->udev;
+       unsigned mask, val;
+       int r;
+
+       mask = tiocm_to_gigaset(old_state ^ new_state);
+       val = tiocm_to_gigaset(new_state);
+
+       gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask);
+       r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41,
+                           (val & 0xff) | ((mask & 0xff) << 8), 0,
+                           NULL, 0, 2000 /* timeout? */);
+       if (r < 0)
+               return r;
+       return 0;
+}
+
+/*
+ * Set M105 configuration value
+ * using undocumented device commands reverse engineered from USB traces
+ * of the Siemens Windows driver
+ */
+static int set_value(struct cardstate *cs, u8 req, u16 val)
+{
+       struct usb_device *udev = cs->hw.usb->udev;
+       int r, r2;
+
+       gig_dbg(DEBUG_USBREQ, "request %02x (%04x)",
+               (unsigned)req, (unsigned)val);
+       r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x12, 0x41,
+                           0xf /*?*/, 0, NULL, 0, 2000 /*?*/);
+       /* no idea what this does */
+       if (r < 0) {
+               dev_err(&udev->dev, "error %d on request 0x12\n", -r);
+               return r;
+       }
+
+       r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), req, 0x41,
+                           val, 0, NULL, 0, 2000 /*?*/);
+       if (r < 0)
+               dev_err(&udev->dev, "error %d on request 0x%02x\n",
+                       -r, (unsigned)req);
+
+       r2 = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
+                            0, 0, cs->hw.usb->bchars, 6, 2000 /*?*/);
+       if (r2 < 0)
+               dev_err(&udev->dev, "error %d on request 0x19\n", -r2);
+
+       return r < 0 ? r : (r2 < 0 ? r2 : 0);
+}
+
+/*
+ * set the baud rate on the internal serial adapter
+ * using the undocumented parameter setting command
+ */
+static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
+{
+       u16 val;
+       u32 rate;
+
+       cflag &= CBAUD;
+
+       switch (cflag) {
+       case    B300: rate =     300; break;
+       case    B600: rate =     600; break;
+       case   B1200: rate =    1200; break;
+       case   B2400: rate =    2400; break;
+       case   B4800: rate =    4800; break;
+       case   B9600: rate =    9600; break;
+       case  B19200: rate =   19200; break;
+       case  B38400: rate =   38400; break;
+       case  B57600: rate =   57600; break;
+       case B115200: rate =  115200; break;
+       default:
+               rate =  9600;
+               dev_err(cs->dev, "unsupported baudrate request 0x%x,"
+                       " using default of B9600\n", cflag);
+       }
+
+       val = 0x383fff / rate + 1;
+
+       return set_value(cs, 1, val);
+}
+
+/*
+ * set the line format on the internal serial adapter
+ * using the undocumented parameter setting command
+ */
+static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
+{
+       u16 val = 0;
+
+       /* set the parity */
+       if (cflag & PARENB)
+               val |= (cflag & PARODD) ? 0x10 : 0x20;
+
+       /* set the number of data bits */
+       switch (cflag & CSIZE) {
+       case CS5:
+               val |= 5 << 8; break;
+       case CS6:
+               val |= 6 << 8; break;
+       case CS7:
+               val |= 7 << 8; break;
+       case CS8:
+               val |= 8 << 8; break;
+       default:
+               dev_err(cs->dev, "CSIZE was not CS5-CS8, using default of 8\n");
+               val |= 8 << 8;
+               break;
+       }
+
+       /* set the number of stop bits */
+       if (cflag & CSTOPB) {
+               if ((cflag & CSIZE) == CS5)
+                       val |= 1; /* 1.5 stop bits */
+               else
+                       val |= 2; /* 2 stop bits */
+       }
+
+       return set_value(cs, 3, val);
+}
+
+
+/*============================================================================*/
+static int gigaset_init_bchannel(struct bc_state *bcs)
+{
+       /* nothing to do for M10x */
+       gigaset_bchannel_up(bcs);
+       return 0;
+}
+
+static int gigaset_close_bchannel(struct bc_state *bcs)
+{
+       /* nothing to do for M10x */
+       gigaset_bchannel_down(bcs);
+       return 0;
+}
+
+static int write_modem(struct cardstate *cs);
+static int send_cb(struct cardstate *cs);
+
+
+/* Write tasklet handler: Continue sending current skb, or send command, or
+ * start sending an skb from the send queue.
+ */
+static void gigaset_modem_fill(unsigned long data)
+{
+       struct cardstate *cs = (struct cardstate *) data;
+       struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
+
+       gig_dbg(DEBUG_OUTPUT, "modem_fill");
+
+       if (cs->hw.usb->busy) {
+               gig_dbg(DEBUG_OUTPUT, "modem_fill: busy");
+               return;
+       }
+
+again:
+       if (!bcs->tx_skb) {     /* no skb is being sent */
+               if (cs->cmdbuf) {       /* commands to send? */
+                       gig_dbg(DEBUG_OUTPUT, "modem_fill: cb");
+                       if (send_cb(cs) < 0) {
+                               gig_dbg(DEBUG_OUTPUT,
+                                       "modem_fill: send_cb failed");
+                               goto again; /* no callback will be called! */
+                       }
+                       return;
+               }
+
+               /* skbs to send? */
+               bcs->tx_skb = skb_dequeue(&bcs->squeue);
+               if (!bcs->tx_skb)
+                       return;
+
+               gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)!",
+                       (unsigned long) bcs->tx_skb);
+       }
+
+       gig_dbg(DEBUG_OUTPUT, "modem_fill: tx_skb");
+       if (write_modem(cs) < 0) {
+               gig_dbg(DEBUG_OUTPUT, "modem_fill: write_modem failed");
+               goto again;     /* no callback will be called! */
+       }
+}
+
+/*
+ * Interrupt Input URB completion routine
+ */
+static void gigaset_read_int_callback(struct urb *urb)
+{
+       struct cardstate *cs = urb->context;
+       struct inbuf_t *inbuf = cs->inbuf;
+       int status = urb->status;
+       int r;
+       unsigned numbytes;
+       unsigned char *src;
+       unsigned long flags;
+
+       if (!status) {
+               numbytes = urb->actual_length;
+
+               if (numbytes) {
+                       src = cs->hw.usb->rcvbuf;
+                       if (unlikely(*src))
+                               dev_warn(cs->dev,
+                                        "%s: There was no leading 0, but 0x%02x!\n",
+                                        __func__, (unsigned) *src);
+                       ++src; /* skip leading 0x00 */
+                       --numbytes;
+                       if (gigaset_fill_inbuf(inbuf, src, numbytes)) {
+                               gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+                               gigaset_schedule_event(inbuf->cs);
+                       }
+               } else
+                       gig_dbg(DEBUG_INTR, "Received zero block length");
+       } else {
+               /* The urb might have been killed. */
+               gig_dbg(DEBUG_ANY, "%s - nonzero status received: %d",
+                       __func__, status);
+               if (status == -ENOENT || status == -ESHUTDOWN)
+                       /* killed or endpoint shutdown: don't resubmit */
+                       return;
+       }
+
+       /* resubmit URB */
+       spin_lock_irqsave(&cs->lock, flags);
+       if (!cs->connected) {
+               spin_unlock_irqrestore(&cs->lock, flags);
+               pr_err("%s: disconnected\n", __func__);
+               return;
+       }
+       r = usb_submit_urb(urb, GFP_ATOMIC);
+       spin_unlock_irqrestore(&cs->lock, flags);
+       if (r)
+               dev_err(cs->dev, "error %d resubmitting URB\n", -r);
+}
+
+
+/* This callback routine is called when data was transmitted to the device. */
+static void gigaset_write_bulk_callback(struct urb *urb)
+{
+       struct cardstate *cs = urb->context;
+       int status = urb->status;
+       unsigned long flags;
+
+       switch (status) {
+       case 0:                 /* normal completion */
+               break;
+       case -ENOENT:           /* killed */
+               gig_dbg(DEBUG_ANY, "%s: killed", __func__);
+               cs->hw.usb->busy = 0;
+               return;
+       default:
+               dev_err(cs->dev, "bulk transfer failed (status %d)\n",
+                       -status);
+               /* That's all we can do. Communication problems
+                  are handled by timeouts or network protocols. */
+       }
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (!cs->connected) {
+               pr_err("%s: disconnected\n", __func__);
+       } else {
+               cs->hw.usb->busy = 0;
+               tasklet_schedule(&cs->write_tasklet);
+       }
+       spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+static int send_cb(struct cardstate *cs)
+{
+       struct cmdbuf_t *cb = cs->cmdbuf;
+       unsigned long flags;
+       int count;
+       int status = -ENOENT;
+       struct usb_cardstate *ucs = cs->hw.usb;
+
+       do {
+               if (!cb->len) {
+                       spin_lock_irqsave(&cs->cmdlock, flags);
+                       cs->cmdbytes -= cs->curlen;
+                       gig_dbg(DEBUG_OUTPUT, "send_cb: sent %u bytes, %u left",
+                               cs->curlen, cs->cmdbytes);
+                       cs->cmdbuf = cb->next;
+                       if (cs->cmdbuf) {
+                               cs->cmdbuf->prev = NULL;
+                               cs->curlen = cs->cmdbuf->len;
+                       } else {
+                               cs->lastcmdbuf = NULL;
+                               cs->curlen = 0;
+                       }
+                       spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+                       if (cb->wake_tasklet)
+                               tasklet_schedule(cb->wake_tasklet);
+                       kfree(cb);
+
+                       cb = cs->cmdbuf;
+               }
+
+               if (cb) {
+                       count = min(cb->len, ucs->bulk_out_size);
+                       gig_dbg(DEBUG_OUTPUT, "send_cb: send %d bytes", count);
+
+                       usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
+                                         usb_sndbulkpipe(ucs->udev,
+                                                         ucs->bulk_out_epnum),
+                                         cb->buf + cb->offset, count,
+                                         gigaset_write_bulk_callback, cs);
+
+                       cb->offset += count;
+                       cb->len -= count;
+                       ucs->busy = 1;
+
+                       spin_lock_irqsave(&cs->lock, flags);
+                       status = cs->connected ?
+                               usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) :
+                               -ENODEV;
+                       spin_unlock_irqrestore(&cs->lock, flags);
+
+                       if (status) {
+                               ucs->busy = 0;
+                               dev_err(cs->dev,
+                                       "could not submit urb (error %d)\n",
+                                       -status);
+                               cb->len = 0; /* skip urb => remove cb+wakeup
+                                               in next loop cycle */
+                       }
+               }
+       } while (cb && status); /* next command on error */
+
+       return status;
+}
+
+/* Send command to device. */
+static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
+{
+       unsigned long flags;
+       int len;
+
+       gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
+                          DEBUG_TRANSCMD : DEBUG_LOCKCMD,
+                          "CMD Transmit", cb->len, cb->buf);
+
+       spin_lock_irqsave(&cs->cmdlock, flags);
+       cb->prev = cs->lastcmdbuf;
+       if (cs->lastcmdbuf)
+               cs->lastcmdbuf->next = cb;
+       else {
+               cs->cmdbuf = cb;
+               cs->curlen = cb->len;
+       }
+       cs->cmdbytes += cb->len;
+       cs->lastcmdbuf = cb;
+       spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+       spin_lock_irqsave(&cs->lock, flags);
+       len = cb->len;
+       if (cs->connected)
+               tasklet_schedule(&cs->write_tasklet);
+       spin_unlock_irqrestore(&cs->lock, flags);
+       return len;
+}
+
+static int gigaset_write_room(struct cardstate *cs)
+{
+       unsigned bytes;
+
+       bytes = cs->cmdbytes;
+       return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
+}
+
+static int gigaset_chars_in_buffer(struct cardstate *cs)
+{
+       return cs->cmdbytes;
+}
+
+/*
+ * set the break characters on the internal serial adapter
+ * using undocumented device commands reverse engineered from USB traces
+ * of the Siemens Windows driver
+ */
+static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
+{
+       struct usb_device *udev = cs->hw.usb->udev;
+
+       gigaset_dbg_buffer(DEBUG_USBREQ, "brkchars", 6, buf);
+       memcpy(cs->hw.usb->bchars, buf, 6);
+       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
+                              0, 0, &buf, 6, 2000);
+}
+
+static void gigaset_freebcshw(struct bc_state *bcs)
+{
+       /* unused */
+}
+
+/* Initialize the b-channel structure */
+static int gigaset_initbcshw(struct bc_state *bcs)
+{
+       /* unused */
+       bcs->hw.usb = NULL;
+       return 0;
+}
+
+static void gigaset_reinitbcshw(struct bc_state *bcs)
+{
+       /* nothing to do for M10x */
+}
+
+static void gigaset_freecshw(struct cardstate *cs)
+{
+       tasklet_kill(&cs->write_tasklet);
+       kfree(cs->hw.usb);
+}
+
+static int gigaset_initcshw(struct cardstate *cs)
+{
+       struct usb_cardstate *ucs;
+
+       cs->hw.usb = ucs =
+               kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
+       if (!ucs) {
+               pr_err("out of memory\n");
+               return -ENOMEM;
+       }
+
+       ucs->bchars[0] = 0;
+       ucs->bchars[1] = 0;
+       ucs->bchars[2] = 0;
+       ucs->bchars[3] = 0;
+       ucs->bchars[4] = 0x11;
+       ucs->bchars[5] = 0x13;
+       ucs->bulk_out_buffer = NULL;
+       ucs->bulk_out_urb = NULL;
+       ucs->read_urb = NULL;
+       tasklet_init(&cs->write_tasklet,
+                    gigaset_modem_fill, (unsigned long) cs);
+
+       return 0;
+}
+
+/* Send data from current skb to the device. */
+static int write_modem(struct cardstate *cs)
+{
+       int ret = 0;
+       int count;
+       struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
+       struct usb_cardstate *ucs = cs->hw.usb;
+       unsigned long flags;
+
+       gig_dbg(DEBUG_OUTPUT, "len: %d...", bcs->tx_skb->len);
+
+       if (!bcs->tx_skb->len) {
+               dev_kfree_skb_any(bcs->tx_skb);
+               bcs->tx_skb = NULL;
+               return -EINVAL;
+       }
+
+       /* Copy data to bulk out buffer and transmit data */
+       count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
+       skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
+       skb_pull(bcs->tx_skb, count);
+       ucs->busy = 1;
+       gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count);
+
+       spin_lock_irqsave(&cs->lock, flags);
+       if (cs->connected) {
+               usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
+                                 usb_sndbulkpipe(ucs->udev,
+                                                 ucs->bulk_out_epnum),
+                                 ucs->bulk_out_buffer, count,
+                                 gigaset_write_bulk_callback, cs);
+               ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC);
+       } else {
+               ret = -ENODEV;
+       }
+       spin_unlock_irqrestore(&cs->lock, flags);
+
+       if (ret) {
+               dev_err(cs->dev, "could not submit urb (error %d)\n", -ret);
+               ucs->busy = 0;
+       }
+
+       if (!bcs->tx_skb->len) {
+               /* skb sent completely */
+               gigaset_skb_sent(bcs, bcs->tx_skb);
+
+               gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
+                       (unsigned long) bcs->tx_skb);
+               dev_kfree_skb_any(bcs->tx_skb);
+               bcs->tx_skb = NULL;
+       }
+
+       return ret;
+}
+
+static int gigaset_probe(struct usb_interface *interface,
+                        const struct usb_device_id *id)
+{
+       int retval;
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct usb_host_interface *hostif = interface->cur_altsetting;
+       struct cardstate *cs = NULL;
+       struct usb_cardstate *ucs = NULL;
+       struct usb_endpoint_descriptor *endpoint;
+       int buffer_size;
+
+       gig_dbg(DEBUG_ANY, "%s: Check if device matches ...", __func__);
+
+       /* See if the device offered us matches what we can accept */
+       if ((le16_to_cpu(udev->descriptor.idVendor)  != USB_M105_VENDOR_ID) ||
+           (le16_to_cpu(udev->descriptor.idProduct) != USB_M105_PRODUCT_ID)) {
+               gig_dbg(DEBUG_ANY, "device ID (0x%x, 0x%x) not for me - skip",
+                       le16_to_cpu(udev->descriptor.idVendor),
+                       le16_to_cpu(udev->descriptor.idProduct));
+               return -ENODEV;
+       }
+       if (hostif->desc.bInterfaceNumber != 0) {
+               gig_dbg(DEBUG_ANY, "interface %d not for me - skip",
+                       hostif->desc.bInterfaceNumber);
+               return -ENODEV;
+       }
+       if (hostif->desc.bAlternateSetting != 0) {
+               dev_notice(&udev->dev, "unsupported altsetting %d - skip",
+                          hostif->desc.bAlternateSetting);
+               return -ENODEV;
+       }
+       if (hostif->desc.bInterfaceClass != 255) {
+               dev_notice(&udev->dev, "unsupported interface class %d - skip",
+                          hostif->desc.bInterfaceClass);
+               return -ENODEV;
+       }
+
+       dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);
+
+       /* allocate memory for our device state and initialize it */
+       cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
+       if (!cs)
+               return -ENODEV;
+       ucs = cs->hw.usb;
+
+       /* save off device structure ptrs for later use */
+       usb_get_dev(udev);
+       ucs->udev = udev;
+       ucs->interface = interface;
+       cs->dev = &interface->dev;
+
+       /* save address of controller structure */
+       usb_set_intfdata(interface, cs);
+
+       endpoint = &hostif->endpoint[0].desc;
+
+       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+       ucs->bulk_out_size = buffer_size;
+       ucs->bulk_out_epnum = usb_endpoint_num(endpoint);
+       ucs->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
+       if (!ucs->bulk_out_buffer) {
+               dev_err(cs->dev, "Couldn't allocate bulk_out_buffer\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       ucs->bulk_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!ucs->bulk_out_urb) {
+               dev_err(cs->dev, "Couldn't allocate bulk_out_urb\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       endpoint = &hostif->endpoint[1].desc;
+
+       ucs->busy = 0;
+
+       ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!ucs->read_urb) {
+               dev_err(cs->dev, "No free urbs available\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+       ucs->rcvbuf_size = buffer_size;
+       ucs->rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
+       if (!ucs->rcvbuf) {
+               dev_err(cs->dev, "Couldn't allocate rcvbuf\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+       /* Fill the interrupt urb and send it to the core */
+       usb_fill_int_urb(ucs->read_urb, udev,
+                        usb_rcvintpipe(udev, usb_endpoint_num(endpoint)),
+                        ucs->rcvbuf, buffer_size,
+                        gigaset_read_int_callback,
+                        cs, endpoint->bInterval);
+
+       retval = usb_submit_urb(ucs->read_urb, GFP_KERNEL);
+       if (retval) {
+               dev_err(cs->dev, "Could not submit URB (error %d)\n", -retval);
+               goto error;
+       }
+
+       /* tell common part that the device is ready */
+       if (startmode == SM_LOCKED)
+               cs->mstate = MS_LOCKED;
+
+       retval = gigaset_start(cs);
+       if (retval < 0) {
+               tasklet_kill(&cs->write_tasklet);
+               goto error;
+       }
+       return 0;
+
+error:
+       usb_kill_urb(ucs->read_urb);
+       kfree(ucs->bulk_out_buffer);
+       usb_free_urb(ucs->bulk_out_urb);
+       kfree(ucs->rcvbuf);
+       usb_free_urb(ucs->read_urb);
+       usb_set_intfdata(interface, NULL);
+       ucs->read_urb = ucs->bulk_out_urb = NULL;
+       ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
+       usb_put_dev(ucs->udev);
+       ucs->udev = NULL;
+       ucs->interface = NULL;
+       gigaset_freecs(cs);
+       return retval;
+}
+
+static void gigaset_disconnect(struct usb_interface *interface)
+{
+       struct cardstate *cs;
+       struct usb_cardstate *ucs;
+
+       cs = usb_get_intfdata(interface);
+       ucs = cs->hw.usb;
+
+       dev_info(cs->dev, "disconnecting Gigaset USB adapter\n");
+
+       usb_kill_urb(ucs->read_urb);
+
+       gigaset_stop(cs);
+
+       usb_set_intfdata(interface, NULL);
+       tasklet_kill(&cs->write_tasklet);
+
+       usb_kill_urb(ucs->bulk_out_urb);
+
+       kfree(ucs->bulk_out_buffer);
+       usb_free_urb(ucs->bulk_out_urb);
+       kfree(ucs->rcvbuf);
+       usb_free_urb(ucs->read_urb);
+       ucs->read_urb = ucs->bulk_out_urb = NULL;
+       ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
+
+       usb_put_dev(ucs->udev);
+       ucs->interface = NULL;
+       ucs->udev = NULL;
+       cs->dev = NULL;
+       gigaset_freecs(cs);
+}
+
+/* gigaset_suspend
+ * This function is called before the USB connection is suspended or reset.
+ */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct cardstate *cs = usb_get_intfdata(intf);
+
+       /* stop activity */
+       cs->connected = 0;      /* prevent rescheduling */
+       usb_kill_urb(cs->hw.usb->read_urb);
+       tasklet_kill(&cs->write_tasklet);
+       usb_kill_urb(cs->hw.usb->bulk_out_urb);
+
+       gig_dbg(DEBUG_SUSPEND, "suspend complete");
+       return 0;
+}
+
+/* gigaset_resume
+ * This function is called after the USB connection has been resumed or reset.
+ */
+static int gigaset_resume(struct usb_interface *intf)
+{
+       struct cardstate *cs = usb_get_intfdata(intf);
+       int rc;
+
+       /* resubmit interrupt URB */
+       cs->connected = 1;
+       rc = usb_submit_urb(cs->hw.usb->read_urb, GFP_KERNEL);
+       if (rc) {
+               dev_err(cs->dev, "Could not submit read URB (error %d)\n", -rc);
+               return rc;
+       }
+
+       gig_dbg(DEBUG_SUSPEND, "resume complete");
+       return 0;
+}
+
+/* gigaset_pre_reset
+ * This function is called before the USB connection is reset.
+ */
+static int gigaset_pre_reset(struct usb_interface *intf)
+{
+       /* same as suspend */
+       return gigaset_suspend(intf, PMSG_ON);
+}
+
+static const struct gigaset_ops ops = {
+       .write_cmd = gigaset_write_cmd,
+       .write_room = gigaset_write_room,
+       .chars_in_buffer = gigaset_chars_in_buffer,
+       .brkchars = gigaset_brkchars,
+       .init_bchannel = gigaset_init_bchannel,
+       .close_bchannel = gigaset_close_bchannel,
+       .initbcshw = gigaset_initbcshw,
+       .freebcshw = gigaset_freebcshw,
+       .reinitbcshw = gigaset_reinitbcshw,
+       .initcshw = gigaset_initcshw,
+       .freecshw = gigaset_freecshw,
+       .set_modem_ctrl = gigaset_set_modem_ctrl,
+       .baud_rate = gigaset_baud_rate,
+       .set_line_ctrl = gigaset_set_line_ctrl,
+       .send_skb = gigaset_m10x_send_skb,
+       .handle_input = gigaset_m10x_input,
+};
+
+/*
+ * This function is called while kernel-module is loaded
+ */
+static int __init usb_gigaset_init(void)
+{
+       int result;
+
+       /* allocate memory for our driver state and initialize it */
+       driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+                                   GIGASET_MODULENAME, GIGASET_DEVNAME,
+                                   &ops, THIS_MODULE);
+       if (driver == NULL) {
+               result = -ENOMEM;
+               goto error;
+       }
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&gigaset_usb_driver);
+       if (result < 0) {
+               pr_err("error %d registering USB driver\n", -result);
+               goto error;
+       }
+
+       pr_info(DRIVER_DESC "\n");
+       return 0;
+
+error:
+       if (driver)
+               gigaset_freedriver(driver);
+       driver = NULL;
+       return result;
+}
+
+/*
+ * This function is called while unloading the kernel-module
+ */
+static void __exit usb_gigaset_exit(void)
+{
+       int i;
+
+       gigaset_blockdriver(driver); /* => probe will fail
+                                     * => no gigaset_start any more
+                                     */
+
+       /* stop all connected devices */
+       for (i = 0; i < driver->minors; i++)
+               gigaset_shutdown(driver->cs + i);
+
+       /* from now on, no isdn callback should be possible */
+
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&gigaset_usb_driver);
+       /* this will call the disconnect-callback */
+       /* from now on, no disconnect/probe callback should be running */
+
+       gigaset_freedriver(driver);
+       driver = NULL;
+}
+
+
+module_init(usb_gigaset_init);
+module_exit(usb_gigaset_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/isdn/hysdn/Kconfig b/drivers/staging/isdn/hysdn/Kconfig
new file mode 100644 (file)
index 0000000..1971ef8
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config HYSDN
+       tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
+       depends on m && PROC_FS && PCI
+       help
+         Say Y here if you have one of Hypercope's active PCI ISDN cards
+         Champ, Ergo and Metro. You will then get a module called hysdn.
+         Please read the file <file:Documentation/isdn/README.hysdn> for more
+         information.
+
+config HYSDN_CAPI
+       bool "HYSDN CAPI 2.0 support"
+       depends on HYSDN && ISDN_CAPI
+       help
+         Say Y here if you like to use Hypercope's CAPI 2.0 interface.
diff --git a/drivers/staging/isdn/hysdn/Makefile b/drivers/staging/isdn/hysdn/Makefile
new file mode 100644 (file)
index 0000000..e01f17f
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Makefile for the hysdn ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_HYSDN)            += hysdn.o
+
+# Multipart objects.
+
+hysdn-y                                := hysdn_procconf.o hysdn_proclog.o boardergo.o \
+                                  hysdn_boot.o hysdn_sched.o hysdn_net.o hysdn_init.o
+hysdn-$(CONFIG_HYSDN_CAPI)     += hycapi.o
diff --git a/drivers/staging/isdn/hysdn/boardergo.c b/drivers/staging/isdn/hysdn/boardergo.c
new file mode 100644 (file)
index 0000000..2aa2a0e
--- /dev/null
@@ -0,0 +1,445 @@
+/* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 kai Exp $
+ *
+ * Linux driver for HYSDN cards, specific routines for ergo type boards.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
+ * DPRAM interface and layout with only minor differences all related
+ * stuff is done here, not in separate modules.
+ *
+ */
+
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "hysdn_defs.h"
+#include "boardergo.h"
+
+#define byteout(addr, val) outb(val, addr)
+#define bytein(addr) inb(addr)
+
+/***************************************************/
+/* The cards interrupt handler. Called from system */
+/***************************************************/
+static irqreturn_t
+ergo_interrupt(int intno, void *dev_id)
+{
+       hysdn_card *card = dev_id;      /* parameter from irq */
+       tErgDpram *dpr;
+       unsigned long flags;
+       unsigned char volatile b;
+
+       if (!card)
+               return IRQ_NONE;                /* error -> spurious interrupt */
+       if (!card->irq_enabled)
+               return IRQ_NONE;                /* other device interrupting or irq switched off */
+
+       spin_lock_irqsave(&card->hysdn_lock, flags); /* no further irqs allowed */
+
+       if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
+               spin_unlock_irqrestore(&card->hysdn_lock, flags);       /* restore old state */
+               return IRQ_NONE;                /* no interrupt requested by E1 */
+       }
+       /* clear any pending ints on the board */
+       dpr = card->dpram;
+       b = dpr->ToPcInt;       /* clear for ergo */
+       b |= dpr->ToPcIntMetro; /* same for metro */
+       b |= dpr->ToHyInt;      /* and for champ */
+
+       /* start kernel task immediately after leaving all interrupts */
+       if (!card->hw_lock)
+               schedule_work(&card->irq_queue);
+       spin_unlock_irqrestore(&card->hysdn_lock, flags);
+       return IRQ_HANDLED;
+}                              /* ergo_interrupt */
+
+/******************************************************************************/
+/* ergo_irq_bh will be called as part of the kernel clearing its shared work  */
+/* queue sometime after a call to schedule_work has been made passing our     */
+/* work_struct. This task is the only one handling data transfer from or to   */
+/* the card after booting. The task may be queued from everywhere             */
+/* (interrupts included).                                                     */
+/******************************************************************************/
+static void
+ergo_irq_bh(struct work_struct *ugli_api)
+{
+       hysdn_card *card = container_of(ugli_api, hysdn_card, irq_queue);
+       tErgDpram *dpr;
+       int again;
+       unsigned long flags;
+
+       if (card->state != CARD_STATE_RUN)
+               return;         /* invalid call */
+
+       dpr = card->dpram;      /* point to DPRAM */
+
+       spin_lock_irqsave(&card->hysdn_lock, flags);
+       if (card->hw_lock) {
+               spin_unlock_irqrestore(&card->hysdn_lock, flags);       /* hardware currently unavailable */
+               return;
+       }
+       card->hw_lock = 1;      /* we now lock the hardware */
+
+       do {
+               again = 0;      /* assume loop not to be repeated */
+
+               if (!dpr->ToHyFlag) {
+                       /* we are able to send a buffer */
+
+                       if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
+                                          ERG_TO_HY_BUF_SIZE)) {
+                               dpr->ToHyFlag = 1;      /* enable tx */
+                               again = 1;      /* restart loop */
+                       }
+               }               /* we are able to send a buffer */
+               if (dpr->ToPcFlag) {
+                       /* a message has arrived for us, handle it */
+
+                       if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
+                               dpr->ToPcFlag = 0;      /* we worked the data */
+                               again = 1;      /* restart loop */
+                       }
+               }               /* a message has arrived for us */
+               if (again) {
+                       dpr->ToHyInt = 1;
+                       dpr->ToPcInt = 1;       /* interrupt to E1 for all cards */
+               } else
+                       card->hw_lock = 0;      /* free hardware again */
+       } while (again);        /* until nothing more to do */
+
+       spin_unlock_irqrestore(&card->hysdn_lock, flags);
+}                              /* ergo_irq_bh */
+
+
+/*********************************************************/
+/* stop the card (hardware reset) and disable interrupts */
+/*********************************************************/
+static void
+ergo_stopcard(hysdn_card *card)
+{
+       unsigned long flags;
+       unsigned char val;
+
+       hysdn_net_release(card);        /* first release the net device if existing */
+#ifdef CONFIG_HYSDN_CAPI
+       hycapi_capi_stop(card);
+#endif /* CONFIG_HYSDN_CAPI */
+       spin_lock_irqsave(&card->hysdn_lock, flags);
+       val = bytein(card->iobase + PCI9050_INTR_REG);  /* get actual value */
+       val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1);        /* mask irq */
+       byteout(card->iobase + PCI9050_INTR_REG, val);
+       card->irq_enabled = 0;
+       byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET);      /* reset E1 processor */
+       card->state = CARD_STATE_UNUSED;
+       card->err_log_state = ERRLOG_STATE_OFF;         /* currently no log active */
+
+       spin_unlock_irqrestore(&card->hysdn_lock, flags);
+}                              /* ergo_stopcard */
+
+/**************************************************************************/
+/* enable or disable the cards error log. The event is queued if possible */
+/**************************************************************************/
+static void
+ergo_set_errlog_state(hysdn_card *card, int on)
+{
+       unsigned long flags;
+
+       if (card->state != CARD_STATE_RUN) {
+               card->err_log_state = ERRLOG_STATE_OFF;         /* must be off */
+               return;
+       }
+       spin_lock_irqsave(&card->hysdn_lock, flags);
+
+       if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
+           ((card->err_log_state == ERRLOG_STATE_ON) && on)) {
+               spin_unlock_irqrestore(&card->hysdn_lock, flags);
+               return;         /* nothing to do */
+       }
+       if (on)
+               card->err_log_state = ERRLOG_STATE_START;       /* request start */
+       else
+               card->err_log_state = ERRLOG_STATE_STOP;        /* request stop */
+
+       spin_unlock_irqrestore(&card->hysdn_lock, flags);
+       schedule_work(&card->irq_queue);
+}                              /* ergo_set_errlog_state */
+
+/******************************************/
+/* test the cards RAM and return 0 if ok. */
+/******************************************/
+static const char TestText[36] = "This Message is filler, why read it";
+
+static int
+ergo_testram(hysdn_card *card)
+{
+       tErgDpram *dpr = card->dpram;
+
+       memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable));      /* clear all Traps */
+       dpr->ToHyInt = 1;       /* E1 INTR state forced */
+
+       memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+              sizeof(TestText));
+       if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+                  sizeof(TestText)))
+               return (-1);
+
+       memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+              sizeof(TestText));
+       if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+                  sizeof(TestText)))
+               return (-1);
+
+       return (0);
+}                              /* ergo_testram */
+
+/*****************************************************************************/
+/* this function is intended to write stage 1 boot image to the cards buffer */
+/* this is done in two steps. First the 1024 hi-words are written (offs=0),  */
+/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the   */
+/* PCI-write-buffers flushed and the card is taken out of reset.             */
+/* The function then waits for a reaction of the E1 processor or a timeout.  */
+/* Negative return values are interpreted as errors.                         */
+/*****************************************************************************/
+static int
+ergo_writebootimg(struct HYSDN_CARD *card, unsigned char *buf,
+                 unsigned long offs)
+{
+       unsigned char *dst;
+       tErgDpram *dpram;
+       int cnt = (BOOT_IMG_SIZE >> 2);         /* number of words to move and swap (byte order!) */
+
+       if (card->debug_flags & LOG_POF_CARD)
+               hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
+
+       dst = card->dpram;      /* pointer to start of DPRAM */
+       dst += (offs + ERG_DPRAM_FILL_SIZE);    /* offset in the DPRAM */
+       while (cnt--) {
+               *dst++ = *(buf + 1);    /* high byte */
+               *dst++ = *buf;  /* low byte */
+               dst += 2;       /* point to next longword */
+               buf += 2;       /* buffer only filled with words */
+       }
+
+       /* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
+       /* flush the PCI-write-buffer and take the E1 out of reset */
+       if (offs) {
+               memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE);    /* fill the DPRAM still not cleared */
+               dpram = card->dpram;    /* get pointer to dpram structure */
+               dpram->ToHyNoDpramErrLog = 0xFF;        /* write a dpram register */
+               while (!dpram->ToHyNoDpramErrLog);      /* reread volatile register to flush PCI */
+
+               byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN);        /* start E1 processor */
+               /* the interrupts are still masked */
+
+               msleep_interruptible(20);               /* Timeout 20ms */
+
+               if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
+                       if (card->debug_flags & LOG_POF_CARD)
+                               hysdn_addlog(card, "ERGO: write bootldr no answer");
+                       return (-ERR_BOOTIMG_FAIL);
+               }
+       }                       /* start_boot_img */
+       return (0);             /* successful */
+}                              /* ergo_writebootimg */
+
+/********************************************************************************/
+/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
+/* using the boot spool mechanism. If everything works fine 0 is returned. In   */
+/* case of errors a negative error value is returned.                           */
+/********************************************************************************/
+static int
+ergo_writebootseq(struct HYSDN_CARD *card, unsigned char *buf, int len)
+{
+       tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
+       unsigned char *dst;
+       unsigned char buflen;
+       int nr_write;
+       unsigned char tmp_rdptr;
+       unsigned char wr_mirror;
+       int i;
+
+       if (card->debug_flags & LOG_POF_CARD)
+               hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
+
+       dst = sp->Data;         /* point to data in spool structure */
+       buflen = sp->Len;       /* maximum len of spooled data */
+       wr_mirror = sp->WrPtr;  /* only once read */
+
+       /* try until all bytes written or error */
+       i = 0x1000;             /* timeout value */
+       while (len) {
+
+               /* first determine the number of bytes that may be buffered */
+               do {
+                       tmp_rdptr = sp->RdPtr;  /* first read the pointer */
+                       i--;    /* decrement timeout */
+               } while (i && (tmp_rdptr != sp->RdPtr));        /* wait for stable pointer */
+
+               if (!i) {
+                       if (card->debug_flags & LOG_POF_CARD)
+                               hysdn_addlog(card, "ERGO: write boot seq timeout");
+                       return (-ERR_BOOTSEQ_FAIL);     /* value not stable -> timeout */
+               }
+               if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
+                       nr_write += buflen;     /* now we got number of free bytes - 1 in buffer */
+
+               if (!nr_write)
+                       continue;       /* no free bytes in buffer */
+
+               if (nr_write > len)
+                       nr_write = len;         /* limit if last few bytes */
+               i = 0x1000;     /* reset timeout value */
+
+               /* now we know how much bytes we may put in the puffer */
+               len -= nr_write;        /* we savely could adjust len before output */
+               while (nr_write--) {
+                       *(dst + wr_mirror) = *buf++;    /* output one byte */
+                       if (++wr_mirror >= buflen)
+                               wr_mirror = 0;
+                       sp->WrPtr = wr_mirror;  /* announce the next byte to E1 */
+               }               /* while (nr_write) */
+
+       }                       /* while (len) */
+       return (0);
+}                              /* ergo_writebootseq */
+
+/***********************************************************************************/
+/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
+/* boot process. If the process has been successful 0 is returned otherwise a     */
+/* negative error code is returned.                                                */
+/***********************************************************************************/
+static int
+ergo_waitpofready(struct HYSDN_CARD *card)
+{
+       tErgDpram *dpr = card->dpram;   /* pointer to DPRAM structure */
+       int timecnt = 10000 / 50;       /* timeout is 10 secs max. */
+       unsigned long flags;
+       int msg_size;
+       int i;
+
+       if (card->debug_flags & LOG_POF_CARD)
+               hysdn_addlog(card, "ERGO: waiting for pof ready");
+       while (timecnt--) {
+               /* wait until timeout  */
+
+               if (dpr->ToPcFlag) {
+                       /* data has arrived */
+
+                       if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
+                           (dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
+                           (dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
+                           ((*(unsigned long *) dpr->ToPcBuf) != RDY_MAGIC))
+                               break;  /* an error occurred */
+
+                       /* Check for additional data delivered during SysReady */
+                       msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
+                       if (msg_size > 0)
+                               if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
+                                       break;
+
+                       if (card->debug_flags & LOG_POF_RECORD)
+                               hysdn_addlog(card, "ERGO: pof boot success");
+                       spin_lock_irqsave(&card->hysdn_lock, flags);
+
+                       card->state = CARD_STATE_RUN;   /* now card is running */
+                       /* enable the cards interrupt */
+                       byteout(card->iobase + PCI9050_INTR_REG,
+                               bytein(card->iobase + PCI9050_INTR_REG) |
+                               (PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
+                       card->irq_enabled = 1;  /* we are ready to receive interrupts */
+
+                       dpr->ToPcFlag = 0;      /* reset data indicator */
+                       dpr->ToHyInt = 1;
+                       dpr->ToPcInt = 1;       /* interrupt to E1 for all cards */
+
+                       spin_unlock_irqrestore(&card->hysdn_lock, flags);
+                       if ((hynet_enable & (1 << card->myid))
+                           && (i = hysdn_net_create(card)))
+                       {
+                               ergo_stopcard(card);
+                               card->state = CARD_STATE_BOOTERR;
+                               return (i);
+                       }
+#ifdef CONFIG_HYSDN_CAPI
+                       if ((i = hycapi_capi_create(card))) {
+                               printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n");
+                       }
+#endif /* CONFIG_HYSDN_CAPI */
+                       return (0);     /* success */
+               }               /* data has arrived */
+               msleep_interruptible(50);               /* Timeout 50ms */
+       }                       /* wait until timeout */
+
+       if (card->debug_flags & LOG_POF_CARD)
+               hysdn_addlog(card, "ERGO: pof boot ready timeout");
+       return (-ERR_POF_TIMEOUT);
+}                              /* ergo_waitpofready */
+
+
+
+/************************************************************************************/
+/* release the cards hardware. Before releasing do a interrupt disable and hardware */
+/* reset. Also unmap dpram.                                                         */
+/* Use only during module release.                                                  */
+/************************************************************************************/
+static void
+ergo_releasehardware(hysdn_card *card)
+{
+       ergo_stopcard(card);    /* first stop the card if not already done */
+       free_irq(card->irq, card);      /* release interrupt */
+       release_region(card->iobase + PCI9050_INTR_REG, 1);     /* release all io ports */
+       release_region(card->iobase + PCI9050_USER_IO, 1);
+       iounmap(card->dpram);
+       card->dpram = NULL;     /* release shared mem */
+}                              /* ergo_releasehardware */
+
+
+/*********************************************************************************/
+/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
+/* value is returned.                                                            */
+/* Use only during module init.                                                  */
+/*********************************************************************************/
+int
+ergo_inithardware(hysdn_card *card)
+{
+       if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN"))
+               return (-1);
+       if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) {
+               release_region(card->iobase + PCI9050_INTR_REG, 1);
+               return (-1);    /* ports already in use */
+       }
+       card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
+       if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) {
+               release_region(card->iobase + PCI9050_INTR_REG, 1);
+               release_region(card->iobase + PCI9050_USER_IO, 1);
+               return (-1);
+       }
+
+       ergo_stopcard(card);    /* disable interrupts */
+       if (request_irq(card->irq, ergo_interrupt, IRQF_SHARED, "HYSDN", card)) {
+               ergo_releasehardware(card); /* return the acquired hardware */
+               return (-1);
+       }
+       /* success, now setup the function pointers */
+       card->stopcard = ergo_stopcard;
+       card->releasehardware = ergo_releasehardware;
+       card->testram = ergo_testram;
+       card->writebootimg = ergo_writebootimg;
+       card->writebootseq = ergo_writebootseq;
+       card->waitpofready = ergo_waitpofready;
+       card->set_errlog_state = ergo_set_errlog_state;
+       INIT_WORK(&card->irq_queue, ergo_irq_bh);
+       spin_lock_init(&card->hysdn_lock);
+
+       return (0);
+}                              /* ergo_inithardware */
diff --git a/drivers/staging/isdn/hysdn/boardergo.h b/drivers/staging/isdn/hysdn/boardergo.h
new file mode 100644 (file)
index 0000000..e99bd81
--- /dev/null
@@ -0,0 +1,100 @@
+/* $Id: boardergo.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+/************************************************/
+/* defines for the dual port memory of the card */
+/************************************************/
+#define ERG_DPRAM_PAGE_SIZE 0x2000     /* DPRAM occupies a 8K page */
+#define BOOT_IMG_SIZE 4096
+#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
+
+#define ERG_TO_HY_BUF_SIZE  0x0E00     /* 3072 bytes buffer size to card */
+#define ERG_TO_PC_BUF_SIZE  0x0E00     /* 3072 bytes to PC, too */
+
+/* following DPRAM layout copied from OS2-driver boarderg.h */
+typedef struct ErgDpram_tag {
+       /*0000 */ unsigned char ToHyBuf[ERG_TO_HY_BUF_SIZE];
+       /*0E00 */ unsigned char ToPcBuf[ERG_TO_PC_BUF_SIZE];
+
+       /*1C00 */ unsigned char bSoftUart[SIZE_RSV_SOFT_UART];
+       /* size 0x1B0 */
+
+       /*1DB0 *//* tErrLogEntry */ unsigned char volatile ErrLogMsg[64];
+       /* size 64 bytes */
+       /*1DB0  unsigned long ulErrType;               */
+       /*1DB4  unsigned long ulErrSubtype;            */
+       /*1DB8  unsigned long ucTextSize;              */
+       /*1DB9  unsigned long ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
+       /*1DF0 */
+
+       /*1DF0 */ unsigned short volatile ToHyChannel;
+       /*1DF2 */ unsigned short volatile ToHySize;
+       /*1DF4 */ unsigned char volatile ToHyFlag;
+       /* !=0: msg for Hy waiting */
+       /*1DF5 */ unsigned char volatile ToPcFlag;
+       /* !=0: msg for PC waiting */
+       /*1DF6 */ unsigned short volatile ToPcChannel;
+       /*1DF8 */ unsigned short volatile ToPcSize;
+       /*1DFA */ unsigned char bRes1DBA[0x1E00 - 0x1DFA];
+       /* 6 bytes */
+
+       /*1E00 */ unsigned char bRestOfEntryTbl[0x1F00 - 0x1E00];
+       /*1F00 */ unsigned long TrapTable[62];
+       /*1FF8 */ unsigned char bRes1FF8[0x1FFB - 0x1FF8];
+       /* low part of reset vetor */
+       /*1FFB */ unsigned char ToPcIntMetro;
+       /* notes:
+        * - metro has 32-bit boot ram - accessing
+        *   ToPcInt and ToHyInt would be the same;
+        *   so we moved ToPcInt to 1FFB.
+        *   Because on the PC side both vars are
+        *   readonly (reseting on int from E1 to PC),
+        *   we can read both vars on both cards
+        *   without destroying anything.
+        * - 1FFB is the high byte of the reset vector,
+        *   so E1 side should NOT change this byte
+        *   when writing!
+        */
+       /*1FFC */ unsigned char volatile ToHyNoDpramErrLog;
+       /* note: ToHyNoDpramErrLog is used to inform
+        *       boot loader, not to use DPRAM based
+        *       ErrLog; when DOS driver is rewritten
+        *       this becomes obsolete
+        */
+       /*1FFD */ unsigned char bRes1FFD;
+       /*1FFE */ unsigned char ToPcInt;
+       /* E1_intclear; on CHAMP2: E1_intset   */
+       /*1FFF */ unsigned char ToHyInt;
+       /* E1_intset;   on CHAMP2: E1_intclear */
+} tErgDpram;
+
+/**********************************************/
+/* PCI9050 controller local register offsets: */
+/* copied from boarderg.c                     */
+/**********************************************/
+#define PCI9050_INTR_REG    0x4C       /* Interrupt register */
+#define PCI9050_USER_IO     0x51       /* User I/O  register */
+
+/* bitmask for PCI9050_INTR_REG: */
+#define PCI9050_INTR_REG_EN1    0x01   /* 1= enable (def.), 0= disable */
+#define PCI9050_INTR_REG_POL1   0x02   /* 1= active high (def.), 0= active low */
+#define PCI9050_INTR_REG_STAT1  0x04   /* 1= intr. active, 0= intr. not active (def.) */
+#define PCI9050_INTR_REG_ENPCI  0x40   /* 1= PCI interrupts enable (def.) */
+
+/* bitmask for PCI9050_USER_IO: */
+#define PCI9050_USER_IO_EN3     0x02   /* 1= disable      , 0= enable (def.) */
+#define PCI9050_USER_IO_DIR3    0x04   /* 1= output (def.), 0= input         */
+#define PCI9050_USER_IO_DAT3    0x08   /* 1= high (def.)  , 0= low           */
+
+#define PCI9050_E1_RESET    (PCI9050_USER_IO_DIR3)             /* 0x04 */
+#define PCI9050_E1_RUN      (PCI9050_USER_IO_DAT3 | PCI9050_USER_IO_DIR3)              /* 0x0C */
diff --git a/drivers/staging/isdn/hysdn/hycapi.c b/drivers/staging/isdn/hysdn/hycapi.c
new file mode 100644 (file)
index 0000000..a2c15cd
--- /dev/null
@@ -0,0 +1,785 @@
+/* $Id: hycapi.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, CAPI2.0-Interface.
+ *
+ * Author    Ulrich Albrecht <u.albrecht@hypercope.de> for Hypercope GmbH
+ * Copyright 2000 by Hypercope GmbH
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+
+#define        VER_DRIVER      0
+#define        VER_CARDTYPE    1
+#define        VER_HWID        2
+#define        VER_SERIAL      3
+#define        VER_OPTION      4
+#define        VER_PROTO       5
+#define        VER_PROFILE     6
+#define        VER_CAPI        7
+
+#include "hysdn_defs.h"
+#include <linux/kernelcapi.h>
+
+static char hycapi_revision[] = "$Revision: 1.8.6.4 $";
+
+unsigned int hycapi_enable = 0xffffffff;
+module_param(hycapi_enable, uint, 0);
+
+typedef struct _hycapi_appl {
+       unsigned int ctrl_mask;
+       capi_register_params rp;
+       struct sk_buff *listen_req[CAPI_MAXCONTR];
+} hycapi_appl;
+
+static hycapi_appl hycapi_applications[CAPI_MAXAPPL];
+
+static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+
+static inline int _hycapi_appCheck(int app_id, int ctrl_no)
+{
+       if ((ctrl_no <= 0) || (ctrl_no > CAPI_MAXCONTR) || (app_id <= 0) ||
+          (app_id > CAPI_MAXAPPL))
+       {
+               printk(KERN_ERR "HYCAPI: Invalid request app_id %d for controller %d", app_id, ctrl_no);
+               return -1;
+       }
+       return ((hycapi_applications[app_id - 1].ctrl_mask & (1 << (ctrl_no-1))) != 0);
+}
+
+/******************************
+Kernel-Capi callback reset_ctr
+******************************/
+
+static void
+hycapi_reset_ctr(struct capi_ctr *ctrl)
+{
+       hycapictrl_info *cinfo = ctrl->driverdata;
+
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "HYCAPI hycapi_reset_ctr\n");
+#endif
+       capilib_release(&cinfo->ncci_head);
+       capi_ctr_down(ctrl);
+}
+
+/******************************
+Kernel-Capi callback remove_ctr
+******************************/
+
+static void
+hycapi_remove_ctr(struct capi_ctr *ctrl)
+{
+       int i;
+       hycapictrl_info *cinfo = NULL;
+       hysdn_card *card = NULL;
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "HYCAPI hycapi_remove_ctr\n");
+#endif
+       cinfo = (hycapictrl_info *)(ctrl->driverdata);
+       if (!cinfo) {
+               printk(KERN_ERR "No hycapictrl_info set!");
+               return;
+       }
+       card = cinfo->card;
+       capi_ctr_suspend_output(ctrl);
+       for (i = 0; i < CAPI_MAXAPPL; i++) {
+               if (hycapi_applications[i].listen_req[ctrl->cnr - 1]) {
+                       kfree_skb(hycapi_applications[i].listen_req[ctrl->cnr - 1]);
+                       hycapi_applications[i].listen_req[ctrl->cnr - 1] = NULL;
+               }
+       }
+       detach_capi_ctr(ctrl);
+       ctrl->driverdata = NULL;
+       kfree(card->hyctrlinfo);
+
+
+       card->hyctrlinfo = NULL;
+}
+
+/***********************************************************
+
+Queue a CAPI-message to the controller.
+
+***********************************************************/
+
+static void
+hycapi_sendmsg_internal(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+       hysdn_card *card = cinfo->card;
+
+       spin_lock_irq(&cinfo->lock);
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_send_message\n");
+#endif
+       cinfo->skbs[cinfo->in_idx++] = skb;     /* add to buffer list */
+       if (cinfo->in_idx >= HYSDN_MAX_CAPI_SKB)
+               cinfo->in_idx = 0;      /* wrap around */
+       cinfo->sk_count++;              /* adjust counter */
+       if (cinfo->sk_count >= HYSDN_MAX_CAPI_SKB) {
+               /* inform upper layers we're full */
+               printk(KERN_ERR "HYSDN Card%d: CAPI-buffer overrun!\n",
+                      card->myid);
+               capi_ctr_suspend_output(ctrl);
+       }
+       cinfo->tx_skb = skb;
+       spin_unlock_irq(&cinfo->lock);
+       schedule_work(&card->irq_queue);
+}
+
+/***********************************************************
+hycapi_register_internal
+
+Send down the CAPI_REGISTER-Command to the controller.
+This functions will also be used if the adapter has been rebooted to
+re-register any applications in the private list.
+
+************************************************************/
+
+static void
+hycapi_register_internal(struct capi_ctr *ctrl, __u16 appl,
+                        capi_register_params *rp)
+{
+       char ExtFeatureDefaults[] = "49  /0/0/0/0,*/1,*/2,*/3,*/4,*/5,*/6,*/7,*/8,*/9,*";
+       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+       hysdn_card *card = cinfo->card;
+       struct sk_buff *skb;
+       __u16 len;
+       __u8 _command = 0xa0, _subcommand = 0x80;
+       __u16 MessageNumber = 0x0000;
+       __u16 MessageBufferSize = 0;
+       int slen = strlen(ExtFeatureDefaults);
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_register_appl\n");
+#endif
+       MessageBufferSize = rp->level3cnt * rp->datablkcnt * rp->datablklen;
+
+       len = CAPI_MSG_BASELEN + 8 + slen + 1;
+       if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+               printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
+                      card->myid);
+               return;
+       }
+       skb_put_data(skb, &len, sizeof(__u16));
+       skb_put_data(skb, &appl, sizeof(__u16));
+       skb_put_data(skb, &_command, sizeof(__u8));
+       skb_put_data(skb, &_subcommand, sizeof(__u8));
+       skb_put_data(skb, &MessageNumber, sizeof(__u16));
+       skb_put_data(skb, &MessageBufferSize, sizeof(__u16));
+       skb_put_data(skb, &(rp->level3cnt), sizeof(__u16));
+       skb_put_data(skb, &(rp->datablkcnt), sizeof(__u16));
+       skb_put_data(skb, &(rp->datablklen), sizeof(__u16));
+       skb_put_data(skb, ExtFeatureDefaults, slen);
+       hycapi_applications[appl - 1].ctrl_mask |= (1 << (ctrl->cnr - 1));
+       hycapi_send_message(ctrl, skb);
+}
+
+/************************************************************
+hycapi_restart_internal
+
+After an adapter has been rebootet, re-register all applications and
+send a LISTEN_REQ (if there has been such a thing )
+
+*************************************************************/
+
+static void hycapi_restart_internal(struct capi_ctr *ctrl)
+{
+       int i;
+       struct sk_buff *skb;
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_WARNING "HYSDN: hycapi_restart_internal");
+#endif
+       for (i = 0; i < CAPI_MAXAPPL; i++) {
+               if (_hycapi_appCheck(i + 1, ctrl->cnr) == 1) {
+                       hycapi_register_internal(ctrl, i + 1,
+                                                &hycapi_applications[i].rp);
+                       if (hycapi_applications[i].listen_req[ctrl->cnr - 1]) {
+                               skb = skb_copy(hycapi_applications[i].listen_req[ctrl->cnr - 1], GFP_ATOMIC);
+                               hycapi_sendmsg_internal(ctrl, skb);
+                       }
+               }
+       }
+}
+
+/*************************************************************
+Register an application.
+Error-checking is done for CAPI-compliance.
+
+The application is recorded in the internal list.
+*************************************************************/
+
+static void
+hycapi_register_appl(struct capi_ctr *ctrl, __u16 appl,
+                    capi_register_params *rp)
+{
+       int MaxLogicalConnections = 0, MaxBDataBlocks = 0, MaxBDataLen = 0;
+       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+       hysdn_card *card = cinfo->card;
+       int chk = _hycapi_appCheck(appl, ctrl->cnr);
+       if (chk < 0) {
+               return;
+       }
+       if (chk == 1) {
+               printk(KERN_INFO "HYSDN: apl %d already registered\n", appl);
+               return;
+       }
+       MaxBDataBlocks = rp->datablkcnt > CAPI_MAXDATAWINDOW ? CAPI_MAXDATAWINDOW : rp->datablkcnt;
+       rp->datablkcnt = MaxBDataBlocks;
+       MaxBDataLen = rp->datablklen < 1024 ? 1024 : rp->datablklen;
+       rp->datablklen = MaxBDataLen;
+
+       MaxLogicalConnections = rp->level3cnt;
+       if (MaxLogicalConnections < 0) {
+               MaxLogicalConnections = card->bchans * -MaxLogicalConnections;
+       }
+       if (MaxLogicalConnections == 0) {
+               MaxLogicalConnections = card->bchans;
+       }
+
+       rp->level3cnt = MaxLogicalConnections;
+       memcpy(&hycapi_applications[appl - 1].rp,
+              rp, sizeof(capi_register_params));
+}
+
+/*********************************************************************
+
+hycapi_release_internal
+
+Send down a CAPI_RELEASE to the controller.
+*********************************************************************/
+
+static void hycapi_release_internal(struct capi_ctr *ctrl, __u16 appl)
+{
+       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+       hysdn_card *card = cinfo->card;
+       struct sk_buff *skb;
+       __u16 len;
+       __u8 _command = 0xa1, _subcommand = 0x80;
+       __u16 MessageNumber = 0x0000;
+
+       capilib_release_appl(&cinfo->ncci_head, appl);
+
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_release_appl\n");
+#endif
+       len = CAPI_MSG_BASELEN;
+       if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+               printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
+                      card->myid);
+               return;
+       }
+       skb_put_data(skb, &len, sizeof(__u16));
+       skb_put_data(skb, &appl, sizeof(__u16));
+       skb_put_data(skb, &_command, sizeof(__u8));
+       skb_put_data(skb, &_subcommand, sizeof(__u8));
+       skb_put_data(skb, &MessageNumber, sizeof(__u16));
+       hycapi_send_message(ctrl, skb);
+       hycapi_applications[appl - 1].ctrl_mask &= ~(1 << (ctrl->cnr - 1));
+}
+
+/******************************************************************
+hycapi_release_appl
+
+Release the application from the internal list an remove it's
+registration at controller-level
+******************************************************************/
+
+static void
+hycapi_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+       int chk;
+
+       chk = _hycapi_appCheck(appl, ctrl->cnr);
+       if (chk < 0) {
+               printk(KERN_ERR "HYCAPI: Releasing invalid appl %d on controller %d\n", appl, ctrl->cnr);
+               return;
+       }
+       if (hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1]) {
+               kfree_skb(hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1]);
+               hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1] = NULL;
+       }
+       if (chk == 1)
+       {
+               hycapi_release_internal(ctrl, appl);
+       }
+}
+
+
+/**************************************************************
+Kill a single controller.
+**************************************************************/
+
+int hycapi_capi_release(hysdn_card *card)
+{
+       hycapictrl_info *cinfo = card->hyctrlinfo;
+       struct capi_ctr *ctrl;
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_capi_release\n");
+#endif
+       if (cinfo) {
+               ctrl = &cinfo->capi_ctrl;
+               hycapi_remove_ctr(ctrl);
+       }
+       return 0;
+}
+
+/**************************************************************
+hycapi_capi_stop
+
+Stop CAPI-Output on a card. (e.g. during reboot)
+***************************************************************/
+
+int hycapi_capi_stop(hysdn_card *card)
+{
+       hycapictrl_info *cinfo = card->hyctrlinfo;
+       struct capi_ctr *ctrl;
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_capi_stop\n");
+#endif
+       if (cinfo) {
+               ctrl = &cinfo->capi_ctrl;
+/*             ctrl->suspend_output(ctrl); */
+               capi_ctr_down(ctrl);
+       }
+       return 0;
+}
+
+/***************************************************************
+hycapi_send_message
+
+Send a message to the controller.
+
+Messages are parsed for their Command/Subcommand-type, and appropriate
+action's are performed.
+
+Note that we have to muck around with a 64Bit-DATA_REQ as there are
+firmware-releases that do not check the MsgLen-Indication!
+
+***************************************************************/
+
+static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+       __u16 appl_id;
+       int _len, _len2;
+       __u8 msghead[64];
+       hycapictrl_info *cinfo = ctrl->driverdata;
+       u16 retval = CAPI_NOERROR;
+
+       appl_id = CAPIMSG_APPID(skb->data);
+       switch (_hycapi_appCheck(appl_id, ctrl->cnr))
+       {
+       case 0:
+/*                     printk(KERN_INFO "Need to register\n"); */
+               hycapi_register_internal(ctrl,
+                                        appl_id,
+                                        &(hycapi_applications[appl_id - 1].rp));
+               break;
+       case 1:
+               break;
+       default:
+               printk(KERN_ERR "HYCAPI: Controller mixup!\n");
+               retval = CAPI_ILLAPPNR;
+               goto out;
+       }
+       switch (CAPIMSG_CMD(skb->data)) {
+       case CAPI_DISCONNECT_B3_RESP:
+               capilib_free_ncci(&cinfo->ncci_head, appl_id,
+                                 CAPIMSG_NCCI(skb->data));
+               break;
+       case CAPI_DATA_B3_REQ:
+               _len = CAPIMSG_LEN(skb->data);
+               if (_len > 22) {
+                       _len2 = _len - 22;
+                       skb_copy_from_linear_data(skb, msghead, 22);
+                       skb_copy_to_linear_data_offset(skb, _len2,
+                                                      msghead, 22);
+                       skb_pull(skb, _len2);
+                       CAPIMSG_SETLEN(skb->data, 22);
+                       retval = capilib_data_b3_req(&cinfo->ncci_head,
+                                                    CAPIMSG_APPID(skb->data),
+                                                    CAPIMSG_NCCI(skb->data),
+                                                    CAPIMSG_MSGID(skb->data));
+               }
+               break;
+       case CAPI_LISTEN_REQ:
+               if (hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1])
+               {
+                       kfree_skb(hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1]);
+                       hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1] = NULL;
+               }
+               if (!(hycapi_applications[appl_id  -1].listen_req[ctrl->cnr - 1] = skb_copy(skb, GFP_ATOMIC)))
+               {
+                       printk(KERN_ERR "HYSDN: memory squeeze in private_listen\n");
+               }
+               break;
+       default:
+               break;
+       }
+out:
+       if (retval == CAPI_NOERROR)
+               hycapi_sendmsg_internal(ctrl, skb);
+       else
+               dev_kfree_skb_any(skb);
+
+       return retval;
+}
+
+static int hycapi_proc_show(struct seq_file *m, void *v)
+{
+       struct capi_ctr *ctrl = m->private;
+       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+       hysdn_card *card = cinfo->card;
+       char *s;
+
+       seq_printf(m, "%-16s %s\n", "name", cinfo->cardname);
+       seq_printf(m, "%-16s 0x%x\n", "io", card->iobase);
+       seq_printf(m, "%-16s %d\n", "irq", card->irq);
+
+       switch (card->brdtype) {
+       case BD_PCCARD:  s = "HYSDN Hycard"; break;
+       case BD_ERGO: s = "HYSDN Ergo2"; break;
+       case BD_METRO: s = "HYSDN Metro4"; break;
+       case BD_CHAMP2: s = "HYSDN Champ2";     break;
+       case BD_PLEXUS: s = "HYSDN Plexus30"; break;
+       default: s = "???"; break;
+       }
+       seq_printf(m, "%-16s %s\n", "type", s);
+       if ((s = cinfo->version[VER_DRIVER]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_driver", s);
+       if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
+       if ((s = cinfo->version[VER_SERIAL]) != NULL)
+               seq_printf(m, "%-16s %s\n", "ver_serial", s);
+
+       seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
+
+       return 0;
+}
+
+/**************************************************************
+hycapi_load_firmware
+
+This does NOT load any firmware, but the callback somehow is needed
+on capi-interface registration.
+
+**************************************************************/
+
+static int hycapi_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_load_firmware\n");
+#endif
+       return 0;
+}
+
+
+static char *hycapi_procinfo(struct capi_ctr *ctrl)
+{
+       hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "%s\n", __func__);
+#endif
+       if (!cinfo)
+               return "";
+       sprintf(cinfo->infobuf, "%s %s 0x%x %d %s",
+               cinfo->cardname[0] ? cinfo->cardname : "-",
+               cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+               cinfo->card ? cinfo->card->iobase : 0x0,
+               cinfo->card ? cinfo->card->irq : 0,
+               hycapi_revision
+               );
+       return cinfo->infobuf;
+}
+
+/******************************************************************
+hycapi_rx_capipkt
+
+Receive a capi-message.
+
+All B3_DATA_IND are converted to 64K-extension compatible format.
+New nccis are created if necessary.
+*******************************************************************/
+
+void
+hycapi_rx_capipkt(hysdn_card *card, unsigned char *buf, unsigned short len)
+{
+       struct sk_buff *skb;
+       hycapictrl_info *cinfo = card->hyctrlinfo;
+       struct capi_ctr *ctrl;
+       __u16 ApplId;
+       __u16 MsgLen, info;
+       __u16 len2, CapiCmd;
+       __u32 CP64[2] = {0, 0};
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_rx_capipkt\n");
+#endif
+       if (!cinfo) {
+               return;
+       }
+       ctrl = &cinfo->capi_ctrl;
+       if (len < CAPI_MSG_BASELEN) {
+               printk(KERN_ERR "HYSDN Card%d: invalid CAPI-message, length %d!\n",
+                      card->myid, len);
+               return;
+       }
+       MsgLen = CAPIMSG_LEN(buf);
+       ApplId = CAPIMSG_APPID(buf);
+       CapiCmd = CAPIMSG_CMD(buf);
+
+       if ((CapiCmd == CAPI_DATA_B3_IND) && (MsgLen < 30)) {
+               len2 = len + (30 - MsgLen);
+               if (!(skb = alloc_skb(len2, GFP_ATOMIC))) {
+                       printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
+                              card->myid);
+                       return;
+               }
+               skb_put_data(skb, buf, MsgLen);
+               skb_put_data(skb, CP64, 2 * sizeof(__u32));
+               skb_put_data(skb, buf + MsgLen, len - MsgLen);
+               CAPIMSG_SETLEN(skb->data, 30);
+       } else {
+               if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+                       printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
+                              card->myid);
+                       return;
+               }
+               skb_put_data(skb, buf, len);
+       }
+       switch (CAPIMSG_CMD(skb->data))
+       {
+       case CAPI_CONNECT_B3_CONF:
+/* Check info-field for error-indication: */
+               info = CAPIMSG_U16(skb->data, 12);
+               switch (info)
+               {
+               case 0:
+                       capilib_new_ncci(&cinfo->ncci_head, ApplId, CAPIMSG_NCCI(skb->data),
+                                        hycapi_applications[ApplId - 1].rp.datablkcnt);
+
+                       break;
+               case 0x0001:
+                       printk(KERN_ERR "HYSDN Card%d: NCPI not supported by current "
+                              "protocol. NCPI ignored.\n", card->myid);
+                       break;
+               case 0x2001:
+                       printk(KERN_ERR "HYSDN Card%d: Message not supported in"
+                              " current state\n", card->myid);
+                       break;
+               case 0x2002:
+                       printk(KERN_ERR "HYSDN Card%d: invalid PLCI\n", card->myid);
+                       break;
+               case 0x2004:
+                       printk(KERN_ERR "HYSDN Card%d: out of NCCI\n", card->myid);
+                       break;
+               case 0x3008:
+                       printk(KERN_ERR "HYSDN Card%d: NCPI not supported\n",
+                              card->myid);
+                       break;
+               default:
+                       printk(KERN_ERR "HYSDN Card%d: Info in CONNECT_B3_CONF: %d\n",
+                              card->myid, info);
+                       break;
+               }
+               break;
+       case CAPI_CONNECT_B3_IND:
+               capilib_new_ncci(&cinfo->ncci_head, ApplId,
+                                CAPIMSG_NCCI(skb->data),
+                                hycapi_applications[ApplId - 1].rp.datablkcnt);
+               break;
+       case CAPI_DATA_B3_CONF:
+               capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+                                    CAPIMSG_NCCI(skb->data),
+                                    CAPIMSG_MSGID(skb->data));
+               break;
+       default:
+               break;
+       }
+       capi_ctr_handle_message(ctrl, ApplId, skb);
+}
+
+/******************************************************************
+hycapi_tx_capiack
+
+Internally acknowledge a msg sent. This will remove the msg from the
+internal queue.
+
+*******************************************************************/
+
+void hycapi_tx_capiack(hysdn_card *card)
+{
+       hycapictrl_info *cinfo = card->hyctrlinfo;
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_tx_capiack\n");
+#endif
+       if (!cinfo) {
+               return;
+       }
+       spin_lock_irq(&cinfo->lock);
+       kfree_skb(cinfo->skbs[cinfo->out_idx]);         /* free skb */
+       cinfo->skbs[cinfo->out_idx++] = NULL;
+       if (cinfo->out_idx >= HYSDN_MAX_CAPI_SKB)
+               cinfo->out_idx = 0;     /* wrap around */
+
+       if (cinfo->sk_count-- == HYSDN_MAX_CAPI_SKB)    /* dec usage count */
+               capi_ctr_resume_output(&cinfo->capi_ctrl);
+       spin_unlock_irq(&cinfo->lock);
+}
+
+/***************************************************************
+hycapi_tx_capiget(hysdn_card *card)
+
+This is called when polling for messages to SEND.
+
+****************************************************************/
+
+struct sk_buff *
+hycapi_tx_capiget(hysdn_card *card)
+{
+       hycapictrl_info *cinfo = card->hyctrlinfo;
+       if (!cinfo) {
+               return (struct sk_buff *)NULL;
+       }
+       if (!cinfo->sk_count)
+               return (struct sk_buff *)NULL;  /* nothing available */
+
+       return (cinfo->skbs[cinfo->out_idx]);           /* next packet to send */
+}
+
+
+/**********************************************************
+int hycapi_init()
+
+attach the capi-driver to the kernel-capi.
+
+***********************************************************/
+
+int hycapi_init(void)
+{
+       int i;
+       for (i = 0; i < CAPI_MAXAPPL; i++) {
+               memset(&(hycapi_applications[i]), 0, sizeof(hycapi_appl));
+       }
+       return (0);
+}
+
+/**************************************************************
+hycapi_cleanup(void)
+
+detach the capi-driver to the kernel-capi. Actually this should
+free some more ressources. Do that later.
+**************************************************************/
+
+void
+hycapi_cleanup(void)
+{
+}
+
+/********************************************************************
+hycapi_capi_create(hysdn_card *card)
+
+Attach the card with its capi-ctrl.
+*********************************************************************/
+
+static void hycapi_fill_profile(hysdn_card *card)
+{
+       hycapictrl_info *cinfo = NULL;
+       struct capi_ctr *ctrl = NULL;
+       cinfo = card->hyctrlinfo;
+       if (!cinfo) return;
+       ctrl = &cinfo->capi_ctrl;
+       strcpy(ctrl->manu, "Hypercope");
+       ctrl->version.majorversion = 2;
+       ctrl->version.minorversion = 0;
+       ctrl->version.majormanuversion = 3;
+       ctrl->version.minormanuversion = 2;
+       ctrl->profile.ncontroller = card->myid;
+       ctrl->profile.nbchannel = card->bchans;
+       ctrl->profile.goptions = GLOBAL_OPTION_INTERNAL_CONTROLLER |
+               GLOBAL_OPTION_B_CHANNEL_OPERATION;
+       ctrl->profile.support1 =  B1_PROT_64KBIT_HDLC |
+               (card->faxchans ? B1_PROT_T30 : 0) |
+               B1_PROT_64KBIT_TRANSPARENT;
+       ctrl->profile.support2 = B2_PROT_ISO7776 |
+               (card->faxchans ? B2_PROT_T30 : 0) |
+               B2_PROT_TRANSPARENT;
+       ctrl->profile.support3 = B3_PROT_TRANSPARENT |
+               B3_PROT_T90NL |
+               (card->faxchans ? B3_PROT_T30 : 0) |
+               (card->faxchans ? B3_PROT_T30EXT : 0) |
+               B3_PROT_ISO8208;
+}
+
+int
+hycapi_capi_create(hysdn_card *card)
+{
+       hycapictrl_info *cinfo = NULL;
+       struct capi_ctr *ctrl = NULL;
+       int retval;
+#ifdef HYCAPI_PRINTFNAMES
+       printk(KERN_NOTICE "hycapi_capi_create\n");
+#endif
+       if ((hycapi_enable & (1 << card->myid)) == 0) {
+               return 1;
+       }
+       if (!card->hyctrlinfo) {
+               cinfo = kzalloc(sizeof(hycapictrl_info), GFP_ATOMIC);
+               if (!cinfo) {
+                       printk(KERN_WARNING "HYSDN: no memory for capi-ctrl.\n");
+                       return -ENOMEM;
+               }
+               card->hyctrlinfo = cinfo;
+               cinfo->card = card;
+               spin_lock_init(&cinfo->lock);
+               INIT_LIST_HEAD(&cinfo->ncci_head);
+
+               switch (card->brdtype) {
+               case BD_PCCARD:  strcpy(cinfo->cardname, "HYSDN Hycard"); break;
+               case BD_ERGO: strcpy(cinfo->cardname, "HYSDN Ergo2"); break;
+               case BD_METRO: strcpy(cinfo->cardname, "HYSDN Metro4"); break;
+               case BD_CHAMP2: strcpy(cinfo->cardname, "HYSDN Champ2"); break;
+               case BD_PLEXUS: strcpy(cinfo->cardname, "HYSDN Plexus30"); break;
+               default: strcpy(cinfo->cardname, "HYSDN ???"); break;
+               }
+
+               ctrl = &cinfo->capi_ctrl;
+               ctrl->driver_name   = "hycapi";
+               ctrl->driverdata    = cinfo;
+               ctrl->register_appl = hycapi_register_appl;
+               ctrl->release_appl  = hycapi_release_appl;
+               ctrl->send_message  = hycapi_send_message;
+               ctrl->load_firmware = hycapi_load_firmware;
+               ctrl->reset_ctr     = hycapi_reset_ctr;
+               ctrl->procinfo      = hycapi_procinfo;
+               ctrl->proc_show     = hycapi_proc_show;
+               strcpy(ctrl->name, cinfo->cardname);
+               ctrl->owner = THIS_MODULE;
+
+               retval = attach_capi_ctr(ctrl);
+               if (retval) {
+                       printk(KERN_ERR "hycapi: attach controller failed.\n");
+                       return -EBUSY;
+               }
+               /* fill in the blanks: */
+               hycapi_fill_profile(card);
+               capi_ctr_ready(ctrl);
+       } else {
+               /* resume output on stopped ctrl */
+               ctrl = &card->hyctrlinfo->capi_ctrl;
+               hycapi_fill_profile(card);
+               capi_ctr_ready(ctrl);
+               hycapi_restart_internal(ctrl);
+/*             ctrl->resume_output(ctrl); */
+       }
+       return 0;
+}
diff --git a/drivers/staging/isdn/hysdn/hysdn_boot.c b/drivers/staging/isdn/hysdn/hysdn_boot.c
new file mode 100644 (file)
index 0000000..ba177c3
--- /dev/null
@@ -0,0 +1,400 @@
+/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * specific routines for booting and pof handling
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "hysdn_defs.h"
+#include "hysdn_pof.h"
+
+/********************************/
+/* defines for pof read handler */
+/********************************/
+#define POF_READ_FILE_HEAD  0
+#define POF_READ_TAG_HEAD   1
+#define POF_READ_TAG_DATA   2
+
+/************************************************************/
+/* definition of boot specific data area. This data is only */
+/* needed during boot and so allocated dynamically.         */
+/************************************************************/
+struct boot_data {
+       unsigned short Cryptor; /* for use with Decrypt function */
+       unsigned short Nrecs;   /* records remaining in file */
+       unsigned char pof_state;/* actual state of read handler */
+       unsigned char is_crypted;/* card data is crypted */
+       int BufSize;            /* actual number of bytes bufferd */
+       int last_error;         /* last occurred error */
+       unsigned short pof_recid;/* actual pof recid */
+       unsigned long pof_reclen;/* total length of pof record data */
+       unsigned long pof_recoffset;/* actual offset inside pof record */
+       union {
+               unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
+               tPofRecHdr PofRecHdr;   /* header for actual record/chunk */
+               tPofFileHdr PofFileHdr;         /* header from POF file */
+               tPofTimeStamp PofTime;  /* time information */
+       } buf;
+};
+
+/*****************************************************/
+/*  start decryption of successive POF file chuncks.  */
+/*                                                   */
+/*  to be called at start of POF file reading,       */
+/*  before starting any decryption on any POF record. */
+/*****************************************************/
+static void
+StartDecryption(struct boot_data *boot)
+{
+       boot->Cryptor = CRYPT_STARTTERM;
+}                              /* StartDecryption */
+
+
+/***************************************************************/
+/* decrypt complete BootBuf                                    */
+/* NOTE: decryption must be applied to all or none boot tags - */
+/*       to HI and LO boot loader and (all) seq tags, because  */
+/*       global Cryptor is started for whole POF.              */
+/***************************************************************/
+static void
+DecryptBuf(struct boot_data *boot, int cnt)
+{
+       unsigned char *bufp = boot->buf.BootBuf;
+
+       while (cnt--) {
+               boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
+               *bufp++ ^= (unsigned char)boot->Cryptor;
+       }
+}                              /* DecryptBuf */
+
+/********************************************************************************/
+/* pof_handle_data executes the required actions dependent on the active record */
+/* id. If successful 0 is returned, a negative value shows an error.           */
+/********************************************************************************/
+static int
+pof_handle_data(hysdn_card *card, int datlen)
+{
+       struct boot_data *boot = card->boot;    /* pointer to boot specific data */
+       long l;
+       unsigned char *imgp;
+       int img_len;
+
+       /* handle the different record types */
+       switch (boot->pof_recid) {
+
+       case TAG_TIMESTMP:
+               if (card->debug_flags & LOG_POF_RECORD)
+                       hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
+               break;
+
+       case TAG_CBOOTDTA:
+               DecryptBuf(boot, datlen);       /* we need to encrypt the buffer */
+               /* fall through */
+       case TAG_BOOTDTA:
+               if (card->debug_flags & LOG_POF_RECORD)
+                       hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+                                    (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
+                                    datlen, boot->pof_recoffset);
+
+               if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
+                       boot->last_error = EPOF_BAD_IMG_SIZE;   /* invalid length */
+                       return (boot->last_error);
+               }
+               imgp = boot->buf.BootBuf;       /* start of buffer */
+               img_len = datlen;       /* maximum length to transfer */
+
+               l = POF_BOOT_LOADER_OFF_IN_PAGE -
+                       (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
+               if (l > 0) {
+                       /* buffer needs to be truncated */
+                       imgp += l;      /* advance pointer */
+                       img_len -= l;   /* adjust len */
+               }
+               /* at this point no special handling for data wrapping over buffer */
+               /* is necessary, because the boot image always will be adjusted to */
+               /* match a page boundary inside the buffer.                        */
+               /* The buffer for the boot image on the card is filled in 2 cycles */
+               /* first the 1024 hi-words are put in the buffer, then the low 1024 */
+               /* word are handled in the same way with different offset.         */
+
+               if (img_len > 0) {
+                       /* data available for copy */
+                       if ((boot->last_error =
+                            card->writebootimg(card, imgp,
+                                               (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
+                               return (boot->last_error);
+               }
+               break;  /* end of case boot image hi/lo */
+
+       case TAG_CABSDATA:
+               DecryptBuf(boot, datlen);       /* we need to encrypt the buffer */
+               /* fall through */
+       case TAG_ABSDATA:
+               if (card->debug_flags & LOG_POF_RECORD)
+                       hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+                                    (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
+                                    datlen, boot->pof_recoffset);
+
+               if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)
+                       return (boot->last_error);      /* error writing data */
+
+               if (boot->pof_recoffset + datlen >= boot->pof_reclen)
+                       return (card->waitpofready(card));      /* data completely spooled, wait for ready */
+
+               break;  /* end of case boot seq data */
+
+       default:
+               if (card->debug_flags & LOG_POF_RECORD)
+                       hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
+                                    datlen, boot->pof_recoffset);
+
+               break;  /* simply skip record */
+       }                       /* switch boot->pof_recid */
+
+       return (0);
+}                              /* pof_handle_data */
+
+
+/******************************************************************************/
+/* pof_write_buffer is called when the buffer has been filled with the needed */
+/* number of data bytes. The number delivered is additionally supplied for    */
+/* verification. The functions handles the data and returns the needed number */
+/* of bytes for the next action. If the returned value is 0 or less an error  */
+/* occurred and booting must be aborted.                                       */
+/******************************************************************************/
+int
+pof_write_buffer(hysdn_card *card, int datlen)
+{
+       struct boot_data *boot = card->boot;    /* pointer to boot specific data */
+
+       if (!boot)
+               return (-EFAULT);       /* invalid call */
+       if (boot->last_error < 0)
+               return (boot->last_error);      /* repeated error */
+
+       if (card->debug_flags & LOG_POF_WRITE)
+               hysdn_addlog(card, "POF write: got %d bytes ", datlen);
+
+       switch (boot->pof_state) {
+       case POF_READ_FILE_HEAD:
+               if (card->debug_flags & LOG_POF_WRITE)
+                       hysdn_addlog(card, "POF write: checking file header");
+
+               if (datlen != sizeof(tPofFileHdr)) {
+                       boot->last_error = -EPOF_INTERNAL;
+                       break;
+               }
+               if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
+                       boot->last_error = -EPOF_BAD_MAGIC;
+                       break;
+               }
+               /* Setup the new state and vars */
+               boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
+               boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
+               boot->last_error = sizeof(tPofRecHdr);  /* new length */
+               break;
+
+       case POF_READ_TAG_HEAD:
+               if (card->debug_flags & LOG_POF_WRITE)
+                       hysdn_addlog(card, "POF write: checking tag header");
+
+               if (datlen != sizeof(tPofRecHdr)) {
+                       boot->last_error = -EPOF_INTERNAL;
+                       break;
+               }
+               boot->pof_recid = boot->buf.PofRecHdr.PofRecId;         /* actual pof recid */
+               boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen;   /* total length */
+               boot->pof_recoffset = 0;        /* no starting offset */
+
+               if (card->debug_flags & LOG_POF_RECORD)
+                       hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
+                                    boot->pof_recid, boot->pof_reclen);
+
+               boot->pof_state = POF_READ_TAG_DATA;    /* now start with tag data */
+               if (boot->pof_reclen < BOOT_BUF_SIZE)
+                       boot->last_error = boot->pof_reclen;    /* limit size */
+               else
+                       boot->last_error = BOOT_BUF_SIZE;       /* maximum */
+
+               if (!boot->last_error) {        /* no data inside record */
+                       boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
+                       boot->last_error = sizeof(tPofRecHdr);  /* new length */
+               }
+               break;
+
+       case POF_READ_TAG_DATA:
+               if (card->debug_flags & LOG_POF_WRITE)
+                       hysdn_addlog(card, "POF write: getting tag data");
+
+               if (datlen != boot->last_error) {
+                       boot->last_error = -EPOF_INTERNAL;
+                       break;
+               }
+               if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
+                       return (boot->last_error);      /* an error occurred */
+               boot->pof_recoffset += datlen;
+               if (boot->pof_recoffset >= boot->pof_reclen) {
+                       boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
+                       boot->last_error = sizeof(tPofRecHdr);  /* new length */
+               } else {
+                       if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
+                               boot->last_error = boot->pof_reclen - boot->pof_recoffset;      /* limit size */
+                       else
+                               boot->last_error = BOOT_BUF_SIZE;       /* maximum */
+               }
+               break;
+
+       default:
+               boot->last_error = -EPOF_INTERNAL;      /* unknown state */
+               break;
+       }                       /* switch (boot->pof_state) */
+
+       return (boot->last_error);
+}                              /* pof_write_buffer */
+
+
+/*******************************************************************************/
+/* pof_write_open is called when an open for boot on the cardlog device occurs. */
+/* The function returns the needed number of bytes for the next operation. If  */
+/* the returned number is less or equal 0 an error specified by this code      */
+/* occurred. Additionally the pointer to the buffer data area is set on success */
+/*******************************************************************************/
+int
+pof_write_open(hysdn_card *card, unsigned char **bufp)
+{
+       struct boot_data *boot; /* pointer to boot specific data */
+
+       if (card->boot) {
+               if (card->debug_flags & LOG_POF_OPEN)
+                       hysdn_addlog(card, "POF open: already opened for boot");
+               return (-ERR_ALREADY_BOOT);     /* boot already active */
+       }
+       /* error no mem available */
+       if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
+               if (card->debug_flags & LOG_MEM_ERR)
+                       hysdn_addlog(card, "POF open: unable to allocate mem");
+               return (-EFAULT);
+       }
+       card->boot = boot;
+       card->state = CARD_STATE_BOOTING;
+
+       card->stopcard(card);   /* first stop the card */
+       if (card->testram(card)) {
+               if (card->debug_flags & LOG_POF_OPEN)
+                       hysdn_addlog(card, "POF open: DPRAM test failure");
+               boot->last_error = -ERR_BOARD_DPRAM;
+               card->state = CARD_STATE_BOOTERR;       /* show boot error */
+               return (boot->last_error);
+       }
+       boot->BufSize = 0;      /* Buffer is empty */
+       boot->pof_state = POF_READ_FILE_HEAD;   /* read file header */
+       StartDecryption(boot);  /* if POF File should be encrypted */
+
+       if (card->debug_flags & LOG_POF_OPEN)
+               hysdn_addlog(card, "POF open: success");
+
+       *bufp = boot->buf.BootBuf;      /* point to buffer */
+       return (sizeof(tPofFileHdr));
+}                              /* pof_write_open */
+
+/********************************************************************************/
+/* pof_write_close is called when an close of boot on the cardlog device occurs. */
+/* The return value must be 0 if everything has happened as desired.            */
+/********************************************************************************/
+int
+pof_write_close(hysdn_card *card)
+{
+       struct boot_data *boot = card->boot;    /* pointer to boot specific data */
+
+       if (!boot)
+               return (-EFAULT);       /* invalid call */
+
+       card->boot = NULL;      /* no boot active */
+       kfree(boot);
+
+       if (card->state == CARD_STATE_RUN)
+               card->set_errlog_state(card, 1);        /* activate error log */
+
+       if (card->debug_flags & LOG_POF_OPEN)
+               hysdn_addlog(card, "POF close: success");
+
+       return (0);
+}                              /* pof_write_close */
+
+/*********************************************************************************/
+/* EvalSysrTokData checks additional records delivered with the Sysready Message */
+/* when POF has been booted. A return value of 0 is used if no error occurred.    */
+/*********************************************************************************/
+int
+EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
+{
+       u_char *p;
+       u_char crc;
+
+       if (card->debug_flags & LOG_POF_RECORD)
+               hysdn_addlog(card, "SysReady Token data length %d", len);
+
+       if (len < 2) {
+               hysdn_addlog(card, "SysReady Token Data to short");
+               return (1);
+       }
+       for (p = cp, crc = 0; p < (cp + len - 2); p++)
+               if ((crc & 0x80))
+                       crc = (((u_char) (crc << 1)) + 1) + *p;
+               else
+                       crc = ((u_char) (crc << 1)) + *p;
+       crc = ~crc;
+       if (crc != *(cp + len - 1)) {
+               hysdn_addlog(card, "SysReady Token Data invalid CRC");
+               return (1);
+       }
+       len--;                  /* don't check CRC byte */
+       while (len > 0) {
+
+               if (*cp == SYSR_TOK_END)
+                       return (0);     /* End of Token stream */
+
+               if (len < (*(cp + 1) + 2)) {
+                       hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
+                       return (1);
+               }
+               switch (*cp) {
+               case SYSR_TOK_B_CHAN:   /* 1 */
+                       if (*(cp + 1) != 1)
+                               return (1);     /* length invalid */
+                       card->bchans = *(cp + 2);
+                       break;
+
+               case SYSR_TOK_FAX_CHAN: /* 2 */
+                       if (*(cp + 1) != 1)
+                               return (1);     /* length invalid */
+                       card->faxchans = *(cp + 2);
+                       break;
+
+               case SYSR_TOK_MAC_ADDR: /* 3 */
+                       if (*(cp + 1) != 6)
+                               return (1);     /* length invalid */
+                       memcpy(card->mac_addr, cp + 2, 6);
+                       break;
+
+               default:
+                       hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
+                       break;
+               }
+               len -= (*(cp + 1) + 2);         /* adjust len */
+               cp += (*(cp + 1) + 2);  /* and pointer */
+       }
+
+       hysdn_addlog(card, "no end token found");
+       return (1);
+}                              /* EvalSysrTokData */
diff --git a/drivers/staging/isdn/hysdn/hysdn_defs.h b/drivers/staging/isdn/hysdn/hysdn_defs.h
new file mode 100644 (file)
index 0000000..cdac46a
--- /dev/null
@@ -0,0 +1,282 @@
+/* $Id: hysdn_defs.h,v 1.5.6.3 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * global definitions and exported vars and functions.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef HYSDN_DEFS_H
+#define HYSDN_DEFS_H
+
+#include <linux/hysdn_if.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+
+#include "ince1pc.h"
+
+#ifdef CONFIG_HYSDN_CAPI
+#include <linux/capi.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+
+/***************************/
+/*   CAPI-Profile values.  */
+/***************************/
+
+#define GLOBAL_OPTION_INTERNAL_CONTROLLER 0x0001
+#define GLOBAL_OPTION_EXTERNAL_CONTROLLER 0x0002
+#define GLOBAL_OPTION_HANDSET             0x0004
+#define GLOBAL_OPTION_DTMF                0x0008
+#define GLOBAL_OPTION_SUPPL_SERVICES      0x0010
+#define GLOBAL_OPTION_CHANNEL_ALLOCATION  0x0020
+#define GLOBAL_OPTION_B_CHANNEL_OPERATION 0x0040
+
+#define B1_PROT_64KBIT_HDLC        0x0001
+#define B1_PROT_64KBIT_TRANSPARENT 0x0002
+#define B1_PROT_V110_ASYNCH        0x0004
+#define B1_PROT_V110_SYNCH         0x0008
+#define B1_PROT_T30                0x0010
+#define B1_PROT_64KBIT_INV_HDLC    0x0020
+#define B1_PROT_56KBIT_TRANSPARENT 0x0040
+
+#define B2_PROT_ISO7776            0x0001
+#define B2_PROT_TRANSPARENT        0x0002
+#define B2_PROT_SDLC               0x0004
+#define B2_PROT_LAPD               0x0008
+#define B2_PROT_T30                0x0010
+#define B2_PROT_PPP                0x0020
+#define B2_PROT_TRANSPARENT_IGNORE_B1_FRAMING_ERRORS 0x0040
+
+#define B3_PROT_TRANSPARENT        0x0001
+#define B3_PROT_T90NL              0x0002
+#define B3_PROT_ISO8208            0x0004
+#define B3_PROT_X25_DCE            0x0008
+#define B3_PROT_T30                0x0010
+#define B3_PROT_T30EXT             0x0020
+
+#define HYSDN_MAXVERSION               8
+
+/* Number of sendbuffers in CAPI-queue */
+#define HYSDN_MAX_CAPI_SKB             20
+
+#endif /* CONFIG_HYSDN_CAPI*/
+
+/************************************************/
+/* constants and bits for debugging/log outputs */
+/************************************************/
+#define LOG_MAX_LINELEN 120
+#define DEB_OUT_SYSLOG  0x80000000     /* output to syslog instead of proc fs */
+#define LOG_MEM_ERR     0x00000001     /* log memory errors like kmalloc failure */
+#define LOG_POF_OPEN    0x00000010     /* log pof open and close activities */
+#define LOG_POF_RECORD  0x00000020     /* log pof record parser */
+#define LOG_POF_WRITE   0x00000040     /* log detailed pof write operation */
+#define LOG_POF_CARD    0x00000080     /* log pof related card functions */
+#define LOG_CNF_LINE    0x00000100     /* all conf lines are put to procfs */
+#define LOG_CNF_DATA    0x00000200     /* non comment conf lines are shown with channel */
+#define LOG_CNF_MISC    0x00000400     /* additional conf line debug outputs */
+#define LOG_SCHED_ASYN  0x00001000     /* debug schedulers async tx routines */
+#define LOG_PROC_OPEN   0x00100000     /* open and close from procfs are logged */
+#define LOG_PROC_ALL    0x00200000     /* all actions from procfs are logged */
+#define LOG_NET_INIT    0x00010000     /* network init and deinit logging */
+
+#define DEF_DEB_FLAGS   0x7fff000f     /* everything is logged to procfs */
+
+/**********************************/
+/* proc filesystem name constants */
+/**********************************/
+#define PROC_SUBDIR_NAME "hysdn"
+#define PROC_CONF_BASENAME "cardconf"
+#define PROC_LOG_BASENAME "cardlog"
+
+/***********************************/
+/* PCI 32 bit parms for IO and MEM */
+/***********************************/
+#define PCI_REG_PLX_MEM_BASE    0
+#define PCI_REG_PLX_IO_BASE     1
+#define PCI_REG_MEMORY_BASE     3
+
+/**************/
+/* card types */
+/**************/
+#define BD_NONE         0U
+#define BD_PERFORMANCE  1U
+#define BD_VALUE        2U
+#define BD_PCCARD       3U
+#define BD_ERGO         4U
+#define BD_METRO        5U
+#define BD_CHAMP2       6U
+#define BD_PLEXUS       7U
+
+/******************************************************/
+/* defined states for cards shown by reading cardconf */
+/******************************************************/
+#define CARD_STATE_UNUSED   0  /* never been used or booted */
+#define CARD_STATE_BOOTING  1  /* booting is in progress */
+#define CARD_STATE_BOOTERR  2  /* a previous boot was aborted */
+#define CARD_STATE_RUN      3  /* card is active */
+
+/*******************************/
+/* defines for error_log_state */
+/*******************************/
+#define ERRLOG_STATE_OFF   0   /* error log is switched off, nothing to do */
+#define ERRLOG_STATE_ON    1   /* error log is switched on, wait for data */
+#define ERRLOG_STATE_START 2   /* start error logging */
+#define ERRLOG_STATE_STOP  3   /* stop error logging */
+
+/*******************************/
+/* data structure for one card */
+/*******************************/
+typedef struct HYSDN_CARD {
+
+       /* general variables for the cards */
+       int myid;               /* own driver card id */
+       unsigned char bus;      /* pci bus the card is connected to */
+       unsigned char devfn;    /* slot+function bit encoded */
+       unsigned short subsysid;/* PCI subsystem id */
+       unsigned char brdtype;  /* type of card */
+       unsigned int bchans;    /* number of available B-channels */
+       unsigned int faxchans;  /* number of available fax-channels */
+       unsigned char mac_addr[6];/* MAC Address read from card */
+       unsigned int irq;       /* interrupt number */
+       unsigned int iobase;    /* IO-port base address */
+       unsigned long plxbase;  /* PLX memory base */
+       unsigned long membase;  /* DPRAM memory base */
+       unsigned long memend;   /* DPRAM memory end */
+       void *dpram;            /* mapped dpram */
+       int state;              /* actual state of card -> CARD_STATE_** */
+       struct HYSDN_CARD *next;        /* pointer to next card */
+
+       /* data areas for the /proc file system */
+       void *proclog;          /* pointer to proclog filesystem specific data */
+       void *procconf;         /* pointer to procconf filesystem specific data */
+
+       /* debugging and logging */
+       unsigned char err_log_state;/* actual error log state of the card */
+       unsigned long debug_flags;/* tells what should be debugged and where */
+       void (*set_errlog_state) (struct HYSDN_CARD *, int);
+
+       /* interrupt handler + interrupt synchronisation */
+       struct work_struct irq_queue;   /* interrupt task queue */
+       unsigned char volatile irq_enabled;/* interrupt enabled if != 0 */
+       unsigned char volatile hw_lock;/* hardware is currently locked -> no access */
+
+       /* boot process */
+       void *boot;             /* pointer to boot private data */
+       int (*writebootimg) (struct HYSDN_CARD *, unsigned char *, unsigned long);
+       int (*writebootseq) (struct HYSDN_CARD *, unsigned char *, int);
+       int (*waitpofready) (struct HYSDN_CARD *);
+       int (*testram) (struct HYSDN_CARD *);
+
+       /* scheduler for data transfer (only async parts) */
+       unsigned char async_data[256];/* async data to be sent (normally for config) */
+       unsigned short volatile async_len;/* length of data to sent */
+       unsigned short volatile async_channel;/* channel number for async transfer */
+       int volatile async_busy;        /* flag != 0 sending in progress */
+       int volatile net_tx_busy;       /* a network packet tx is in progress */
+
+       /* network interface */
+       void *netif;            /* pointer to network structure */
+
+       /* init and deinit stopcard for booting, too */
+       void (*stopcard) (struct HYSDN_CARD *);
+       void (*releasehardware) (struct HYSDN_CARD *);
+
+       spinlock_t hysdn_lock;
+#ifdef CONFIG_HYSDN_CAPI
+       struct hycapictrl_info {
+               char cardname[32];
+               spinlock_t lock;
+               int versionlen;
+               char versionbuf[1024];
+               char *version[HYSDN_MAXVERSION];
+
+               char infobuf[128];      /* for function procinfo */
+
+               struct HYSDN_CARD  *card;
+               struct capi_ctr capi_ctrl;
+               struct sk_buff *skbs[HYSDN_MAX_CAPI_SKB];
+               int in_idx, out_idx;    /* indexes to buffer ring */
+               int sk_count;           /* number of buffers currently in ring */
+               struct sk_buff *tx_skb; /* buffer for tx operation */
+
+               struct list_head ncci_head;
+       } *hyctrlinfo;
+#endif /* CONFIG_HYSDN_CAPI */
+} hysdn_card;
+
+#ifdef CONFIG_HYSDN_CAPI
+typedef struct hycapictrl_info hycapictrl_info;
+#endif /* CONFIG_HYSDN_CAPI */
+
+
+/*****************/
+/* exported vars */
+/*****************/
+extern hysdn_card *card_root;  /* pointer to first card */
+
+
+
+/*************************/
+/* im/exported functions */
+/*************************/
+
+/* hysdn_procconf.c */
+extern int hysdn_procconf_init(void);  /* init proc config filesys */
+extern void hysdn_procconf_release(void);      /* deinit proc config filesys */
+
+/* hysdn_proclog.c */
+extern int hysdn_proclog_init(hysdn_card *);   /* init proc log entry */
+extern void hysdn_proclog_release(hysdn_card *);       /* deinit proc log entry */
+extern void hysdn_addlog(hysdn_card *, char *, ...);   /* output data to log */
+extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int);      /* output card log */
+
+/* boardergo.c */
+extern int ergo_inithardware(hysdn_card *card);        /* get hardware -> module init */
+
+/* hysdn_boot.c */
+extern int pof_write_close(hysdn_card *);      /* close proc file after writing pof */
+extern int pof_write_open(hysdn_card *, unsigned char **);     /* open proc file for writing pof */
+extern int pof_write_buffer(hysdn_card *, int);                /* write boot data to card */
+extern int EvalSysrTokData(hysdn_card *, unsigned char *, int);                /* Check Sysready Token Data */
+
+/* hysdn_sched.c */
+extern int hysdn_sched_tx(hysdn_card *, unsigned char *,
+                         unsigned short volatile *, unsigned short volatile *,
+                         unsigned short);
+extern int hysdn_sched_rx(hysdn_card *, unsigned char *, unsigned short,
+                         unsigned short);
+extern int hysdn_tx_cfgline(hysdn_card *, unsigned char *,
+                           unsigned short);    /* send one cfg line */
+
+/* hysdn_net.c */
+extern unsigned int hynet_enable;
+extern int hysdn_net_create(hysdn_card *);     /* create a new net device */
+extern int hysdn_net_release(hysdn_card *);    /* delete the device */
+extern char *hysdn_net_getname(hysdn_card *);  /* get name of net interface */
+extern void hysdn_tx_netack(hysdn_card *);     /* acknowledge a packet tx */
+extern struct sk_buff *hysdn_tx_netget(hysdn_card *);  /* get next network packet */
+extern void hysdn_rx_netpkt(hysdn_card *, unsigned char *,
+                           unsigned short);    /* rxed packet from network */
+
+#ifdef CONFIG_HYSDN_CAPI
+extern unsigned int hycapi_enable;
+extern int hycapi_capi_create(hysdn_card *);   /* create a new capi device */
+extern int hycapi_capi_release(hysdn_card *);  /* delete the device */
+extern int hycapi_capi_stop(hysdn_card *card);   /* suspend */
+extern void hycapi_rx_capipkt(hysdn_card *card, unsigned char *buf,
+                             unsigned short len);
+extern void hycapi_tx_capiack(hysdn_card *card);
+extern struct sk_buff *hycapi_tx_capiget(hysdn_card *card);
+extern int hycapi_init(void);
+extern void hycapi_cleanup(void);
+#endif /* CONFIG_HYSDN_CAPI */
+
+#endif /* HYSDN_DEFS_H */
diff --git a/drivers/staging/isdn/hysdn/hysdn_init.c b/drivers/staging/isdn/hysdn/hysdn_init.c
new file mode 100644 (file)
index 0000000..0db2f75
--- /dev/null
@@ -0,0 +1,213 @@
+/* $Id: hysdn_init.c,v 1.6.6.6 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, init functions.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static struct pci_device_id hysdn_pci_tbl[] = {
+       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO, 0, 0, BD_METRO },
+       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, 0, 0, BD_CHAMP2 },
+       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, 0, 0, BD_ERGO },
+       { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+         PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, 0, 0, BD_ERGO },
+
+       { }                             /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, hysdn_pci_tbl);
+MODULE_DESCRIPTION("ISDN4Linux: Driver for HYSDN cards");
+MODULE_AUTHOR("Werner Cornelius");
+MODULE_LICENSE("GPL");
+
+static int cardmax;            /* number of found cards */
+hysdn_card *card_root = NULL;  /* pointer to first card */
+static hysdn_card *card_last = NULL;   /* pointer to first card */
+
+
+/****************************************************************************/
+/* The module startup and shutdown code. Only compiled when used as module. */
+/* Using the driver as module is always advisable, because the booting      */
+/* image becomes smaller and the driver code is only loaded when needed.    */
+/* Additionally newer versions may be activated without rebooting.          */
+/****************************************************************************/
+
+/****************************************************************************/
+/* init_module is called once when the module is loaded to do all necessary */
+/* things like autodetect...                                                */
+/* If the return value of this function is 0 the init has been successful   */
+/* and the module is added to the list in /proc/modules, otherwise an error */
+/* is assumed and the module will not be kept in memory.                    */
+/****************************************************************************/
+
+static int hysdn_pci_init_one(struct pci_dev *akt_pcidev,
+                             const struct pci_device_id *ent)
+{
+       hysdn_card *card;
+       int rc;
+
+       rc = pci_enable_device(akt_pcidev);
+       if (rc)
+               return rc;
+
+       if (!(card = kzalloc(sizeof(hysdn_card), GFP_KERNEL))) {
+               printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
+               rc = -ENOMEM;
+               goto err_out;
+       }
+       card->myid = cardmax;   /* set own id */
+       card->bus = akt_pcidev->bus->number;
+       card->devfn = akt_pcidev->devfn;        /* slot + function */
+       card->subsysid = akt_pcidev->subsystem_device;
+       card->irq = akt_pcidev->irq;
+       card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE);
+       card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE);
+       card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE);
+       card->brdtype = BD_NONE;        /* unknown */
+       card->debug_flags = DEF_DEB_FLAGS;      /* set default debug */
+       card->faxchans = 0;     /* default no fax channels */
+       card->bchans = 2;       /* and 2 b-channels */
+       card->brdtype = ent->driver_data;
+
+       if (ergo_inithardware(card)) {
+               printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
+               rc = -EBUSY;
+               goto err_out_card;
+       }
+
+       cardmax++;
+       card->next = NULL;      /*end of chain */
+       if (card_last)
+               card_last->next = card;         /* pointer to next card */
+       else
+               card_root = card;
+       card_last = card;       /* new chain end */
+
+       pci_set_drvdata(akt_pcidev, card);
+       return 0;
+
+err_out_card:
+       kfree(card);
+err_out:
+       pci_disable_device(akt_pcidev);
+       return rc;
+}
+
+static void hysdn_pci_remove_one(struct pci_dev *akt_pcidev)
+{
+       hysdn_card *card = pci_get_drvdata(akt_pcidev);
+
+       pci_set_drvdata(akt_pcidev, NULL);
+
+       if (card->stopcard)
+               card->stopcard(card);
+
+#ifdef CONFIG_HYSDN_CAPI
+       hycapi_capi_release(card);
+#endif
+
+       if (card->releasehardware)
+               card->releasehardware(card);   /* free all hardware resources */
+
+       if (card == card_root) {
+               card_root = card_root->next;
+               if (!card_root)
+                       card_last = NULL;
+       } else {
+               hysdn_card *tmp = card_root;
+               while (tmp) {
+                       if (tmp->next == card)
+                               tmp->next = card->next;
+                       card_last = tmp;
+                       tmp = tmp->next;
+               }
+       }
+
+       kfree(card);
+       pci_disable_device(akt_pcidev);
+}
+
+static struct pci_driver hysdn_pci_driver = {
+       .name           = "hysdn",
+       .id_table       = hysdn_pci_tbl,
+       .probe          = hysdn_pci_init_one,
+       .remove         = hysdn_pci_remove_one,
+};
+
+static int hysdn_have_procfs;
+
+static int __init
+hysdn_init(void)
+{
+       int rc;
+
+       printk(KERN_NOTICE "HYSDN: module loaded\n");
+
+       rc = pci_register_driver(&hysdn_pci_driver);
+       if (rc)
+               return rc;
+
+       printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
+
+       if (!hysdn_procconf_init())
+               hysdn_have_procfs = 1;
+
+#ifdef CONFIG_HYSDN_CAPI
+       if (cardmax > 0) {
+               if (hycapi_init()) {
+                       printk(KERN_ERR "HYCAPI: init failed\n");
+
+                       if (hysdn_have_procfs)
+                               hysdn_procconf_release();
+
+                       pci_unregister_driver(&hysdn_pci_driver);
+                       return -ESPIPE;
+               }
+       }
+#endif /* CONFIG_HYSDN_CAPI */
+
+       return 0;               /* no error */
+}                              /* init_module */
+
+
+/***********************************************************************/
+/* cleanup_module is called when the module is released by the kernel. */
+/* The routine is only called if init_module has been successful and   */
+/* the module counter has a value of 0. Otherwise this function will   */
+/* not be called. This function must release all resources still allo- */
+/* cated as after the return from this function the module code will   */
+/* be removed from memory.                                             */
+/***********************************************************************/
+static void __exit
+hysdn_exit(void)
+{
+       if (hysdn_have_procfs)
+               hysdn_procconf_release();
+
+       pci_unregister_driver(&hysdn_pci_driver);
+
+#ifdef CONFIG_HYSDN_CAPI
+       hycapi_cleanup();
+#endif /* CONFIG_HYSDN_CAPI */
+
+       printk(KERN_NOTICE "HYSDN: module unloaded\n");
+}                              /* cleanup_module */
+
+module_init(hysdn_init);
+module_exit(hysdn_exit);
diff --git a/drivers/staging/isdn/hysdn/hysdn_net.c b/drivers/staging/isdn/hysdn/hysdn_net.c
new file mode 100644 (file)
index 0000000..8e9c34f
--- /dev/null
@@ -0,0 +1,326 @@
+/* $Id: hysdn_net.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, net (ethernet type) handling routines.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This net module has been inspired by the skeleton driver from
+ * Donald Becker (becker@CESDIS.gsfc.nasa.gov)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inetdevice.h>
+
+#include "hysdn_defs.h"
+
+unsigned int hynet_enable = 0xffffffff;
+module_param(hynet_enable, uint, 0);
+
+#define MAX_SKB_BUFFERS 20     /* number of buffers for keeping TX-data */
+
+/****************************************************************************/
+/* structure containing the complete network data. The structure is aligned */
+/* in a way that both, the device and statistics are kept inside it.        */
+/* for proper access, the device structure MUST be the first var/struct     */
+/* inside the definition.                                                   */
+/****************************************************************************/
+struct net_local {
+       /* Tx control lock.  This protects the transmit buffer ring
+        * state along with the "tx full" state of the driver.  This
+        * means all netif_queue flow control actions are protected
+        * by this lock as well.
+        */
+       struct net_device *dev;
+       spinlock_t lock;
+       struct sk_buff *skbs[MAX_SKB_BUFFERS];  /* pointers to tx-skbs */
+       int in_idx, out_idx;    /* indexes to buffer ring */
+       int sk_count;           /* number of buffers currently in ring */
+};                             /* net_local */
+
+
+
+/*********************************************************************/
+/* Open/initialize the board. This is called (in the current kernel) */
+/* sometime after booting when the 'ifconfig' program is run.        */
+/* This routine should set everything up anew at each open, even     */
+/* registers that "should" only need to be set once at boot, so that */
+/* there is non-reboot way to recover if something goes wrong.       */
+/*********************************************************************/
+static int
+net_open(struct net_device *dev)
+{
+       struct in_device *in_dev;
+       hysdn_card *card = dev->ml_priv;
+       int i;
+
+       netif_start_queue(dev); /* start tx-queueing */
+
+       /* Fill in the MAC-level header (if not already set) */
+       if (!card->mac_addr[0]) {
+               for (i = 0; i < ETH_ALEN; i++)
+                       dev->dev_addr[i] = 0xfc;
+               if ((in_dev = dev->ip_ptr) != NULL) {
+                       struct in_ifaddr *ifa = in_dev->ifa_list;
+                       if (ifa != NULL)
+                               memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ifa->ifa_local)), &ifa->ifa_local, sizeof(ifa->ifa_local));
+               }
+       } else
+               memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
+
+       return (0);
+}                              /* net_open */
+
+/*******************************************/
+/* flush the currently occupied tx-buffers */
+/* must only be called when device closed  */
+/*******************************************/
+static void
+flush_tx_buffers(struct net_local *nl)
+{
+
+       while (nl->sk_count) {
+               dev_kfree_skb(nl->skbs[nl->out_idx++]);         /* free skb */
+               if (nl->out_idx >= MAX_SKB_BUFFERS)
+                       nl->out_idx = 0;        /* wrap around */
+               nl->sk_count--;
+       }
+}                              /* flush_tx_buffers */
+
+
+/*********************************************************************/
+/* close/decativate the device. The device is not removed, but only  */
+/* deactivated.                                                      */
+/*********************************************************************/
+static int
+net_close(struct net_device *dev)
+{
+
+       netif_stop_queue(dev);  /* disable queueing */
+
+       flush_tx_buffers((struct net_local *) dev);
+
+       return (0);             /* success */
+}                              /* net_close */
+
+/************************************/
+/* send a packet on this interface. */
+/* new style for kernel >= 2.3.33   */
+/************************************/
+static netdev_tx_t
+net_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+       struct net_local *lp = (struct net_local *) dev;
+
+       spin_lock_irq(&lp->lock);
+
+       lp->skbs[lp->in_idx++] = skb;   /* add to buffer list */
+       if (lp->in_idx >= MAX_SKB_BUFFERS)
+               lp->in_idx = 0; /* wrap around */
+       lp->sk_count++;         /* adjust counter */
+       netif_trans_update(dev);
+
+       /* If we just used up the very last entry in the
+        * TX ring on this device, tell the queueing
+        * layer to send no more.
+        */
+       if (lp->sk_count >= MAX_SKB_BUFFERS)
+               netif_stop_queue(dev);
+
+       /* When the TX completion hw interrupt arrives, this
+        * is when the transmit statistics are updated.
+        */
+
+       spin_unlock_irq(&lp->lock);
+
+       if (lp->sk_count <= 3) {
+               schedule_work(&((hysdn_card *) dev->ml_priv)->irq_queue);
+       }
+       return NETDEV_TX_OK;    /* success */
+}                              /* net_send_packet */
+
+
+
+/***********************************************************************/
+/* acknowlegde a packet send. The network layer will be informed about */
+/* completion                                                          */
+/***********************************************************************/
+void
+hysdn_tx_netack(hysdn_card *card)
+{
+       struct net_local *lp = card->netif;
+
+       if (!lp)
+               return;         /* non existing device */
+
+
+       if (!lp->sk_count)
+               return;         /* error condition */
+
+       lp->dev->stats.tx_packets++;
+       lp->dev->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
+
+       dev_kfree_skb(lp->skbs[lp->out_idx++]);         /* free skb */
+       if (lp->out_idx >= MAX_SKB_BUFFERS)
+               lp->out_idx = 0;        /* wrap around */
+
+       if (lp->sk_count-- == MAX_SKB_BUFFERS)  /* dec usage count */
+               netif_start_queue((struct net_device *) lp);
+}                              /* hysdn_tx_netack */
+
+/*****************************************************/
+/* we got a packet from the network, go and queue it */
+/*****************************************************/
+void
+hysdn_rx_netpkt(hysdn_card *card, unsigned char *buf, unsigned short len)
+{
+       struct net_local *lp = card->netif;
+       struct net_device *dev;
+       struct sk_buff *skb;
+
+       if (!lp)
+               return;         /* non existing device */
+
+       dev = lp->dev;
+       dev->stats.rx_bytes += len;
+
+       skb = dev_alloc_skb(len);
+       if (skb == NULL) {
+               printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+                      dev->name);
+               dev->stats.rx_dropped++;
+               return;
+       }
+       /* copy the data */
+       skb_put_data(skb, buf, len);
+
+       /* determine the used protocol */
+       skb->protocol = eth_type_trans(skb, dev);
+
+       dev->stats.rx_packets++;        /* adjust packet count */
+
+       netif_rx(skb);
+}                              /* hysdn_rx_netpkt */
+
+/*****************************************************/
+/* return the pointer to a network packet to be send */
+/*****************************************************/
+struct sk_buff *
+hysdn_tx_netget(hysdn_card *card)
+{
+       struct net_local *lp = card->netif;
+
+       if (!lp)
+               return (NULL);  /* non existing device */
+
+       if (!lp->sk_count)
+               return (NULL);  /* nothing available */
+
+       return (lp->skbs[lp->out_idx]);         /* next packet to send */
+}                              /* hysdn_tx_netget */
+
+static const struct net_device_ops hysdn_netdev_ops = {
+       .ndo_open               = net_open,
+       .ndo_stop               = net_close,
+       .ndo_start_xmit         = net_send_packet,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+
+/*****************************************************************************/
+/* hysdn_net_create creates a new net device for the given card. If a device */
+/* already exists, it will be deleted and created a new one. The return value */
+/* 0 announces success, else a negative error code will be returned.         */
+/*****************************************************************************/
+int
+hysdn_net_create(hysdn_card *card)
+{
+       struct net_device *dev;
+       int i;
+       struct net_local *lp;
+
+       if (!card) {
+               printk(KERN_WARNING "No card-pt in hysdn_net_create!\n");
+               return (-ENOMEM);
+       }
+       hysdn_net_release(card);        /* release an existing net device */
+
+       dev = alloc_etherdev(sizeof(struct net_local));
+       if (!dev) {
+               printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
+               return (-ENOMEM);
+       }
+
+       lp = netdev_priv(dev);
+       lp->dev = dev;
+
+       dev->netdev_ops = &hysdn_netdev_ops;
+       spin_lock_init(&((struct net_local *) dev)->lock);
+
+       /* initialise necessary or informing fields */
+       dev->base_addr = card->iobase;  /* IO address */
+       dev->irq = card->irq;   /* irq */
+
+       dev->netdev_ops = &hysdn_netdev_ops;
+       if ((i = register_netdev(dev))) {
+               printk(KERN_WARNING "HYSDN: unable to create network device\n");
+               free_netdev(dev);
+               return (i);
+       }
+       dev->ml_priv = card;    /* remember pointer to own data structure */
+       card->netif = dev;      /* setup the local pointer */
+
+       if (card->debug_flags & LOG_NET_INIT)
+               hysdn_addlog(card, "network device created");
+       return (0);             /* and return success */
+}                              /* hysdn_net_create */
+
+/***************************************************************************/
+/* hysdn_net_release deletes the net device for the given card. The return */
+/* value 0 announces success, else a negative error code will be returned. */
+/***************************************************************************/
+int
+hysdn_net_release(hysdn_card *card)
+{
+       struct net_device *dev = card->netif;
+
+       if (!dev)
+               return (0);     /* non existing */
+
+       card->netif = NULL;     /* clear out pointer */
+       net_close(dev);
+
+       flush_tx_buffers((struct net_local *) dev);     /* empty buffers */
+
+       unregister_netdev(dev); /* release the device */
+       free_netdev(dev);       /* release the memory allocated */
+       if (card->debug_flags & LOG_NET_INIT)
+               hysdn_addlog(card, "network device deleted");
+
+       return (0);             /* always successful */
+}                              /* hysdn_net_release */
+
+/*****************************************************************************/
+/* hysdn_net_getname returns a pointer to the name of the network interface. */
+/* if the interface is not existing, a "-" is returned.                      */
+/*****************************************************************************/
+char *
+hysdn_net_getname(hysdn_card *card)
+{
+       struct net_device *dev = card->netif;
+
+       if (!dev)
+               return ("-");   /* non existing */
+
+       return (dev->name);
+}                              /* hysdn_net_getname */
diff --git a/drivers/staging/isdn/hysdn/hysdn_pof.h b/drivers/staging/isdn/hysdn/hysdn_pof.h
new file mode 100644 (file)
index 0000000..f63f5fa
--- /dev/null
@@ -0,0 +1,78 @@
+/* $Id: hysdn_pof.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, definitions used for handling pof-files.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/************************/
+/* POF specific defines */
+/************************/
+#define BOOT_BUF_SIZE   0x1000 /* =4096, maybe moved to other h file */
+#define CRYPT_FEEDTERM  0x8142
+#define CRYPT_STARTTERM 0x81a5
+/*  max. timeout time in seconds
+ *  from end of booting to POF is ready
+ */
+#define POF_READY_TIME_OUT_SEC  10
+
+/**********************************/
+/* defines for 1.stage boot image */
+/**********************************/
+
+/*  the POF file record containing the boot loader image
+ *  has 2 pages a 16KB:
+ *  1. page contains the high 16-bit part of the 32-bit E1 words
+ *  2. page contains the low  16-bit part of the 32-bit E1 words
+ *
+ *  In each 16KB page we assume the start of the boot loader code
+ *  in the highest 2KB part (at offset 0x3800);
+ *  the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
+ */
+
+#define POF_BOOT_LOADER_PAGE_SIZE   0x4000     /* =16384U */
+#define POF_BOOT_LOADER_TOTAL_SIZE  (2U * POF_BOOT_LOADER_PAGE_SIZE)
+
+#define POF_BOOT_LOADER_CODE_SIZE   0x0800     /* =2KB =2048U */
+
+/* offset in boot page, where loader code may start */
+/* =0x3800= 14336U */
+#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
+
+
+/*--------------------------------------POF file record structs------------*/
+typedef struct PofFileHdr_tag {        /* Pof file header */
+       /*00 */ unsigned long Magic __attribute__((packed));
+       /*04 */ unsigned long N_PofRecs __attribute__((packed));
+/*08 */
+} tPofFileHdr;
+
+typedef struct PofRecHdr_tag { /* Pof record header */
+       /*00 */ unsigned short PofRecId __attribute__((packed));
+       /*02 */ unsigned long PofRecDataLen __attribute__((packed));
+/*06 */
+} tPofRecHdr;
+
+typedef struct PofTimeStamp_tag {
+       /*00 */ unsigned long UnixTime __attribute__((packed));
+       /*04 */ unsigned char DateTimeText[0x28];
+       /* =40 */
+/*2C */
+} tPofTimeStamp;
+
+/* tPofFileHdr.Magic value: */
+#define TAGFILEMAGIC 0x464F501AUL
+/* tPofRecHdr.PofRecId values: */
+#define TAG_ABSDATA  0x1000    /* abs. data */
+#define TAG_BOOTDTA  0x1001    /* boot data */
+#define TAG_COMMENT  0x0020
+#define TAG_SYSCALL  0x0021
+#define TAG_FLOWCTRL 0x0022
+#define TAG_TIMESTMP 0x0010    /* date/time stamp of version */
+#define TAG_CABSDATA 0x1100    /* crypted abs. data */
+#define TAG_CBOOTDTA 0x1101    /* crypted boot data */
diff --git a/drivers/staging/isdn/hysdn/hysdn_procconf.c b/drivers/staging/isdn/hysdn/hysdn_procconf.c
new file mode 100644 (file)
index 0000000..7307921
--- /dev/null
@@ -0,0 +1,411 @@
+/* $Id: hysdn_procconf.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/cred.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <net/net_namespace.h>
+
+#include "hysdn_defs.h"
+
+static DEFINE_MUTEX(hysdn_conf_mutex);
+
+#define INFO_OUT_LEN 80                /* length of info line including lf */
+
+/********************************************************/
+/* defines and data structure for conf write operations */
+/********************************************************/
+#define CONF_STATE_DETECT 0    /* waiting for detect */
+#define CONF_STATE_CONF   1    /* writing config data */
+#define CONF_STATE_POF    2    /* writing pof data */
+#define CONF_LINE_LEN   255    /* 255 chars max */
+
+struct conf_writedata {
+       hysdn_card *card;       /* card the device is connected to */
+       int buf_size;           /* actual number of bytes in the buffer */
+       int needed_size;        /* needed size when reading pof */
+       int state;              /* actual interface states from above constants */
+       unsigned char conf_line[CONF_LINE_LEN]; /* buffered conf line */
+       unsigned short channel;         /* active channel number */
+       unsigned char *pof_buffer;      /* buffer when writing pof */
+};
+
+/***********************************************************************/
+/* process_line parses one config line and transfers it to the card if */
+/* necessary.                                                          */
+/* if the return value is negative an error occurred.                   */
+/***********************************************************************/
+static int
+process_line(struct conf_writedata *cnf)
+{
+       unsigned char *cp = cnf->conf_line;
+       int i;
+
+       if (cnf->card->debug_flags & LOG_CNF_LINE)
+               hysdn_addlog(cnf->card, "conf line: %s", cp);
+
+       if (*cp == '-') {       /* option */
+               cp++;           /* point to option char */
+
+               if (*cp++ != 'c')
+                       return (0);     /* option unknown or used */
+               i = 0;          /* start value for channel */
+               while ((*cp <= '9') && (*cp >= '0'))
+                       i = i * 10 + *cp++ - '0';       /* get decimal number */
+               if (i > 65535) {
+                       if (cnf->card->debug_flags & LOG_CNF_MISC)
+                               hysdn_addlog(cnf->card, "conf channel invalid  %d", i);
+                       return (-ERR_INV_CHAN);         /* invalid channel */
+               }
+               cnf->channel = i & 0xFFFF;      /* set new channel number */
+               return (0);     /* success */
+       }                       /* option */
+       if (*cp == '*') {       /* line to send */
+               if (cnf->card->debug_flags & LOG_CNF_DATA)
+                       hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
+               return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
+                                        cnf->channel));        /* send the line without * */
+       }                       /* line to send */
+       return (0);
+}                              /* process_line */
+
+/***********************************/
+/* conf file operations and tables */
+/***********************************/
+
+/****************************************************/
+/* write conf file -> boot or send cfg line to card */
+/****************************************************/
+static ssize_t
+hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
+{
+       struct conf_writedata *cnf;
+       int i;
+       unsigned char ch, *cp;
+
+       if (!count)
+               return (0);     /* nothing to handle */
+
+       if (!(cnf = file->private_data))
+               return (-EFAULT);       /* should never happen */
+
+       if (cnf->state == CONF_STATE_DETECT) {  /* auto detect cnf or pof data */
+               if (copy_from_user(&ch, buf, 1))        /* get first char for detect */
+                       return (-EFAULT);
+
+               if (ch == 0x1A) {
+                       /* we detected a pof file */
+                       if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
+                               return (cnf->needed_size);      /* an error occurred -> exit */
+                       cnf->buf_size = 0;      /* buffer is empty */
+                       cnf->state = CONF_STATE_POF;    /* new state */
+               } else {
+                       /* conf data has been detected */
+                       cnf->buf_size = 0;      /* buffer is empty */
+                       cnf->state = CONF_STATE_CONF;   /* requested conf data write */
+                       if (cnf->card->state != CARD_STATE_RUN)
+                               return (-ERR_NOT_BOOTED);
+                       cnf->conf_line[CONF_LINE_LEN - 1] = 0;  /* limit string length */
+                       cnf->channel = 4098;    /* default channel for output */
+               }
+       }                       /* state was auto detect */
+       if (cnf->state == CONF_STATE_POF) {     /* pof write active */
+               i = cnf->needed_size - cnf->buf_size;   /* bytes still missing for write */
+               if (i <= 0)
+                       return (-EINVAL);       /* size error handling pof */
+
+               if (i < count)
+                       count = i;      /* limit requested number of bytes */
+               if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
+                       return (-EFAULT);       /* error while copying */
+               cnf->buf_size += count;
+
+               if (cnf->needed_size == cnf->buf_size) {
+                       cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size);  /* write data */
+                       if (cnf->needed_size <= 0) {
+                               cnf->card->state = CARD_STATE_BOOTERR;  /* show boot error */
+                               return (cnf->needed_size);      /* an error occurred */
+                       }
+                       cnf->buf_size = 0;      /* buffer is empty again */
+               }
+       }
+       /* pof write active */
+       else {                  /* conf write active */
+
+               if (cnf->card->state != CARD_STATE_RUN) {
+                       if (cnf->card->debug_flags & LOG_CNF_MISC)
+                               hysdn_addlog(cnf->card, "cnf write denied -> not booted");
+                       return (-ERR_NOT_BOOTED);
+               }
+               i = (CONF_LINE_LEN - 1) - cnf->buf_size;        /* bytes available in buffer */
+               if (i > 0) {
+                       /* copy remaining bytes into buffer */
+
+                       if (count > i)
+                               count = i;      /* limit transfer */
+                       if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
+                               return (-EFAULT);       /* error while copying */
+
+                       i = count;      /* number of chars in buffer */
+                       cp = cnf->conf_line + cnf->buf_size;
+                       while (i) {
+                               /* search for end of line */
+                               if ((*cp < ' ') && (*cp != 9))
+                                       break;  /* end of line found */
+                               cp++;
+                               i--;
+                       }       /* search for end of line */
+
+                       if (i) {
+                               /* delimiter found */
+                               *cp++ = 0;      /* string termination */
+                               count -= (i - 1);       /* subtract remaining bytes from count */
+                               while ((i) && (*cp < ' ') && (*cp != 9)) {
+                                       i--;    /* discard next char */
+                                       count++;        /* mark as read */
+                                       cp++;   /* next char */
+                               }
+                               cnf->buf_size = 0;      /* buffer is empty after transfer */
+                               if ((i = process_line(cnf)) < 0)        /* handle the line */
+                                       count = i;      /* return the error */
+                       }
+                       /* delimiter found */
+                       else {
+                               cnf->buf_size += count;         /* add chars to string */
+                               if (cnf->buf_size >= CONF_LINE_LEN - 1) {
+                                       if (cnf->card->debug_flags & LOG_CNF_MISC)
+                                               hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
+                                       return (-ERR_CONF_LONG);
+                               }
+                       }       /* not delimited */
+
+               }
+               /* copy remaining bytes into buffer */
+               else {
+                       if (cnf->card->debug_flags & LOG_CNF_MISC)
+                               hysdn_addlog(cnf->card, "cnf line too long");
+                       return (-ERR_CONF_LONG);
+               }
+       }                       /* conf write active */
+
+       return (count);
+}                              /* hysdn_conf_write */
+
+/*******************************************/
+/* read conf file -> output card info data */
+/*******************************************/
+static ssize_t
+hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t *off)
+{
+       char *cp;
+
+       if (!(file->f_mode & FMODE_READ))
+               return -EPERM;  /* no permission to read */
+
+       if (!(cp = file->private_data))
+               return -EFAULT; /* should never happen */
+
+       return simple_read_from_buffer(buf, count, off, cp, strlen(cp));
+}                              /* hysdn_conf_read */
+
+/******************/
+/* open conf file */
+/******************/
+static int
+hysdn_conf_open(struct inode *ino, struct file *filep)
+{
+       hysdn_card *card;
+       struct conf_writedata *cnf;
+       char *cp, *tmp;
+
+       /* now search the addressed card */
+       mutex_lock(&hysdn_conf_mutex);
+       card = PDE_DATA(ino);
+       if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+               hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
+                            filep->f_cred->fsuid, filep->f_cred->fsgid,
+                            filep->f_mode);
+
+       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+               /* write only access -> write boot file or conf line */
+
+               if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
+                       mutex_unlock(&hysdn_conf_mutex);
+                       return (-EFAULT);
+               }
+               cnf->card = card;
+               cnf->buf_size = 0;      /* nothing buffered */
+               cnf->state = CONF_STATE_DETECT;         /* start auto detect */
+               filep->private_data = cnf;
+
+       } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+               /* read access -> output card info data */
+
+               if (!(tmp = kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
+                       mutex_unlock(&hysdn_conf_mutex);
+                       return (-EFAULT);       /* out of memory */
+               }
+               filep->private_data = tmp;      /* start of string */
+
+               /* first output a headline */
+               sprintf(tmp, "id bus slot type irq iobase dp-mem     b-chans fax-chans state device");
+               cp = tmp;       /* start of string */
+               while (*cp)
+                       cp++;
+               while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+                       *cp++ = ' ';
+               *cp++ = '\n';
+
+               /* and now the data */
+               sprintf(cp, "%d  %3d %4d %4d %3d 0x%04x 0x%08lx %7d %9d %3d   %s",
+                       card->myid,
+                       card->bus,
+                       PCI_SLOT(card->devfn),
+                       card->brdtype,
+                       card->irq,
+                       card->iobase,
+                       card->membase,
+                       card->bchans,
+                       card->faxchans,
+                       card->state,
+                       hysdn_net_getname(card));
+               while (*cp)
+                       cp++;
+               while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+                       *cp++ = ' ';
+               *cp++ = '\n';
+               *cp = 0;        /* end of string */
+       } else {                /* simultaneous read/write access forbidden ! */
+               mutex_unlock(&hysdn_conf_mutex);
+               return (-EPERM);        /* no permission this time */
+       }
+       mutex_unlock(&hysdn_conf_mutex);
+       return nonseekable_open(ino, filep);
+}                              /* hysdn_conf_open */
+
+/***************************/
+/* close a config file.    */
+/***************************/
+static int
+hysdn_conf_close(struct inode *ino, struct file *filep)
+{
+       hysdn_card *card;
+       struct conf_writedata *cnf;
+       int retval = 0;
+
+       mutex_lock(&hysdn_conf_mutex);
+       card = PDE_DATA(ino);
+       if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+               hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
+                            filep->f_cred->fsuid, filep->f_cred->fsgid,
+                            filep->f_mode);
+
+       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+               /* write only access -> write boot file or conf line */
+               if (filep->private_data) {
+                       cnf = filep->private_data;
+
+                       if (cnf->state == CONF_STATE_POF)
+                               retval = pof_write_close(cnf->card);    /* close the pof write */
+                       kfree(filep->private_data);     /* free allocated memory for buffer */
+
+               }               /* handle write private data */
+       } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+               /* read access -> output card info data */
+
+               kfree(filep->private_data);     /* release memory */
+       }
+       mutex_unlock(&hysdn_conf_mutex);
+       return (retval);
+}                              /* hysdn_conf_close */
+
+/******************************************************/
+/* table for conf filesystem functions defined above. */
+/******************************************************/
+static const struct file_operations conf_fops =
+{
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .read           = hysdn_conf_read,
+       .write          = hysdn_conf_write,
+       .open           = hysdn_conf_open,
+       .release        = hysdn_conf_close,
+};
+
+/*****************************/
+/* hysdn subdir in /proc/net */
+/*****************************/
+struct proc_dir_entry *hysdn_proc_entry = NULL;
+
+/*******************************************************************************/
+/* hysdn_procconf_init is called when the module is loaded and after the cards */
+/* have been detected. The needed proc dir and card config files are created.  */
+/* The log init is called at last.                                             */
+/*******************************************************************************/
+int
+hysdn_procconf_init(void)
+{
+       hysdn_card *card;
+       unsigned char conf_name[20];
+
+       hysdn_proc_entry = proc_mkdir(PROC_SUBDIR_NAME, init_net.proc_net);
+       if (!hysdn_proc_entry) {
+               printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
+               return (-1);
+       }
+       card = card_root;       /* point to first card */
+       while (card) {
+
+               sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+               if ((card->procconf = (void *) proc_create_data(conf_name,
+                                                          S_IFREG | S_IRUGO | S_IWUSR,
+                                                          hysdn_proc_entry,
+                                                          &conf_fops,
+                                                          card)) != NULL) {
+                       hysdn_proclog_init(card);       /* init the log file entry */
+               }
+               card = card->next;      /* next entry */
+       }
+
+       printk(KERN_NOTICE "HYSDN: procfs initialised\n");
+       return (0);
+}                              /* hysdn_procconf_init */
+
+/*************************************************************************************/
+/* hysdn_procconf_release is called when the module is unloaded and before the cards */
+/* resources are released. The module counter is assumed to be 0 !                   */
+/*************************************************************************************/
+void
+hysdn_procconf_release(void)
+{
+       hysdn_card *card;
+       unsigned char conf_name[20];
+
+       card = card_root;       /* start with first card */
+       while (card) {
+
+               sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+               if (card->procconf)
+                       remove_proc_entry(conf_name, hysdn_proc_entry);
+
+               hysdn_proclog_release(card);    /* init the log file entry */
+
+               card = card->next;      /* point to next card */
+       }
+
+       remove_proc_entry(PROC_SUBDIR_NAME, init_net.proc_net);
+}
diff --git a/drivers/staging/isdn/hysdn/hysdn_proclog.c b/drivers/staging/isdn/hysdn/hysdn_proclog.c
new file mode 100644 (file)
index 0000000..6e898b9
--- /dev/null
@@ -0,0 +1,357 @@
+/* $Id: hysdn_proclog.c,v 1.9.6.3 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, /proc/net filesystem log functions.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+
+#include "hysdn_defs.h"
+
+/* the proc subdir for the interface is defined in the procconf module */
+extern struct proc_dir_entry *hysdn_proc_entry;
+
+static DEFINE_MUTEX(hysdn_log_mutex);
+static void put_log_buffer(hysdn_card *card, char *cp);
+
+/*************************************************/
+/* structure keeping ascii log for device output */
+/*************************************************/
+struct log_data {
+       struct log_data *next;
+       unsigned long usage_cnt;/* number of files still to work */
+       void *proc_ctrl;        /* pointer to own control procdata structure */
+       char log_start[2];      /* log string start (final len aligned by size) */
+};
+
+/**********************************************/
+/* structure holding proc entrys for one card */
+/**********************************************/
+struct procdata {
+       struct proc_dir_entry *log;     /* log entry */
+       char log_name[15];      /* log filename */
+       struct log_data *log_head, *log_tail;   /* head and tail for queue */
+       int if_used;            /* open count for interface */
+       unsigned char logtmp[LOG_MAX_LINELEN];
+       wait_queue_head_t rd_queue;
+};
+
+
+/**********************************************/
+/* log function for cards error log interface */
+/**********************************************/
+void
+hysdn_card_errlog(hysdn_card *card, tErrLogEntry *logp, int maxsize)
+{
+       char buf[ERRLOG_TEXT_SIZE + 40];
+
+       sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
+       put_log_buffer(card, buf);      /* output the string */
+}                              /* hysdn_card_errlog */
+
+/***************************************************/
+/* Log function using format specifiers for output */
+/***************************************************/
+void
+hysdn_addlog(hysdn_card *card, char *fmt, ...)
+{
+       struct procdata *pd = card->proclog;
+       char *cp;
+       va_list args;
+
+       if (!pd)
+               return;         /* log structure non existent */
+
+       cp = pd->logtmp;
+       cp += sprintf(cp, "HYSDN: card %d ", card->myid);
+
+       va_start(args, fmt);
+       cp += vsprintf(cp, fmt, args);
+       va_end(args);
+       *cp++ = '\n';
+       *cp = 0;
+
+       if (card->debug_flags & DEB_OUT_SYSLOG)
+               printk(KERN_INFO "%s", pd->logtmp);
+       else
+               put_log_buffer(card, pd->logtmp);
+
+}                              /* hysdn_addlog */
+
+/********************************************/
+/* put an log buffer into the log queue.    */
+/* This buffer will be kept until all files */
+/* opened for read got the contents.        */
+/* Flushes buffers not longer in use.       */
+/********************************************/
+static void
+put_log_buffer(hysdn_card *card, char *cp)
+{
+       struct log_data *ib;
+       struct procdata *pd = card->proclog;
+       unsigned long flags;
+
+       if (!pd)
+               return;
+       if (!cp)
+               return;
+       if (!*cp)
+               return;
+       if (pd->if_used <= 0)
+               return;         /* no open file for read */
+
+       if (!(ib = kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+               return; /* no memory */
+       strcpy(ib->log_start, cp);      /* set output string */
+       ib->next = NULL;
+       ib->proc_ctrl = pd;     /* point to own control structure */
+       spin_lock_irqsave(&card->hysdn_lock, flags);
+       ib->usage_cnt = pd->if_used;
+       if (!pd->log_head)
+               pd->log_head = ib;      /* new head */
+       else
+               pd->log_tail->next = ib;        /* follows existing messages */
+       pd->log_tail = ib;      /* new tail */
+
+       /* delete old entrys */
+       while (pd->log_head->next) {
+               if ((pd->log_head->usage_cnt <= 0) &&
+                   (pd->log_head->next->usage_cnt <= 0)) {
+                       ib = pd->log_head;
+                       pd->log_head = pd->log_head->next;
+                       kfree(ib);
+               } else {
+                       break;
+               }
+       }               /* pd->log_head->next */
+
+       spin_unlock_irqrestore(&card->hysdn_lock, flags);
+
+       wake_up_interruptible(&(pd->rd_queue));         /* announce new entry */
+}                              /* put_log_buffer */
+
+
+/******************************/
+/* file operations and tables */
+/******************************/
+
+/****************************************/
+/* write log file -> set log level bits */
+/****************************************/
+static ssize_t
+hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
+{
+       int rc;
+       hysdn_card *card = file->private_data;
+
+       rc = kstrtoul_from_user(buf, count, 0, &card->debug_flags);
+       if (rc < 0)
+               return rc;
+       hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
+       return (count);
+}                              /* hysdn_log_write */
+
+/******************/
+/* read log file */
+/******************/
+static ssize_t
+hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t *off)
+{
+       struct log_data *inf;
+       int len;
+       hysdn_card *card = PDE_DATA(file_inode(file));
+
+       if (!(inf = *((struct log_data **) file->private_data))) {
+               struct procdata *pd = card->proclog;
+               if (file->f_flags & O_NONBLOCK)
+                       return (-EAGAIN);
+
+               wait_event_interruptible(pd->rd_queue, (inf =
+                               *((struct log_data **) file->private_data)));
+       }
+       if (!inf)
+               return (0);
+
+       inf->usage_cnt--;       /* new usage count */
+       file->private_data = &inf->next;        /* next structure */
+       if ((len = strlen(inf->log_start)) <= count) {
+               if (copy_to_user(buf, inf->log_start, len))
+                       return -EFAULT;
+               *off += len;
+               return (len);
+       }
+       return (0);
+}                              /* hysdn_log_read */
+
+/******************/
+/* open log file */
+/******************/
+static int
+hysdn_log_open(struct inode *ino, struct file *filep)
+{
+       hysdn_card *card = PDE_DATA(ino);
+
+       mutex_lock(&hysdn_log_mutex);
+       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+               /* write only access -> write log level only */
+               filep->private_data = card;     /* remember our own card */
+       } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+               struct procdata *pd = card->proclog;
+               unsigned long flags;
+
+               /* read access -> log/debug read */
+               spin_lock_irqsave(&card->hysdn_lock, flags);
+               pd->if_used++;
+               if (pd->log_head)
+                       filep->private_data = &pd->log_tail->next;
+               else
+                       filep->private_data = &pd->log_head;
+               spin_unlock_irqrestore(&card->hysdn_lock, flags);
+       } else {                /* simultaneous read/write access forbidden ! */
+               mutex_unlock(&hysdn_log_mutex);
+               return (-EPERM);        /* no permission this time */
+       }
+       mutex_unlock(&hysdn_log_mutex);
+       return nonseekable_open(ino, filep);
+}                              /* hysdn_log_open */
+
+/*******************************************************************************/
+/* close a cardlog file. If the file has been opened for exclusive write it is */
+/* assumed as pof data input and the pof loader is noticed about.              */
+/* Otherwise file is handled as log output. In this case the interface usage   */
+/* count is decremented and all buffers are noticed of closing. If this file   */
+/* was the last one to be closed, all buffers are freed.                       */
+/*******************************************************************************/
+static int
+hysdn_log_close(struct inode *ino, struct file *filep)
+{
+       struct log_data *inf;
+       struct procdata *pd;
+       hysdn_card *card;
+       int retval = 0;
+
+       mutex_lock(&hysdn_log_mutex);
+       if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+               /* write only access -> write debug level written */
+               retval = 0;     /* success */
+       } else {
+               /* read access -> log/debug read, mark one further file as closed */
+
+               inf = *((struct log_data **) filep->private_data);      /* get first log entry */
+               if (inf)
+                       pd = (struct procdata *) inf->proc_ctrl;        /* still entries there */
+               else {
+                       /* no info available -> search card */
+                       card = PDE_DATA(file_inode(filep));
+                       pd = card->proclog;     /* pointer to procfs log */
+               }
+               if (pd)
+                       pd->if_used--;  /* decrement interface usage count by one */
+
+               while (inf) {
+                       inf->usage_cnt--;       /* decrement usage count for buffers */
+                       inf = inf->next;
+               }
+
+               if (pd)
+                       if (pd->if_used <= 0)   /* delete buffers if last file closed */
+                               while (pd->log_head) {
+                                       inf = pd->log_head;
+                                       pd->log_head = pd->log_head->next;
+                                       kfree(inf);
+                               }
+       }                       /* read access */
+       mutex_unlock(&hysdn_log_mutex);
+
+       return (retval);
+}                              /* hysdn_log_close */
+
+/*************************************************/
+/* select/poll routine to be able using select() */
+/*************************************************/
+static __poll_t
+hysdn_log_poll(struct file *file, poll_table *wait)
+{
+       __poll_t mask = 0;
+       hysdn_card *card = PDE_DATA(file_inode(file));
+       struct procdata *pd = card->proclog;
+
+       if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
+               return (mask);  /* no polling for write supported */
+
+       poll_wait(file, &(pd->rd_queue), wait);
+
+       if (*((struct log_data **) file->private_data))
+               mask |= EPOLLIN | EPOLLRDNORM;
+
+       return mask;
+}                              /* hysdn_log_poll */
+
+/**************************************************/
+/* table for log filesystem functions defined above. */
+/**************************************************/
+static const struct file_operations log_fops =
+{
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .read           = hysdn_log_read,
+       .write          = hysdn_log_write,
+       .poll           = hysdn_log_poll,
+       .open           = hysdn_log_open,
+       .release        = hysdn_log_close,
+};
+
+
+/***********************************************************************************/
+/* hysdn_proclog_init is called when the module is loaded after creating the cards */
+/* conf files.                                                                     */
+/***********************************************************************************/
+int
+hysdn_proclog_init(hysdn_card *card)
+{
+       struct procdata *pd;
+
+       /* create a cardlog proc entry */
+
+       if ((pd = kzalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
+               sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
+               pd->log = proc_create_data(pd->log_name,
+                                     S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry,
+                                     &log_fops, card);
+
+               init_waitqueue_head(&(pd->rd_queue));
+
+               card->proclog = (void *) pd;    /* remember procfs structure */
+       }
+       return (0);
+}                              /* hysdn_proclog_init */
+
+/************************************************************************************/
+/* hysdn_proclog_release is called when the module is unloaded and before the cards */
+/* conf file is released                                                            */
+/* The module counter is assumed to be 0 !                                          */
+/************************************************************************************/
+void
+hysdn_proclog_release(hysdn_card *card)
+{
+       struct procdata *pd;
+
+       if ((pd = (struct procdata *) card->proclog) != NULL) {
+               if (pd->log)
+                       remove_proc_entry(pd->log_name, hysdn_proc_entry);
+               kfree(pd);      /* release memory */
+               card->proclog = NULL;
+       }
+}                              /* hysdn_proclog_release */
diff --git a/drivers/staging/isdn/hysdn/hysdn_sched.c b/drivers/staging/isdn/hysdn/hysdn_sched.c
new file mode 100644 (file)
index 0000000..31d7c14
--- /dev/null
@@ -0,0 +1,197 @@
+/* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * scheduler routines for handling exchange card <-> pc.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "hysdn_defs.h"
+
+/*****************************************************************************/
+/* hysdn_sched_rx is called from the cards handler to announce new data is   */
+/* available from the card. The routine has to handle the data and return    */
+/* with a nonzero code if the data could be worked (or even thrown away), if */
+/* no room to buffer the data is available a zero return tells the card      */
+/* to keep the data until later.                                             */
+/*****************************************************************************/
+int
+hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len,
+              unsigned short chan)
+{
+
+       switch (chan) {
+       case CHAN_NDIS_DATA:
+               if (hynet_enable & (1 << card->myid)) {
+                       /* give packet to network handler */
+                       hysdn_rx_netpkt(card, buf, len);
+               }
+               break;
+
+       case CHAN_ERRLOG:
+               hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
+               if (card->err_log_state == ERRLOG_STATE_ON)
+                       card->err_log_state = ERRLOG_STATE_START;       /* start new fetch */
+               break;
+#ifdef CONFIG_HYSDN_CAPI
+       case CHAN_CAPI:
+/* give packet to CAPI handler */
+               if (hycapi_enable & (1 << card->myid)) {
+                       hycapi_rx_capipkt(card, buf, len);
+               }
+               break;
+#endif /* CONFIG_HYSDN_CAPI */
+       default:
+               printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
+               break;
+
+       }                       /* switch rx channel */
+
+       return (1);             /* always handled */
+}                              /* hysdn_sched_rx */
+
+/*****************************************************************************/
+/* hysdn_sched_tx is called from the cards handler to announce that there is */
+/* room in the tx-buffer to the card and data may be sent if needed.         */
+/* If the routine wants to send data it must fill buf, len and chan with the */
+/* appropriate data and return a nonzero value. With a zero return no new    */
+/* data to send is assumed. maxlen specifies the buffer size available for   */
+/* sending.                                                                  */
+/*****************************************************************************/
+int
+hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
+              unsigned short volatile *len, unsigned short volatile *chan,
+              unsigned short maxlen)
+{
+       struct sk_buff *skb;
+
+       if (card->net_tx_busy) {
+               card->net_tx_busy = 0;  /* reset flag */
+               hysdn_tx_netack(card);  /* acknowledge packet send */
+       }                       /* a network packet has completely been transferred */
+       /* first of all async requests are handled */
+       if (card->async_busy) {
+               if (card->async_len <= maxlen) {
+                       memcpy(buf, card->async_data, card->async_len);
+                       *len = card->async_len;
+                       *chan = card->async_channel;
+                       card->async_busy = 0;   /* reset request */
+                       return (1);
+               }
+               card->async_busy = 0;   /* in case of length error */
+       }                       /* async request */
+       if ((card->err_log_state == ERRLOG_STATE_START) &&
+           (maxlen >= ERRLOG_CMD_REQ_SIZE)) {
+               strcpy(buf, ERRLOG_CMD_REQ);    /* copy the command */
+               *len = ERRLOG_CMD_REQ_SIZE;     /* buffer length */
+               *chan = CHAN_ERRLOG;    /* and channel */
+               card->err_log_state = ERRLOG_STATE_ON;  /* new state is on */
+               return (1);     /* tell that data should be send */
+       }                       /* error log start and able to send */
+       if ((card->err_log_state == ERRLOG_STATE_STOP) &&
+           (maxlen >= ERRLOG_CMD_STOP_SIZE)) {
+               strcpy(buf, ERRLOG_CMD_STOP);   /* copy the command */
+               *len = ERRLOG_CMD_STOP_SIZE;    /* buffer length */
+               *chan = CHAN_ERRLOG;    /* and channel */
+               card->err_log_state = ERRLOG_STATE_OFF;         /* new state is off */
+               return (1);     /* tell that data should be send */
+       }                       /* error log start and able to send */
+       /* now handle network interface packets */
+       if ((hynet_enable & (1 << card->myid)) &&
+           (skb = hysdn_tx_netget(card)) != NULL)
+       {
+               if (skb->len <= maxlen) {
+                       /* copy the packet to the buffer */
+                       skb_copy_from_linear_data(skb, buf, skb->len);
+                       *len = skb->len;
+                       *chan = CHAN_NDIS_DATA;
+                       card->net_tx_busy = 1;  /* we are busy sending network data */
+                       return (1);     /* go and send the data */
+               } else
+                       hysdn_tx_netack(card);  /* aknowledge packet -> throw away */
+       }                       /* send a network packet if available */
+#ifdef CONFIG_HYSDN_CAPI
+       if (((hycapi_enable & (1 << card->myid))) &&
+           ((skb = hycapi_tx_capiget(card)) != NULL))
+       {
+               if (skb->len <= maxlen) {
+                       skb_copy_from_linear_data(skb, buf, skb->len);
+                       *len = skb->len;
+                       *chan = CHAN_CAPI;
+                       hycapi_tx_capiack(card);
+                       return (1);     /* go and send the data */
+               }
+       }
+#endif /* CONFIG_HYSDN_CAPI */
+       return (0);             /* nothing to send */
+}                              /* hysdn_sched_tx */
+
+
+/*****************************************************************************/
+/* send one config line to the card and return 0 if successful, otherwise a */
+/* negative error code.                                                      */
+/* The function works with timeouts perhaps not giving the greatest speed    */
+/* sending the line, but this should be meaningless because only some lines  */
+/* are to be sent and this happens very seldom.                              */
+/*****************************************************************************/
+int
+hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan)
+{
+       int cnt = 50;           /* timeout intervalls */
+       unsigned long flags;
+
+       if (card->debug_flags & LOG_SCHED_ASYN)
+               hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
+
+       while (card->async_busy) {
+
+               if (card->debug_flags & LOG_SCHED_ASYN)
+                       hysdn_addlog(card, "async tx-cfg delayed");
+
+               msleep_interruptible(20);               /* Timeout 20ms */
+               if (!--cnt)
+                       return (-ERR_ASYNC_TIME);       /* timed out */
+       }                       /* wait for buffer to become free */
+
+       spin_lock_irqsave(&card->hysdn_lock, flags);
+       strcpy(card->async_data, line);
+       card->async_len = strlen(line) + 1;
+       card->async_channel = chan;
+       card->async_busy = 1;   /* request transfer */
+
+       /* now queue the task */
+       schedule_work(&card->irq_queue);
+       spin_unlock_irqrestore(&card->hysdn_lock, flags);
+
+       if (card->debug_flags & LOG_SCHED_ASYN)
+               hysdn_addlog(card, "async tx-cfg data queued");
+
+       cnt++;                  /* short delay */
+
+       while (card->async_busy) {
+
+               if (card->debug_flags & LOG_SCHED_ASYN)
+                       hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
+
+               msleep_interruptible(20);               /* Timeout 20ms */
+               if (!--cnt)
+                       return (-ERR_ASYNC_TIME);       /* timed out */
+       }                       /* wait for buffer to become free again */
+
+       if (card->debug_flags & LOG_SCHED_ASYN)
+               hysdn_addlog(card, "async tx-cfg data send");
+
+       return (0);             /* line send correctly */
+}                              /* hysdn_tx_cfgline */
diff --git a/drivers/staging/isdn/hysdn/ince1pc.h b/drivers/staging/isdn/hysdn/ince1pc.h
new file mode 100644 (file)
index 0000000..cab6836
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Linux driver for HYSDN cards
+ * common definitions for both sides of the bus:
+ * - conventions both spoolers must know
+ * - channel numbers agreed upon
+ *
+ * Author    M. Steinkopf
+ * Copyright 1999 by M. Steinkopf
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __INCE1PC_H__
+#define __INCE1PC_H__
+
+/*  basic scalar definitions have same meanning,
+ *  but their declaration location depends on environment
+ */
+
+/*--------------------------------------channel numbers---------------------*/
+#define CHAN_SYSTEM     0x0001      /* system channel (spooler to spooler) */
+#define CHAN_ERRLOG     0x0005      /* error logger */
+#define CHAN_CAPI       0x0064      /* CAPI interface */
+#define CHAN_NDIS_DATA  0x1001      /* NDIS data transfer */
+
+/*--------------------------------------POF ready msg-----------------------*/
+/* NOTE: after booting POF sends system ready message to PC: */
+#define RDY_MAGIC       0x52535953UL    /* 'SYSR' reversed */
+#define RDY_MAGIC_SIZE  4               /* size in bytes */
+
+#define MAX_N_TOK_BYTES 255
+
+#define MIN_RDY_MSG_SIZE    RDY_MAGIC_SIZE
+#define MAX_RDY_MSG_SIZE    (RDY_MAGIC_SIZE + MAX_N_TOK_BYTES)
+
+#define SYSR_TOK_END            0
+#define SYSR_TOK_B_CHAN         1   /* nr. of B-Channels;   DataLen=1; def: 2 */
+#define SYSR_TOK_FAX_CHAN       2   /* nr. of FAX Channels; DataLen=1; def: 0 */
+#define SYSR_TOK_MAC_ADDR       3   /* MAC-Address; DataLen=6; def: auto */
+#define SYSR_TOK_ESC            255 /* undefined data size yet */
+/* default values, if not corrected by token: */
+#define SYSR_TOK_B_CHAN_DEF     2   /* assume 2 B-Channels */
+#define SYSR_TOK_FAX_CHAN_DEF   1   /* assume 1 FAX Channel */
+
+/*  syntax of new SYSR token stream:
+ *  channel: CHAN_SYSTEM
+ *  msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
+ *           RDY_MAGIC_SIZE   <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+ *  msg    : 0 1 2 3 {4 5 6 ..}
+ *           S Y S R  MAX_N_TOK_BYTES bytes of TokenStream
+ *
+ *  TokenStream     :=   empty
+ *                     | {NonEndTokenChunk} EndToken RotlCRC
+ *  NonEndTokenChunk:= NonEndTokenId DataLen [Data]
+ *  NonEndTokenId   := 0x01 .. 0xFE                 1 BYTE
+ *  DataLen         := 0x00 .. 0xFF                 1 BYTE
+ *  Data            := DataLen bytes
+ *  EndToken        := 0x00
+ *  RotlCRC         := special 1 byte CRC over all NonEndTokenChunk bytes
+ *                     s. RotlCRC algorithm
+ *
+ *  RotlCRC algorithm:
+ *      ucSum= 0                        1 unsigned char
+ *      for all NonEndTokenChunk bytes:
+ *          ROTL(ucSum,1)               rotate left by 1
+ *          ucSum += Char;              add current byte with swap around
+ *      RotlCRC= ~ucSum;                invert all bits for result
+ *
+ *  note:
+ *  - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
+ */
+
+/*--------------------------------------error logger------------------------*/
+/* note: pof needs final 0 ! */
+#define ERRLOG_CMD_REQ          "ERRLOG ON"
+#define ERRLOG_CMD_REQ_SIZE     10              /* with final 0 byte ! */
+#define ERRLOG_CMD_STOP         "ERRLOG OFF"
+#define ERRLOG_CMD_STOP_SIZE    11              /* with final 0 byte ! */
+
+#define ERRLOG_ENTRY_SIZE       64      /* sizeof(tErrLogEntry) */
+                                       /* remaining text size = 55 */
+#define ERRLOG_TEXT_SIZE    (ERRLOG_ENTRY_SIZE - 2 * 4 - 1)
+
+typedef struct ErrLogEntry_tag {
+
+       /*00 */ unsigned long ulErrType;
+
+       /*04 */ unsigned long ulErrSubtype;
+
+       /*08 */ unsigned char ucTextSize;
+
+       /*09 */ unsigned char ucText[ERRLOG_TEXT_SIZE];
+       /* ASCIIZ of len ucTextSize-1 */
+
+/*40 */
+} tErrLogEntry;
+
+
+#if defined(__TURBOC__)
+#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
+#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
+#endif                         /*  */
+#endif                         /*  */
+
+/*--------------------------------------DPRAM boot spooler------------------*/
+/*  this is the struture used between pc and
+ *  hyperstone to exchange boot data
+ */
+#define DPRAM_SPOOLER_DATA_SIZE 0x20
+typedef struct DpramBootSpooler_tag {
+
+       /*00 */ unsigned char Len;
+
+       /*01 */ volatile unsigned char RdPtr;
+
+       /*02 */ unsigned char WrPtr;
+
+       /*03 */ unsigned char Data[DPRAM_SPOOLER_DATA_SIZE];
+
+/*23 */
+} tDpramBootSpooler;
+
+
+#define DPRAM_SPOOLER_MIN_SIZE  5       /* Len+RdPtr+Wrptr+2*data */
+#define DPRAM_SPOOLER_DEF_SIZE  0x23    /* current default size   */
+
+/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/
+/* at DPRAM offset 0x1C00: */
+#define SIZE_RSV_SOFT_UART  0x1B0   /* 432 bytes reserved for SoftUart */
+
+
+#endif /* __INCE1PC_H__ */