[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/drivers/isdn/gigaset/Kconfig b/src/kernel/linux/v4.14/drivers/isdn/gigaset/Kconfig
new file mode 100644
index 0000000..83f62b8
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/Kconfig
@@ -0,0 +1,70 @@
+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_I4L
+	bool
+	depends on ISDN_I4L='y'||(ISDN_I4L='m'&&ISDN_DRV_GIGASET='m')
+	default !GIGASET_CAPI
+
+config GIGASET_DUMMYLL
+	bool
+	default !GIGASET_CAPI&&!GIGASET_I4L
+
+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/src/kernel/linux/v4.14/drivers/isdn/gigaset/Makefile b/src/kernel/linux/v4.14/drivers/isdn/gigaset/Makefile
new file mode 100644
index 0000000..ac45a27
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
+gigaset-$(CONFIG_GIGASET_CAPI) += capi.o
+gigaset-$(CONFIG_GIGASET_I4L) += i4l.o
+gigaset-$(CONFIG_GIGASET_DUMMYLL) += dummyll.o
+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/src/kernel/linux/v4.14/drivers/isdn/gigaset/asyncdata.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/asyncdata.c
new file mode 100644
index 0000000..bc20855
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/asyncdata.c
@@ -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;
+			}
+			/* --v-- fall through --v-- */
+		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 */
+			/* --v-- fall through --v-- */
+		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/src/kernel/linux/v4.14/drivers/isdn/gigaset/bas-gigaset.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/bas-gigaset.c
new file mode 100644
index 0000000..2da3ff6
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/bas-gigaset.c
@@ -0,0 +1,2679 @@
+/*
+ * 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 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(unsigned long data)
+{
+	struct cardstate *cs = (struct cardstate *) data;
+	struct bas_cardstate *ucs = cs->hw.bas;
+	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(unsigned long data)
+{
+	struct cardstate *cs = (struct cardstate *) data;
+	struct bas_cardstate *ucs = cs->hw.bas;
+	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;
+	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;
+	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;
+	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 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;
+		}
+
+		urb->dev = bcs->cs->hw.bas->udev;
+		urb->pipe = usb_rcvisocpipe(urb->dev, 3 + 2 * bcs->channel);
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->transfer_buffer = ubc->isoinbuf + k * BAS_INBUFSIZE;
+		urb->transfer_buffer_length = BAS_INBUFSIZE;
+		urb->number_of_packets = BAS_NUMFRAMES;
+		urb->interval = BAS_FRAMETIME;
+		urb->complete = read_iso_callback;
+		urb->context = bcs;
+		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;
+		}
+		urb->dev = bcs->cs->hw.bas->udev;
+		urb->pipe = usb_sndisocpipe(urb->dev, 4 + 2 * bcs->channel);
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->transfer_buffer = ubc->isooutbuf->data;
+		urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data);
+		urb->number_of_packets = BAS_NUMFRAMES;
+		urb->interval = BAS_FRAMETIME;
+		urb->complete = write_iso_callback;
+		urb->context = &ubc->isoouturbs[k];
+		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(unsigned long data)
+{
+	struct cardstate *cs = (struct cardstate *) data;
+	struct bas_cardstate *ucs = cs->hw.bas;
+	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(unsigned long data)
+{
+	struct cardstate *cs = (struct cardstate *) data;
+	struct bas_cardstate *ucs = cs->hw.bas;
+
+	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 = kmalloc(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;
+	}
+
+	ucs->urb_cmd_in = NULL;
+	ucs->urb_cmd_out = NULL;
+	ucs->rcvbuf = NULL;
+	ucs->rcvbuf_size = 0;
+
+	spin_lock_init(&ucs->lock);
+	ucs->pending = 0;
+
+	ucs->basstate = 0;
+	setup_timer(&ucs->timer_ctrl, req_timeout, (unsigned long) cs);
+	setup_timer(&ucs->timer_atrdy, atrdy_timeout, (unsigned long) cs);
+	setup_timer(&ucs->timer_cmd_in, cmd_in_timeout, (unsigned long) cs);
+	setup_timer(&ucs->timer_int_in, int_in_resubmit, (unsigned long) cs);
+	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/src/kernel/linux/v4.14/drivers/isdn/gigaset/capi.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/capi.c
new file mode 100644
index 0000000..ccec777
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/capi.c
@@ -0,0 +1,2533 @@
+/*
+ * 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(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(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 = kzalloc(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;
+}
+
+static int gigaset_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, gigaset_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations gigaset_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= gigaset_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+/**
+ * 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_fops = &gigaset_proc_fops;
+	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/src/kernel/linux/v4.14/drivers/isdn/gigaset/common.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/common.c
new file mode 100644
index 0000000..7c78144
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/common.c
@@ -0,0 +1,1157 @@
+/*
+ * 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(unsigned long data)
+{
+	struct cardstate *cs = (struct cardstate *) data;
+	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;
+	init_timer(&cs->timer); /* clear next & prev */
+	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(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);
+	setup_timer(&cs->timer, timer_tick, (unsigned long) cs);
+	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(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/src/kernel/linux/v4.14/drivers/isdn/gigaset/dummyll.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/dummyll.c
new file mode 100644
index 0000000..570c2d5
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/dummyll.c
@@ -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/src/kernel/linux/v4.14/drivers/isdn/gigaset/ev-layer.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/ev-layer.c
new file mode 100644
index 0000000..1cfcea6
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/ev-layer.c
@@ -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);
+		/* --v-- fall through --v-- */
+	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;
+		}
+		/* bad cid: fall through */
+	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/src/kernel/linux/v4.14/drivers/isdn/gigaset/gigaset.h b/src/kernel/linux/v4.14/drivers/isdn/gigaset/gigaset.h
new file mode 100644
index 0000000..166537e
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/gigaset.h
@@ -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/src/kernel/linux/v4.14/drivers/isdn/gigaset/i4l.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/i4l.c
new file mode 100644
index 0000000..2d75329
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/i4l.c
@@ -0,0 +1,695 @@
+/*
+ * 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/isdnif.h>
+#include <linux/export.h>
+
+#define SBUFSIZE	4096	/* sk_buff payload size */
+#define TRANSBUFSIZE	768	/* bytes per skb for transparent receive */
+#define HW_HDR_LEN	2	/* Header size used to store ack info */
+#define MAX_BUF_SIZE	(SBUFSIZE - HW_HDR_LEN)	/* max data packet from LL */
+
+/* == Handling of I4L IO =====================================================*/
+
+/* writebuf_from_LL
+ * called by LL to transmit data on an open channel
+ * inserts the buffer data into the send queue and starts the transmission
+ * Note that this operation must not sleep!
+ * When the buffer is processed completely, gigaset_skb_sent() should be called.
+ * parameters:
+ *	driverID	driver ID as assigned by LL
+ *	channel		channel number
+ *	ack		if != 0 LL wants to be notified on completion via
+ *			statcallb(ISDN_STAT_BSENT)
+ *	skb		skb containing data to send
+ * return value:
+ *	number of accepted bytes
+ *	0 if temporarily unable to accept data (out of buffer space)
+ *	<0 on error (eg. -EINVAL)
+ */
+static int writebuf_from_LL(int driverID, int channel, int ack,
+			    struct sk_buff *skb)
+{
+	struct cardstate *cs = gigaset_get_cs_by_id(driverID);
+	struct bc_state *bcs;
+	unsigned char *ack_header;
+	unsigned len;
+
+	if (!cs) {
+		pr_err("%s: invalid driver ID (%d)\n", __func__, driverID);
+		return -ENODEV;
+	}
+	if (channel < 0 || channel >= cs->channels) {
+		dev_err(cs->dev, "%s: invalid channel ID (%d)\n",
+			__func__, channel);
+		return -ENODEV;
+	}
+	bcs = &cs->bcs[channel];
+
+	/* can only handle linear sk_buffs */
+	if (skb_linearize(skb) < 0) {
+		dev_err(cs->dev, "%s: skb_linearize failed\n", __func__);
+		return -ENOMEM;
+	}
+	len = skb->len;
+
+	gig_dbg(DEBUG_LLDATA,
+		"Receiving data from LL (id: %d, ch: %d, ack: %d, sz: %d)",
+		driverID, channel, ack, len);
+
+	if (!len) {
+		if (ack)
+			dev_notice(cs->dev, "%s: not ACKing empty packet\n",
+				   __func__);
+		return 0;
+	}
+	if (len > MAX_BUF_SIZE) {
+		dev_err(cs->dev, "%s: packet too large (%d bytes)\n",
+			__func__, len);
+		return -EINVAL;
+	}
+
+	/* set up acknowledgement header */
+	if (skb_headroom(skb) < HW_HDR_LEN) {
+		/* should never happen */
+		dev_err(cs->dev, "%s: insufficient skb headroom\n", __func__);
+		return -ENOMEM;
+	}
+	skb_set_mac_header(skb, -HW_HDR_LEN);
+	skb->mac_len = HW_HDR_LEN;
+	ack_header = skb_mac_header(skb);
+	if (ack) {
+		ack_header[0] = len & 0xff;
+		ack_header[1] = len >> 8;
+	} else {
+		ack_header[0] = ack_header[1] = 0;
+	}
+	gig_dbg(DEBUG_MCMD, "skb: len=%u, ack=%d: %02x %02x",
+		len, ack, ack_header[0], ack_header[1]);
+
+	/* pass to device-specific module */
+	return cs->ops->send_skb(bcs, skb);
+}
+
+/**
+ * gigaset_skb_sent() - acknowledge sending an 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 *skb)
+{
+	isdn_if *iif = bcs->cs->iif;
+	unsigned char *ack_header = skb_mac_header(skb);
+	unsigned len;
+	isdn_ctrl response;
+
+	++bcs->trans_up;
+
+	if (skb->len)
+		dev_warn(bcs->cs->dev, "%s: skb->len==%d\n",
+			 __func__, skb->len);
+
+	len = ack_header[0] + ((unsigned) ack_header[1] << 8);
+	if (len) {
+		gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)",
+			bcs->cs->myid, bcs->channel, len);
+
+		response.driver = bcs->cs->myid;
+		response.command = ISDN_STAT_BSENT;
+		response.arg = bcs->channel;
+		response.parm.length = len;
+		iif->statcallb(&response);
+	}
+}
+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)
+{
+	isdn_if *iif = bcs->cs->iif;
+
+	iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb);
+	bcs->trans_down++;
+}
+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)
+{
+	isdn_if *iif = bcs->cs->iif;
+	isdn_ctrl response;
+
+	/* if currently ignoring packets, just count down */
+	if (bcs->ignore) {
+		bcs->ignore--;
+		return;
+	}
+
+	/* update statistics */
+	bcs->corrupted++;
+
+	/* error -> LL */
+	gig_dbg(DEBUG_CMD, "sending L1ERR");
+	response.driver = bcs->cs->myid;
+	response.command = ISDN_STAT_L1ERR;
+	response.arg = bcs->channel;
+	response.parm.errcode = ISDN_STAT_L1ERR_RECV;
+	iif->statcallb(&response);
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
+/* This function will be called by LL to send commands
+ * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL,
+ * so don't put too much effort into it.
+ */
+static int command_from_LL(isdn_ctrl *cntrl)
+{
+	struct cardstate *cs;
+	struct bc_state *bcs;
+	int retval = 0;
+	char **commands;
+	int ch;
+	int i;
+	size_t l;
+
+	gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx",
+		cntrl->driver, cntrl->command, cntrl->arg);
+
+	cs = gigaset_get_cs_by_id(cntrl->driver);
+	if (cs == NULL) {
+		pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
+		return -ENODEV;
+	}
+	ch = cntrl->arg & 0xff;
+
+	switch (cntrl->command) {
+	case ISDN_CMD_IOCTL:
+		dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
+		return -EINVAL;
+
+	case ISDN_CMD_DIAL:
+		gig_dbg(DEBUG_CMD,
+			"ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
+			cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
+			cntrl->parm.setup.si1, cntrl->parm.setup.si2);
+
+		if (ch >= cs->channels) {
+			dev_err(cs->dev,
+				"ISDN_CMD_DIAL: invalid channel (%d)\n", ch);
+			return -EINVAL;
+		}
+		bcs = cs->bcs + ch;
+		if (gigaset_get_channel(bcs) < 0) {
+			dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
+			return -EBUSY;
+		}
+		switch (bcs->proto2) {
+		case L2_HDLC:
+			bcs->rx_bufsize = SBUFSIZE;
+			break;
+		default:			/* assume transparent */
+			bcs->rx_bufsize = TRANSBUFSIZE;
+		}
+		dev_kfree_skb(bcs->rx_skb);
+		gigaset_new_rx_skb(bcs);
+
+		commands = kzalloc(AT_NUM * (sizeof *commands), GFP_ATOMIC);
+		if (!commands) {
+			gigaset_free_channel(bcs);
+			dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
+			return -ENOMEM;
+		}
+
+		l = 3 + strlen(cntrl->parm.setup.phone);
+		commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC);
+		if (!commands[AT_DIAL])
+			goto oom;
+		if (cntrl->parm.setup.phone[0] == '*' &&
+		    cntrl->parm.setup.phone[1] == '*') {
+			/* internal call: translate ** prefix to CTP value */
+			commands[AT_TYPE] = kstrdup("^SCTP=0\r", GFP_ATOMIC);
+			if (!commands[AT_TYPE])
+				goto oom;
+			snprintf(commands[AT_DIAL], l,
+				 "D%s\r", cntrl->parm.setup.phone + 2);
+		} else {
+			commands[AT_TYPE] = kstrdup("^SCTP=1\r", GFP_ATOMIC);
+			if (!commands[AT_TYPE])
+				goto oom;
+			snprintf(commands[AT_DIAL], l,
+				 "D%s\r", cntrl->parm.setup.phone);
+		}
+
+		l = strlen(cntrl->parm.setup.eazmsn);
+		if (l) {
+			l += 8;
+			commands[AT_MSN] = kmalloc(l, GFP_ATOMIC);
+			if (!commands[AT_MSN])
+				goto oom;
+			snprintf(commands[AT_MSN], l, "^SMSN=%s\r",
+				 cntrl->parm.setup.eazmsn);
+		}
+
+		switch (cntrl->parm.setup.si1) {
+		case 1:		/* audio */
+			/* BC = 9090A3: 3.1 kHz audio, A-law */
+			commands[AT_BC] = kstrdup("^SBC=9090A3\r", GFP_ATOMIC);
+			if (!commands[AT_BC])
+				goto oom;
+			break;
+		case 7:		/* data */
+		default:	/* hope the app knows what it is doing */
+			/* BC = 8890: unrestricted digital information */
+			commands[AT_BC] = kstrdup("^SBC=8890\r", GFP_ATOMIC);
+			if (!commands[AT_BC])
+				goto oom;
+		}
+		/* ToDo: other si1 values, inspect si2, set HLC/LLC */
+
+		commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
+		if (!commands[AT_PROTO])
+			goto oom;
+		snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+
+		commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
+		if (!commands[AT_ISO])
+			goto oom;
+		snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
+			 (unsigned) bcs->channel + 1);
+
+		if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
+				       bcs->at_state.seq_index, NULL)) {
+			for (i = 0; i < AT_NUM; ++i)
+				kfree(commands[i]);
+			kfree(commands);
+			gigaset_free_channel(bcs);
+			return -ENOMEM;
+		}
+		gigaset_schedule_event(cs);
+		break;
+	case ISDN_CMD_ACCEPTD:
+		gig_dbg(DEBUG_CMD, "ISDN_CMD_ACCEPTD");
+		if (ch >= cs->channels) {
+			dev_err(cs->dev,
+				"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
+			return -EINVAL;
+		}
+		bcs = cs->bcs + ch;
+		switch (bcs->proto2) {
+		case L2_HDLC:
+			bcs->rx_bufsize = SBUFSIZE;
+			break;
+		default:			/* assume transparent */
+			bcs->rx_bufsize = TRANSBUFSIZE;
+		}
+		dev_kfree_skb(bcs->rx_skb);
+		gigaset_new_rx_skb(bcs);
+		if (!gigaset_add_event(cs, &bcs->at_state,
+				       EV_ACCEPT, NULL, 0, NULL))
+			return -ENOMEM;
+		gigaset_schedule_event(cs);
+
+		break;
+	case ISDN_CMD_HANGUP:
+		gig_dbg(DEBUG_CMD, "ISDN_CMD_HANGUP");
+		if (ch >= cs->channels) {
+			dev_err(cs->dev,
+				"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
+			return -EINVAL;
+		}
+		bcs = cs->bcs + ch;
+		if (!gigaset_add_event(cs, &bcs->at_state,
+				       EV_HUP, NULL, 0, NULL))
+			return -ENOMEM;
+		gigaset_schedule_event(cs);
+
+		break;
+	case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
+		dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
+		break;
+	case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
+		dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
+			 cntrl->parm.num);
+		break;
+	case ISDN_CMD_SETL2: /* Set L2 to given protocol */
+		if (ch >= cs->channels) {
+			dev_err(cs->dev,
+				"ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
+			return -EINVAL;
+		}
+		bcs = cs->bcs + ch;
+		if (bcs->chstate & CHS_D_UP) {
+			dev_err(cs->dev,
+				"ISDN_CMD_SETL2: channel active (%d)\n", ch);
+			return -EINVAL;
+		}
+		switch (cntrl->arg >> 8) {
+		case ISDN_PROTO_L2_HDLC:
+			gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC");
+			bcs->proto2 = L2_HDLC;
+			break;
+		case ISDN_PROTO_L2_TRANS:
+			gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE");
+			bcs->proto2 = L2_VOICE;
+			break;
+		default:
+			dev_err(cs->dev,
+				"ISDN_CMD_SETL2: unsupported protocol (%lu)\n",
+				cntrl->arg >> 8);
+			return -EINVAL;
+		}
+		break;
+	case ISDN_CMD_SETL3: /* Set L3 to given protocol */
+		gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL3");
+		if (ch >= cs->channels) {
+			dev_err(cs->dev,
+				"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
+			return -EINVAL;
+		}
+
+		if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
+			dev_err(cs->dev,
+				"ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
+				cntrl->arg >> 8);
+			return -EINVAL;
+		}
+
+		break;
+
+	default:
+		gig_dbg(DEBUG_CMD, "unknown command %d from LL",
+			cntrl->command);
+		return -EINVAL;
+	}
+
+	return retval;
+
+oom:
+	dev_err(bcs->cs->dev, "out of memory\n");
+	for (i = 0; i < AT_NUM; ++i)
+		kfree(commands[i]);
+	kfree(commands);
+	gigaset_free_channel(bcs);
+	return -ENOMEM;
+}
+
+static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
+{
+	isdn_if *iif = cs->iif;
+	isdn_ctrl command;
+
+	command.driver = cs->myid;
+	command.command = cmd;
+	command.arg = 0;
+	iif->statcallb(&command);
+}
+
+static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
+{
+	isdn_if *iif = bcs->cs->iif;
+	isdn_ctrl command;
+
+	command.driver = bcs->cs->myid;
+	command.command = cmd;
+	command.arg = bcs->channel;
+	iif->statcallb(&command);
+}
+
+/**
+ * gigaset_isdn_icall() - signal incoming call
+ * @at_state:	connection state structure.
+ *
+ * Called by main module 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;
+	isdn_if *iif = cs->iif;
+	isdn_ctrl response;
+	int retval;
+
+	/* fill ICALL structure */
+	response.parm.setup.si1 = 0;	/* default: unknown */
+	response.parm.setup.si2 = 0;
+	response.parm.setup.screen = 0;
+	response.parm.setup.plan = 0;
+	if (!at_state->str_var[STR_ZBC]) {
+		/* no BC (internal call): assume speech, A-law */
+		response.parm.setup.si1 = 1;
+	} else if (!strcmp(at_state->str_var[STR_ZBC], "8890")) {
+		/* unrestricted digital information */
+		response.parm.setup.si1 = 7;
+	} else if (!strcmp(at_state->str_var[STR_ZBC], "8090A3")) {
+		/* speech, A-law */
+		response.parm.setup.si1 = 1;
+	} else if (!strcmp(at_state->str_var[STR_ZBC], "9090A3")) {
+		/* 3,1 kHz audio, A-law */
+		response.parm.setup.si1 = 1;
+		response.parm.setup.si2 = 2;
+	} else {
+		dev_warn(cs->dev, "RING ignored - unsupported BC %s\n",
+			 at_state->str_var[STR_ZBC]);
+		return ICALL_IGNORE;
+	}
+	if (at_state->str_var[STR_NMBR]) {
+		strlcpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
+			sizeof response.parm.setup.phone);
+	} else
+		response.parm.setup.phone[0] = 0;
+	if (at_state->str_var[STR_ZCPN]) {
+		strlcpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
+			sizeof response.parm.setup.eazmsn);
+	} else
+		response.parm.setup.eazmsn[0] = 0;
+
+	if (!bcs) {
+		dev_notice(cs->dev, "no channel for incoming call\n");
+		response.command = ISDN_STAT_ICALLW;
+		response.arg = 0;
+	} else {
+		gig_dbg(DEBUG_CMD, "Sending ICALL");
+		response.command = ISDN_STAT_ICALL;
+		response.arg = bcs->channel;
+	}
+	response.driver = cs->myid;
+	retval = iif->statcallb(&response);
+	gig_dbg(DEBUG_CMD, "Response: %d", retval);
+	switch (retval) {
+	case 0:	/* no takers */
+		return ICALL_IGNORE;
+	case 1:	/* alerting */
+		bcs->chstate |= CHS_NOTIFY_LL;
+		return ICALL_ACCEPT;
+	case 2:	/* reject */
+		return ICALL_REJECT;
+	case 3:	/* incomplete */
+		dev_warn(cs->dev,
+			 "LL requested unsupported feature: Incomplete Number\n");
+		return ICALL_IGNORE;
+	case 4:	/* proceeding */
+		/* Gigaset will send ALERTING anyway.
+		 * There doesn't seem to be a way to avoid this.
+		 */
+		return ICALL_ACCEPT;
+	case 5:	/* deflect */
+		dev_warn(cs->dev,
+			 "LL requested unsupported feature: Call Deflection\n");
+		return ICALL_IGNORE;
+	default:
+		dev_err(cs->dev, "LL error %d on ICALL\n", retval);
+		return ICALL_IGNORE;
+	}
+}
+
+/**
+ * gigaset_isdn_connD() - signal D channel connect
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the D channel connection has
+ * been established.
+ */
+void gigaset_isdn_connD(struct bc_state *bcs)
+{
+	gig_dbg(DEBUG_CMD, "sending DCONN");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+}
+
+/**
+ * gigaset_isdn_hupD() - signal D channel hangup
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the D channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+	gig_dbg(DEBUG_CMD, "sending DHUP");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+}
+
+/**
+ * gigaset_isdn_connB() - signal B channel connect
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been established.
+ */
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+	gig_dbg(DEBUG_CMD, "sending BCONN");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
+}
+
+/**
+ * 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)
+{
+	gig_dbg(DEBUG_CMD, "sending BHUP");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
+}
+
+/**
+ * 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)
+{
+	gig_dbg(DEBUG_CMD, "sending RUN");
+	gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
+}
+
+/**
+ * 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)
+{
+	gig_dbg(DEBUG_CMD, "sending STOP");
+	gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
+}
+
+/**
+ * gigaset_isdn_regdev() - register 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)
+{
+	isdn_if *iif;
+
+	iif = kmalloc(sizeof *iif, GFP_KERNEL);
+	if (!iif) {
+		pr_err("out of memory\n");
+		return -ENOMEM;
+	}
+
+	if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
+	    >= sizeof iif->id) {
+		pr_err("ID too long: %s\n", isdnid);
+		kfree(iif);
+		return -EINVAL;
+	}
+
+	iif->owner = THIS_MODULE;
+	iif->channels = cs->channels;
+	iif->maxbufsize = MAX_BUF_SIZE;
+	iif->features = ISDN_FEATURE_L2_TRANS |
+		ISDN_FEATURE_L2_HDLC |
+		ISDN_FEATURE_L2_X75I |
+		ISDN_FEATURE_L3_TRANS |
+		ISDN_FEATURE_P_EURO;
+	iif->hl_hdrlen = HW_HDR_LEN;		/* Area for storing ack */
+	iif->command = command_from_LL;
+	iif->writebuf_skb = writebuf_from_LL;
+	iif->writecmd = NULL;			/* Don't support isdnctrl */
+	iif->readstat = NULL;			/* Don't support isdnctrl */
+	iif->rcvcallb_skb = NULL;		/* Will be set by LL */
+	iif->statcallb = NULL;			/* Will be set by LL */
+
+	if (!register_isdn(iif)) {
+		pr_err("register_isdn failed\n");
+		kfree(iif);
+		return -EINVAL;
+	}
+
+	cs->iif = iif;
+	cs->myid = iif->channels;		/* Set my device id */
+	cs->hw_hdr_len = HW_HDR_LEN;
+	return 0;
+}
+
+/**
+ * gigaset_isdn_unregdev() - unregister device from LL
+ * @cs:		device descriptor structure.
+ */
+void gigaset_isdn_unregdev(struct cardstate *cs)
+{
+	gig_dbg(DEBUG_CMD, "sending UNLOAD");
+	gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
+	kfree(cs->iif);
+	cs->iif = NULL;
+}
+
+/**
+ * gigaset_isdn_regdrv() - register driver to LL
+ */
+void gigaset_isdn_regdrv(void)
+{
+	pr_info("ISDN4Linux interface\n");
+	/* nothing to do */
+}
+
+/**
+ * gigaset_isdn_unregdrv() - unregister driver from LL
+ */
+void gigaset_isdn_unregdrv(void)
+{
+	/* nothing to do */
+}
diff --git a/src/kernel/linux/v4.14/drivers/isdn/gigaset/interface.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/interface.c
new file mode 100644
index 0000000..600c79b
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/interface.c
@@ -0,0 +1,605 @@
+/*
+ * 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, (const unsigned char *) arg);
+				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;
+}
+
+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,
+	.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/src/kernel/linux/v4.14/drivers/isdn/gigaset/isocdata.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/isocdata.c
new file mode 100644
index 0000000..97e0011
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/isocdata.c
@@ -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;
+			}
+			/* --v-- fall through --v-- */
+		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/src/kernel/linux/v4.14/drivers/isdn/gigaset/proc.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/proc.c
new file mode 100644
index 0000000..e3f9d0f
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/proc.c
@@ -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/src/kernel/linux/v4.14/drivers/isdn/gigaset/ser-gigaset.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/ser-gigaset.c
new file mode 100644
index 0000000..ab0b63a
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/ser-gigaset.c
@@ -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;
+		}
+		/* Pass 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/src/kernel/linux/v4.14/drivers/isdn/gigaset/usb-gigaset.c b/src/kernel/linux/v4.14/drivers/isdn/gigaset/usb-gigaset.c
new file mode 100644
index 0000000..4c239f1
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/isdn/gigaset/usb-gigaset.c
@@ -0,0 +1,962 @@
+/*
+ * 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 = kzalloc(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;
+	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;
+	}
+
+	if (hostif->desc.bNumEndpoints < 2) {
+		dev_err(&interface->dev, "missing endpoints\n");
+		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;
+
+	if (!usb_endpoint_is_bulk_out(endpoint)) {
+		dev_err(&interface->dev, "missing bulk-out endpoint\n");
+		retval = -ENODEV;
+		goto error;
+	}
+
+	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;
+
+	if (!usb_endpoint_is_int_in(endpoint)) {
+		dev_err(&interface->dev, "missing int-in endpoint\n");
+		retval = -ENODEV;
+		goto error;
+	}
+
+	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");