From 69e656cc16511719a89d83373c48172d3f39bc5f Mon Sep 17 00:00:00 2001
From: Karsten Keil <kkeil@suse.de>
Date: Wed, 7 Jan 2009 00:00:59 +0100
Subject: mISDN: Cleanup hfc multiport driver

Remove references to external zaptel modules and some
cosmetic cleanups.

Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfc_multi.h |   4 --
 drivers/isdn/hardware/mISDN/hfcmulti.c  | 109 +++++++-------------------------
 2 files changed, 24 insertions(+), 89 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
index 7bbf7300593d..6d74951a1797 100644
--- a/drivers/isdn/hardware/mISDN/hfc_multi.h
+++ b/drivers/isdn/hardware/mISDN/hfc_multi.h
@@ -2,10 +2,6 @@
  * see notice in hfc_multi.c
  */
 
-extern void ztdummy_extern_interrupt(void);
-extern void ztdummy_register_interrupt(void);
-extern int ztdummy_unregister_interrupt(void);
-
 #define DEBUG_HFCMULTI_FIFO	0x00010000
 #define	DEBUG_HFCMULTI_CRC	0x00020000
 #define	DEBUG_HFCMULTI_INIT	0x00040000
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index c63e2f49da8a..ff5ec3cbeb77 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -140,7 +140,7 @@
  * #define HFC_REGISTER_DEBUG
  */
 
-static const char *hfcmulti_revision = "2.02";
+#define HFC_MULTI_VERSION	"2.03"
 
 #include <linux/module.h>
 #include <linux/pci.h>
@@ -165,10 +165,6 @@ static LIST_HEAD(HFClist);
 static spinlock_t HFClock; /* global hfc list lock */
 
 static void ph_state_change(struct dchannel *);
-static void (*hfc_interrupt)(void);
-static void (*register_interrupt)(void);
-static int (*unregister_interrupt)(void);
-static int interrupt_registered;
 
 static struct hfc_multi *syncmaster;
 static int plxsd_master; /* if we have a master card (yet) */
@@ -209,6 +205,7 @@ static int	HFC_cnt, Port_cnt, PCM_cnt = 99;
 
 MODULE_AUTHOR("Andreas Eversberg");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(HFC_MULTI_VERSION);
 module_param(debug, uint, S_IRUGO | S_IWUSR);
 module_param(poll, uint, S_IRUGO | S_IWUSR);
 module_param(timer, uint, S_IRUGO | S_IWUSR);
@@ -1419,19 +1416,6 @@ controller_fail:
 	HFC_outb(hc, R_TI_WD, poll_timer);
 	hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
 
-	/*
-	 * set up 125us interrupt, only if function pointer is available
-	 * and module parameter timer is set
-	 */
-	if (timer && hfc_interrupt && register_interrupt) {
-		/* only one chip should use this interrupt */
-		timer = 0;
-		interrupt_registered = 1;
-		hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK;
-		/* deactivate other interrupts in ztdummy */
-		register_interrupt();
-	}
-
 	/* set E1 state machine IRQ */
 	if (hc->type == 1)
 		hc->hw.r_irqmsk_misc |= V_STA_IRQMSK;
@@ -2583,7 +2567,6 @@ hfcmulti_interrupt(int intno, void *dev_id)
 	static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0,
 	    iq5 = 0, iq6 = 0, iqcnt = 0;
 #endif
-	static int		count;
 	struct hfc_multi	*hc = dev_id;
 	struct dchannel		*dch;
 	u_char			r_irq_statech, status, r_irq_misc, r_irq_oview;
@@ -2698,16 +2681,11 @@ hfcmulti_interrupt(int intno, void *dev_id)
 			/* -> DTMF IRQ */
 			hfcmulti_dtmf(hc);
 		}
-		/* TODO: REPLACE !!!! 125 us Interrupts are not acceptable  */
 		if (r_irq_misc & V_IRQ_PROC) {
-			/* IRQ every 125us */
-			count++;
-			/* generate 1kHz signal */
-			if (count == 8) {
-				if (hfc_interrupt)
-					hfc_interrupt();
-				count = 0;
-			}
+			static int irq_proc_cnt;
+			if (!irq_proc_cnt++)
+				printk(KERN_WARNING "%s: got V_IRQ_PROC -"
+				    " this should not happen\n", __func__);
 		}
 
 	}
@@ -4945,9 +4923,7 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 	switch (m->dip_type) {
 	case DIP_4S:
 		/*
-		 * get DIP Setting for beroNet 1S/2S/4S cards
-		 *  check if Port Jumper config matches
-		 * module param 'protocol'
+		 * Get DIP setting for beroNet 1S/2S/4S cards
 		 * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) +
 		 * GPI 19/23 (R_GPI_IN2))
 		 */
@@ -4966,9 +4942,8 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 		break;
 	case DIP_8S:
 		/*
-		 * get DIP Setting for beroNet 8S0+ cards
-		 *
-		 * enable PCI auxbridge function
+		 * Get DIP Setting for beroNet 8S0+ cards
+		 * Enable PCI auxbridge function
 		 */
 		HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
 		/* prepare access to auxport */
@@ -5137,8 +5112,7 @@ static struct pci_device_id hfmultipci_ids[] __devinitdata = {
 	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
 	PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */
 	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
-	PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)},
-	    /* IOB8ST Recording */
+	PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, /* IOB8ST Recording */
 	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
 		PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST  */
 	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
@@ -5188,18 +5162,16 @@ hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	struct hm_map	*m = (struct hm_map *)ent->driver_data;
 	int		ret;
 
-	if (m == NULL) {
-		if (ent->vendor == PCI_VENDOR_ID_CCD)
-			if (ent->device == PCI_DEVICE_ID_CCD_HFC4S ||
-			    ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
-			    ent->device == PCI_DEVICE_ID_CCD_HFCE1)
-				printk(KERN_ERR
-				    "unknown HFC multiport controller "
-				    "(vendor:%x device:%x subvendor:%x "
-				    "subdevice:%x) Please contact the "
-				    "driver maintainer for support.\n",
-				    ent->vendor, ent->device,
-				    ent->subvendor, ent->subdevice);
+	if (m == NULL && ent->vendor == PCI_VENDOR_ID_CCD && (
+	    ent->device == PCI_DEVICE_ID_CCD_HFC4S ||
+	    ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
+	    ent->device == PCI_DEVICE_ID_CCD_HFCE1)) {
+		printk(KERN_ERR
+		    "Unknown HFC multiport controller (vendor:%x device:%x "
+		    "subvendor:%x subdevice:%x)\n", ent->vendor, ent->device,
+		    ent->subvendor, ent->subdevice);
+		printk(KERN_ERR
+		    "Please contact the driver maintainer for support.\n");
 		return -ENODEV;
 	}
 	ret = hfcmulti_init(pdev, ent);
@@ -5222,22 +5194,9 @@ HFCmulti_cleanup(void)
 {
 	struct hfc_multi *card, *next;
 
-	/* unload interrupt function symbol */
-	if (hfc_interrupt)
-		symbol_put(ztdummy_extern_interrupt);
-	if (register_interrupt)
-		symbol_put(ztdummy_register_interrupt);
-	if (unregister_interrupt) {
-		if (interrupt_registered) {
-			interrupt_registered = 0;
-			unregister_interrupt();
-		}
-		symbol_put(ztdummy_unregister_interrupt);
-	}
-
+	/* get rid of all devices of this driver */
 	list_for_each_entry_safe(card, next, &HFClist, list)
 		release_card(card);
-	/* get rid of all devices of this driver */
 	pci_unregister_driver(&hfcmultipci_driver);
 }
 
@@ -5246,8 +5205,10 @@ HFCmulti_init(void)
 {
 	int err;
 
+	printk(KERN_INFO "mISDN: HFC-multi driver %s\n", HFC_MULTI_VERSION);
+
 #ifdef IRQ_DEBUG
-	printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__);
+	printk(KERN_DEBUG "%s: IRQ_DEBUG IS ENABLED!\n", __func__);
 #endif
 
 	spin_lock_init(&HFClock);
@@ -5256,22 +5217,11 @@ HFCmulti_init(void)
 	if (debug & DEBUG_HFCMULTI_INIT)
 		printk(KERN_DEBUG "%s: init entered\n", __func__);
 
-	hfc_interrupt = symbol_get(ztdummy_extern_interrupt);
-	register_interrupt = symbol_get(ztdummy_register_interrupt);
-	unregister_interrupt = symbol_get(ztdummy_unregister_interrupt);
-	printk(KERN_INFO "mISDN: HFC-multi driver %s\n",
-	    hfcmulti_revision);
-
 	switch (poll) {
 	case 0:
 		poll_timer = 6;
 		poll = 128;
 		break;
-		/*
-		 * wenn dieses break nochmal verschwindet,
-		 * gibt es heisse ohren :-)
-		 * "without the break you will get hot ears ???"
-		 */
 	case 8:
 		poll_timer = 2;
 		break;
@@ -5301,17 +5251,6 @@ HFCmulti_init(void)
 	err = pci_register_driver(&hfcmultipci_driver);
 	if (err < 0) {
 		printk(KERN_ERR "error registering pci driver: %x\n", err);
-		if (hfc_interrupt)
-			symbol_put(ztdummy_extern_interrupt);
-		if (register_interrupt)
-			symbol_put(ztdummy_register_interrupt);
-		if (unregister_interrupt) {
-			if (interrupt_registered) {
-				interrupt_registered = 0;
-				unregister_interrupt();
-			}
-			symbol_put(ztdummy_unregister_interrupt);
-		}
 		return err;
 	}
 	return 0;
-- 
cgit v1.2.3


From 8dd2f36f317569665e454268a2677cfba3e848f1 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sat, 2 Aug 2008 22:51:52 +0200
Subject: mISDN: Add feature via MISDN_CTRL_FILL_EMPTY to fill fifo if empty

This prevents underrun of fifo when filled and in case of an underrun it
prevents subsequent underruns due to jitter.
Improve dsp, so buffers are kept filled with a certain delay, so moderate
jitter will not cause underrun all the time -> the audio quality is highly
improved. tones are not interrupted by gaps anymore, except when CPU is
stalling or in high load.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfc_multi.h |  3 ++
 drivers/isdn/hardware/mISDN/hfc_pci.h   |  1 +
 drivers/isdn/hardware/mISDN/hfcmulti.c  | 45 ++++++++++++++++++++------
 drivers/isdn/hardware/mISDN/hfcpci.c    | 41 ++++++++++++++++++++++--
 drivers/isdn/mISDN/dsp.h                |  1 +
 drivers/isdn/mISDN/dsp_cmx.c            | 56 ++++++++++++++++++++++-----------
 drivers/isdn/mISDN/dsp_core.c           | 37 ++++++++++++++++++++--
 include/linux/mISDNhw.h                 | 25 ++++++++-------
 include/linux/mISDNif.h                 |  1 +
 9 files changed, 165 insertions(+), 45 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
index 6d74951a1797..4aa6a8b41f50 100644
--- a/drivers/isdn/hardware/mISDN/hfc_multi.h
+++ b/drivers/isdn/hardware/mISDN/hfc_multi.h
@@ -9,6 +9,7 @@
 #define	DEBUG_HFCMULTI_MODE	0x00100000
 #define	DEBUG_HFCMULTI_MSG	0x00200000
 #define	DEBUG_HFCMULTI_STATE	0x00400000
+#define	DEBUG_HFCMULTI_FILL	0x00800000
 #define	DEBUG_HFCMULTI_SYNC	0x01000000
 #define	DEBUG_HFCMULTI_DTMF	0x02000000
 #define	DEBUG_HFCMULTI_LOCK	0x80000000
@@ -166,6 +167,8 @@ struct hfc_multi {
 
 	u_long		chip;	/* chip configuration */
 	int		masterclk; /* port that provides master clock -1=off */
+	unsigned char	silence;/* silence byte */
+	unsigned char	silence_data[128];/* silence block */
 	int		dtmf;	/* flag that dtmf is currently in process */
 	int		Flen;	/* F-buffer size */
 	int		Zlen;	/* Z-buffer size (must be int for calculation)*/
diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h
index 5783d22a18fe..fd9241ab1802 100644
--- a/drivers/isdn/hardware/mISDN/hfc_pci.h
+++ b/drivers/isdn/hardware/mISDN/hfc_pci.h
@@ -27,6 +27,7 @@
  */
 #define HFCPCI_BTRANS_THRESHOLD 128
 #define HFCPCI_BTRANS_MAX	256
+#define HFCPCI_FILLEMPTY	64
 #define HFCPCI_BTRANS_THRESMASK 0x00
 
 /* defines for PCI config */
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index ff5ec3cbeb77..3fc2e9d95341 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -180,7 +180,6 @@ static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30  };
 #define	CLKDEL_TE	0x0f	/* CLKDEL in TE mode */
 #define	CLKDEL_NT	0x6c	/* CLKDEL in NT mode
 				   (0x60 MUST be included!) */
-static u_char silence =	0xff;	/* silence by LAW */
 
 #define	DIP_4S	0x1		/* DIP Switches for Beronet 1S/2S/4S cards */
 #define	DIP_8S	0x2		/* DIP Switches for Beronet 8S+ cards */
@@ -1975,6 +1974,17 @@ next_frame:
 		return; /* no data */
 	}
 
+	/* "fill fifo if empty" feature */
+	if (bch && test_bit(FLG_FILLEMPTY, &bch->Flags)
+		&& !test_bit(FLG_HDLC, &bch->Flags) && z2 == z1) {
+		if (debug & DEBUG_HFCMULTI_FILL)
+			printk(KERN_DEBUG "%s: buffer empty, so we have "
+				"underrun\n", __func__);
+		/* fill buffer, to prevent future underrun */
+		hc->write_fifo(hc, hc->silence_data, poll >> 1);
+		Zspace -= (poll >> 1);
+	}
+
 	/* if audio data and connected slot */
 	if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending)
 		&& slot_tx >= 0) {
@@ -2011,7 +2021,6 @@ next_frame:
 			__func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i,
 			temp ? "HDLC":"TRANS");
 
-
 	/* Have to prep the audio data */
 	hc->write_fifo(hc, d, ii - i);
 	*idxp = ii;
@@ -2050,7 +2059,7 @@ next_frame:
 	 * no more data at all. this prevents sending an undefined value.
 	 */
 	if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
-		HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+		HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence);
 }
 
 
@@ -2932,7 +2941,7 @@ mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx,
 			HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
 			HFC_wait(hc);
 			/* tx silence */
-			HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+			HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence);
 			HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
 			    ((ch % 4) * 4)) << 1);
 			HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1));
@@ -2947,7 +2956,7 @@ mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx,
 			HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
 			HFC_wait(hc);
 			/* tx silence */
-			HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+			HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence);
 			/* enable RX fifo */
 			HFC_outb(hc, R_FIFO, (ch<<1)|1);
 			HFC_wait(hc);
@@ -3439,7 +3448,7 @@ channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
 	switch (cq->op) {
 	case MISDN_CTRL_GETOP:
 		cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP
-			| MISDN_CTRL_RX_OFF;
+			| MISDN_CTRL_RX_OFF | MISDN_CTRL_FILL_EMPTY;
 		break;
 	case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */
 		hc->chan[bch->slot].rx_off = !!cq->p1;
@@ -3454,6 +3463,12 @@ channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
 			printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n",
 			    __func__, bch->nr, hc->chan[bch->slot].rx_off);
 		break;
+	case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */
+		test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d "
+				"off=%d)\n", __func__, bch->nr, !!cq->p1);
+		break;
 	case MISDN_CTRL_HW_FEATURES: /* fill features structure */
 		if (debug & DEBUG_HFCMULTI_MSG)
 			printk(KERN_DEBUG "%s: HW_FEATURE request\n",
@@ -3970,6 +3985,7 @@ open_bchannel(struct hfc_multi *hc, struct dchannel *dch,
 	}
 	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
 		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
 	bch->ch.protocol = rq->protocol;
 	hc->chan[ch].rx_off = 0;
 	rq->ch = &bch->ch;
@@ -4806,6 +4822,7 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 	struct hfc_multi	*hc;
 	u_long		flags;
 	u_char		dips = 0, pmj = 0; /* dip settings, port mode Jumpers */
+	int		i;
 
 	if (HFC_cnt >= MAX_CARDS) {
 		printk(KERN_ERR "too many cards (max=%d).\n",
@@ -4839,11 +4856,11 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 	hc->id = HFC_cnt;
 	hc->pcm = pcm[HFC_cnt];
 	hc->io_mode = iomode[HFC_cnt];
-	if (dslot[HFC_cnt] < 0) {
+	if (dslot[HFC_cnt] < 0 && hc->type == 1) {
 		hc->dslot = 0;
 		printk(KERN_INFO "HFC-E1 card has disabled D-channel, but "
 			"31 B-channels\n");
-	} if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) {
+	} if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32 && hc->type == 1) {
 		hc->dslot = dslot[HFC_cnt];
 		printk(KERN_INFO "HFC-E1 card has alternating D-channel on "
 			"time slot %d\n", dslot[HFC_cnt]);
@@ -4854,9 +4871,17 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 	hc->masterclk = -1;
 	if (type[HFC_cnt] & 0x100) {
 		test_and_set_bit(HFC_CHIP_ULAW, &hc->chip);
-		silence = 0xff; /* ulaw silence */
+		hc->silence = 0xff; /* ulaw silence */
 	} else
-		silence = 0x2a; /* alaw silence */
+		hc->silence = 0x2a; /* alaw silence */
+	if ((poll >> 1) > sizeof(hc->silence_data)) {
+		printk(KERN_ERR "HFCMULTI error: silence_data too small, "
+			"please fix\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < (poll >> 1); i++)
+		hc->silence_data[i] = hc->silence;
+
 	if (!(type[HFC_cnt] & 0x200))
 		test_and_set_bit(HFC_CHIP_DTMF, &hc->chip);
 
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index cd8302af40eb..80c356e5dbe1 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -751,6 +751,36 @@ hfcpci_fill_fifo(struct bchannel *bch)
 			    /* fcnt contains available bytes in fifo */
 		fcnt = B_FIFO_SIZE - fcnt;
 		    /* remaining bytes to send (bytes in fifo) */
+
+		/* "fill fifo if empty" feature */
+		if (test_bit(FLG_FILLEMPTY, &bch->Flags) && !fcnt) {
+			/* printk(KERN_DEBUG "%s: buffer empty, so we have "
+				"underrun\n", __func__); */
+			/* fill buffer, to prevent future underrun */
+			count = HFCPCI_FILLEMPTY;
+			new_z1 = le16_to_cpu(*z1t) + count;
+			   /* new buffer Position */
+			if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+				new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
+			dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL);
+			maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t);
+			    /* end of fifo */
+			if (bch->debug & DEBUG_HW_BFIFO)
+				printk(KERN_DEBUG "hfcpci_FFt fillempty "
+				    "fcnt(%d) maxl(%d) nz1(%x) dst(%p)\n",
+				    fcnt, maxlen, new_z1, dst);
+			fcnt += count;
+			if (maxlen > count)
+				maxlen = count; 	/* limit size */
+			memset(dst, 0x2a, maxlen);	/* first copy */
+			count -= maxlen;		/* remaining bytes */
+			if (count) {
+				dst = bdata;		/* start of buffer */
+				memset(dst, 0x2a, count);
+			}
+			*z1t = cpu_to_le16(new_z1);	/* now send data */
+		}
+
 next_t_frame:
 		count = bch->tx_skb->len - bch->tx_idx;
 		/* maximum fill shall be HFCPCI_BTRANS_MAX */
@@ -1481,11 +1511,17 @@ deactivate_bchannel(struct bchannel *bch)
 static int
 channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
 {
-	int			ret = 0;
+	int	ret = 0;
 
 	switch (cq->op) {
 	case MISDN_CTRL_GETOP:
-		cq->op = 0;
+		cq->op = MISDN_CTRL_FILL_EMPTY;
+		break;
+	case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */
+		test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
+		if (debug & DEBUG_HW_OPEN)
+			printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d "
+				"off=%d)\n", __func__, bch->nr, !!cq->p1);
 		break;
 	default:
 		printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op);
@@ -1903,6 +1939,7 @@ open_bchannel(struct hfc_pci *hc, struct channel_req *rq)
 	bch = &hc->bch[rq->adr.channel - 1];
 	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
 		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
 	bch->ch.protocol = rq->protocol;
 	rq->ch = &bch->ch; /* TODO: E-channel */
 	if (!try_module_get(THIS_MODULE))
diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h
index 6c3fed6b8d4f..ded948069335 100644
--- a/drivers/isdn/mISDN/dsp.h
+++ b/drivers/isdn/mISDN/dsp.h
@@ -198,6 +198,7 @@ struct dsp {
 	/* hardware stuff */
 	struct dsp_features features;
 	int		features_rx_off; /* set if rx_off is featured */
+	int		features_fill_empty; /* set if fill_empty is featured */
 	int		pcm_slot_rx; /* current PCM slot (or -1) */
 	int		pcm_bank_rx;
 	int		pcm_slot_tx;
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index c884511e2d49..fc8ea41ae6a2 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -1168,11 +1168,18 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
 		dsp->rx_init = 0;
 		if (dsp->features.unordered) {
 			dsp->rx_R = (hh->id & CMX_BUFF_MASK);
-			dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
-				& CMX_BUFF_MASK;
+			if (dsp->cmx_delay)
+				dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+					& CMX_BUFF_MASK;
+			else
+				dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1))
+					& CMX_BUFF_MASK;
 		} else {
 			dsp->rx_R = 0;
-			dsp->rx_W = dsp->cmx_delay;
+			if (dsp->cmx_delay)
+				dsp->rx_W = dsp->cmx_delay;
+			else
+				dsp->rx_W = dsp_poll >> 1;
 		}
 	}
 	/* if frame contains time code, write directly */
@@ -1190,14 +1197,20 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
 			    "cmx_receive(dsp=%lx): UNDERRUN (or overrun the "
 			    "maximum delay), adjusting read pointer! "
 			    "(inst %s)\n", (u_long)dsp, dsp->name);
-		/* flush buffer */
+		/* flush rx buffer and set delay to dsp_poll / 2 */
 		if (dsp->features.unordered) {
 			dsp->rx_R = (hh->id & CMX_BUFF_MASK);
-			dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
-				& CMX_BUFF_MASK;
+			if (dsp->cmx_delay)
+				dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+					& CMX_BUFF_MASK;
+				dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1))
+					& CMX_BUFF_MASK;
 		} else {
 			dsp->rx_R = 0;
-			dsp->rx_W = dsp->cmx_delay;
+			if (dsp->cmx_delay)
+				dsp->rx_W = dsp->cmx_delay;
+			else
+				dsp->rx_W = dsp_poll >> 1;
 		}
 		memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
 	}
@@ -1360,8 +1373,11 @@ dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
 				t = (t+1) & CMX_BUFF_MASK;
 				r = (r+1) & CMX_BUFF_MASK;
 			}
-			if (r != rr)
+			if (r != rr) {
+				printk(KERN_DEBUG "%s: buffer empty\n",
+					__func__);
 				memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK);
+			}
 		/* -> if echo is enabled */
 		} else {
 			/*
@@ -1704,9 +1720,10 @@ dsp_cmx_send(void *arg)
 			}
 			/*
 			 * remove rx_delay only if we have delay AND we
-			 * have not preset cmx_delay
+			 * have not preset cmx_delay AND
+			 * the delay is greater dsp_poll
 			 */
-			if (delay && !dsp->cmx_delay) {
+			if (delay > dsp_poll && !dsp->cmx_delay) {
 				if (dsp_debug & DEBUG_DSP_CMX)
 					printk(KERN_DEBUG
 					    "%s lowest rx_delay of %d bytes for"
@@ -1714,7 +1731,8 @@ dsp_cmx_send(void *arg)
 					    __func__, delay,
 					    dsp->name);
 				r = dsp->rx_R;
-				rr = (r + delay) & CMX_BUFF_MASK;
+				rr = (r + delay - (dsp_poll >> 1))
+					& CMX_BUFF_MASK;
 				/* delete rx-data */
 				while (r != rr) {
 					p[r] = dsp_silence;
@@ -1736,7 +1754,7 @@ dsp_cmx_send(void *arg)
 			 * remove delay only if we have delay AND we
 			 * have enabled tx_dejitter
 			 */
-			if (delay && dsp->tx_dejitter) {
+			if (delay > dsp_poll && dsp->tx_dejitter) {
 				if (dsp_debug & DEBUG_DSP_CMX)
 					printk(KERN_DEBUG
 					    "%s lowest tx_delay of %d bytes for"
@@ -1744,7 +1762,8 @@ dsp_cmx_send(void *arg)
 					    __func__, delay,
 					    dsp->name);
 				r = dsp->tx_R;
-				rr = (r + delay) & CMX_BUFF_MASK;
+				rr = (r + delay - (dsp_poll >> 1))
+					& CMX_BUFF_MASK;
 				/* delete tx-data */
 				while (r != rr) {
 					q[r] = dsp_silence;
@@ -1797,14 +1816,13 @@ dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
 	ww = dsp->tx_R;
 	p = dsp->tx_buff;
 	d = skb->data;
-	space = ww-w;
-	if (space <= 0)
-		space += CMX_BUFF_SIZE;
+	space = (ww - w - 1) & CMX_BUFF_MASK;
 	/* write-pointer should not overrun nor reach read pointer */
-	if (space-1 < skb->len)
+	if (space < skb->len) {
 		/* write to the space we have left */
-		ww = (ww - 1) & CMX_BUFF_MASK;
-	else
+		ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */
+		printk(KERN_DEBUG "%s: buffer overflow\n", __func__);
+	} else
 		/* write until all byte are copied */
 		ww = (w + skb->len) & CMX_BUFF_MASK;
 	dsp->tx_W = ww;
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index 1dc21d803410..1d504ba954f5 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -191,6 +191,8 @@ dsp_rx_off_member(struct dsp *dsp)
 	struct mISDN_ctrl_req	cq;
 	int rx_off = 1;
 
+	memset(&cq, 0, sizeof(cq));
+
 	if (!dsp->features_rx_off)
 		return;
 
@@ -249,6 +251,32 @@ dsp_rx_off(struct dsp *dsp)
 	}
 }
 
+/* enable "fill empty" feature */
+static void
+dsp_fill_empty(struct dsp *dsp)
+{
+	struct mISDN_ctrl_req	cq;
+
+	memset(&cq, 0, sizeof(cq));
+
+	if (!dsp->ch.peer) {
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: no peer, no fill_empty\n",
+				__func__);
+		return;
+	}
+	cq.op = MISDN_CTRL_FILL_EMPTY;
+	cq.p1 = 1;
+	if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
+		printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+			__func__);
+		return;
+	}
+	if (dsp_debug & DEBUG_DSP_CORE)
+		printk(KERN_DEBUG "%s: %s set fill_empty = 1\n",
+			__func__, dsp->name);
+}
+
 static int
 dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb)
 {
@@ -593,8 +621,6 @@ get_features(struct mISDNchannel *ch)
 	struct dsp		*dsp = container_of(ch, struct dsp, ch);
 	struct mISDN_ctrl_req	cq;
 
-	if (dsp_options & DSP_OPT_NOHARDWARE)
-		return;
 	if (!ch->peer) {
 		if (dsp_debug & DEBUG_DSP_CORE)
 			printk(KERN_DEBUG "%s: no peer, no features\n",
@@ -610,6 +636,10 @@ get_features(struct mISDNchannel *ch)
 	}
 	if (cq.op & MISDN_CTRL_RX_OFF)
 		dsp->features_rx_off = 1;
+	if (cq.op & MISDN_CTRL_FILL_EMPTY)
+		dsp->features_fill_empty = 1;
+	if (dsp_options & DSP_OPT_NOHARDWARE)
+		return;
 	if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) {
 		cq.op = MISDN_CTRL_HW_FEATURES;
 		*((u_long *)&cq.p1) = (u_long)&dsp->features;
@@ -865,6 +895,9 @@ dsp_function(struct mISDNchannel *ch,  struct sk_buff *skb)
 		if (dsp->dtmf.hardware || dsp->dtmf.software)
 			dsp_dtmf_goertzel_init(dsp);
 		get_features(ch);
+		/* enable fill_empty feature */
+		if (dsp->features_fill_empty)
+			dsp_fill_empty(dsp);
 		/* send ph_activate */
 		hh->prim = PH_ACTIVATE_REQ;
 		if (ch->peer)
diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h
index e794dfb87504..9384b92dfc65 100644
--- a/include/linux/mISDNhw.h
+++ b/include/linux/mISDNhw.h
@@ -57,20 +57,21 @@
 #define FLG_L2DATA		14	/* channel use L2 DATA primitivs */
 #define FLG_ORIGIN		15	/* channel is on origin site */
 /* channel specific stuff */
+#define FLG_FILLEMPTY		16	/* fill fifo on first frame (empty) */
 /* arcofi specific */
-#define FLG_ARCOFI_TIMER	16
-#define FLG_ARCOFI_ERROR	17
+#define FLG_ARCOFI_TIMER	17
+#define FLG_ARCOFI_ERROR	18
 /* isar specific */
-#define FLG_INITIALIZED		16
-#define FLG_DLEETX		17
-#define FLG_LASTDLE		18
-#define FLG_FIRST		19
-#define FLG_LASTDATA		20
-#define FLG_NMD_DATA		21
-#define FLG_FTI_RUN		22
-#define FLG_LL_OK		23
-#define FLG_LL_CONN		24
-#define FLG_DTMFSEND		25
+#define FLG_INITIALIZED		17
+#define FLG_DLEETX		18
+#define FLG_LASTDLE		19
+#define FLG_FIRST		20
+#define FLG_LASTDATA		21
+#define FLG_NMD_DATA		22
+#define FLG_FTI_RUN		23
+#define FLG_LL_OK		24
+#define FLG_LL_CONN		25
+#define FLG_DTMFSEND		26
 
 /* workq events */
 #define FLG_RECVQUEUE		30
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index 8f2d60da04e7..74c903cd7a0a 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -312,6 +312,7 @@ clear_channelmap(u_int nr, u_char *map)
 #define MISDN_CTRL_SETPEER		0x0040
 #define MISDN_CTRL_UNSETPEER		0x0080
 #define MISDN_CTRL_RX_OFF		0x0100
+#define MISDN_CTRL_FILL_EMPTY		0x0200
 #define MISDN_CTRL_HW_FEATURES_OP	0x2000
 #define MISDN_CTRL_HW_FEATURES		0x2001
 #define MISDN_CTRL_HFC_OP		0x4000
-- 
cgit v1.2.3


From 190f71d9d8516fea4db01ba86ddfad5738a92703 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sun, 3 Aug 2008 10:36:53 +0200
Subject: mISDN: Add some debug option for clock problems

Add a new debug bit for clock problems.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/dsp.h     |  1 +
 drivers/isdn/mISDN/dsp_cmx.c | 16 +++++++++-------
 2 files changed, 10 insertions(+), 7 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h
index ded948069335..98a33c58f091 100644
--- a/drivers/isdn/mISDN/dsp.h
+++ b/drivers/isdn/mISDN/dsp.h
@@ -15,6 +15,7 @@
 #define DEBUG_DSP_TONE		0x0020
 #define DEBUG_DSP_BLOWFISH	0x0040
 #define DEBUG_DSP_DELAY		0x0100
+#define DEBUG_DSP_CLOCK		0x0200
 #define DEBUG_DSP_DTMFCOEFF	0x8000 /* heavy output */
 
 /* options may be:
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index fc8ea41ae6a2..93fb819c5203 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -1192,7 +1192,7 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
 	 * we set our new read pointer, and write silence to buffer
 	 */
 	if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
-		if (dsp_debug & DEBUG_DSP_CMX)
+		if (dsp_debug & DEBUG_DSP_CLOCK)
 			printk(KERN_DEBUG
 			    "cmx_receive(dsp=%lx): UNDERRUN (or overrun the "
 			    "maximum delay), adjusting read pointer! "
@@ -1218,7 +1218,7 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
 	if (dsp->cmx_delay)
 		if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >=
 		    (dsp->cmx_delay << 1)) {
-			if (dsp_debug & DEBUG_DSP_CMX)
+			if (dsp_debug & DEBUG_DSP_CLOCK)
 				printk(KERN_DEBUG
 				    "cmx_receive(dsp=%lx): OVERRUN (because "
 				    "twice the delay is reached), adjusting "
@@ -1374,8 +1374,9 @@ dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
 				r = (r+1) & CMX_BUFF_MASK;
 			}
 			if (r != rr) {
-				printk(KERN_DEBUG "%s: buffer empty\n",
-					__func__);
+				if (dsp_debug & DEBUG_DSP_CLOCK)
+					printk(KERN_DEBUG "%s: RX empty\n",
+						__func__);
 				memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK);
 			}
 		/* -> if echo is enabled */
@@ -1724,7 +1725,7 @@ dsp_cmx_send(void *arg)
 			 * the delay is greater dsp_poll
 			 */
 			if (delay > dsp_poll && !dsp->cmx_delay) {
-				if (dsp_debug & DEBUG_DSP_CMX)
+				if (dsp_debug & DEBUG_DSP_CLOCK)
 					printk(KERN_DEBUG
 					    "%s lowest rx_delay of %d bytes for"
 					    " dsp %s are now removed.\n",
@@ -1755,7 +1756,7 @@ dsp_cmx_send(void *arg)
 			 * have enabled tx_dejitter
 			 */
 			if (delay > dsp_poll && dsp->tx_dejitter) {
-				if (dsp_debug & DEBUG_DSP_CMX)
+				if (dsp_debug & DEBUG_DSP_CLOCK)
 					printk(KERN_DEBUG
 					    "%s lowest tx_delay of %d bytes for"
 					    " dsp %s are now removed.\n",
@@ -1821,7 +1822,8 @@ dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
 	if (space < skb->len) {
 		/* write to the space we have left */
 		ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */
-		printk(KERN_DEBUG "%s: buffer overflow\n", __func__);
+		if (dsp_debug & DEBUG_DSP_CLOCK)
+			printk(KERN_DEBUG "%s: TX overflow\n", __func__);
 	} else
 		/* write until all byte are copied */
 		ww = (w + skb->len) & CMX_BUFF_MASK;
-- 
cgit v1.2.3


From 11618496d03d9e1ad70ac68afbfa7df9b5fc19d1 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Wed, 6 Aug 2008 19:13:07 +0200
Subject: mISDN: Fix queue limit counting problem

If received frames are not handled in time, purge them.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/hwchannel.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c
index 2596fba4e614..535ceacc05b9 100644
--- a/drivers/isdn/mISDN/hwchannel.c
+++ b/drivers/isdn/mISDN/hwchannel.c
@@ -50,9 +50,6 @@ bchannel_bh(struct work_struct *ws)
 
 	if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
 		while ((skb = skb_dequeue(&bch->rqueue))) {
-			if (bch->rcount >= 64)
-				printk(KERN_WARNING "B-channel %p receive "
-					"queue if full, but empties...\n", bch);
 			bch->rcount--;
 			if (likely(bch->ch.peer)) {
 				err = bch->ch.recv(bch->ch.peer, skb);
@@ -177,8 +174,10 @@ recv_Bchannel(struct bchannel *bch)
 	hh->prim = PH_DATA_IND;
 	hh->id = MISDN_ID_ANY;
 	if (bch->rcount >= 64) {
-		dev_kfree_skb(bch->rx_skb);
-		bch->rx_skb = NULL;
+		printk(KERN_WARNING "B-channel %p receive queue overflow, "
+			"fushing!\n", bch);
+		skb_queue_purge(&bch->rqueue);
+		bch->rcount = 0;
 		return;
 	}
 	bch->rcount++;
@@ -200,8 +199,10 @@ void
 recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
 {
 	if (bch->rcount >= 64) {
-		dev_kfree_skb(skb);
-		return;
+		printk(KERN_WARNING "B-channel %p receive queue overflow, "
+			"fushing!\n", bch);
+		skb_queue_purge(&bch->rqueue);
+		bch->rcount = 0;
 	}
 	bch->rcount++;
 	skb_queue_tail(&bch->rqueue, skb);
@@ -245,8 +246,12 @@ confirm_Bsend(struct bchannel *bch)
 {
 	struct sk_buff	*skb;
 
-	if (bch->rcount >= 64)
-		return;
+	if (bch->rcount >= 64) {
+		printk(KERN_WARNING "B-channel %p receive queue overflow, "
+			"fushing!\n", bch);
+		skb_queue_purge(&bch->rqueue);
+		bch->rcount = 0;
+	}
 	skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
 	    0, NULL, GFP_ATOMIC);
 	if (!skb) {
-- 
cgit v1.2.3


From 9776ef0829ed33e8f36d9e7d859d00cc91c54ffd Mon Sep 17 00:00:00 2001
From: Matthias Urlichs <smurf@smurf.noris.de>
Date: Mon, 11 Aug 2008 14:43:33 +0200
Subject: mISDN: Remove the local max-card limit

This is not needed.

Signed-off-by: Matthias Urlichs <smurf@smurf.noris.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcpci.c | 4 ----
 1 file changed, 4 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index 80c356e5dbe1..8461477a3ac0 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -34,7 +34,6 @@
 
 static const char *hfcpci_revision = "2.0";
 
-#define MAX_CARDS	8
 static int HFC_cnt;
 static uint debug;
 
@@ -2077,9 +2076,6 @@ setup_card(struct hfc_pci *card)
 	u_long		flags;
 	char		name[MISDN_MAX_IDLEN];
 
-	if (HFC_cnt >= MAX_CARDS)
-		return -EINVAL; /* maybe better value */
-
 	card->dch.debug = debug;
 	spin_lock_init(&card->lock);
 	mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state);
-- 
cgit v1.2.3


From 8b6015f736125050722dbe59c4f943e78cd626f0 Mon Sep 17 00:00:00 2001
From: Matthias Urlichs <smurf@smurf.noris.de>
Date: Tue, 12 Aug 2008 10:12:09 +0200
Subject: mISDN: Added an ioctl to change the device name

To get persistent device names with hotplug we need to rename devices
sometime.

Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/socket.c | 15 +++++++++++++++
 include/linux/mISDNif.h     | 31 +++++++++++++++++++------------
 2 files changed, 34 insertions(+), 12 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index 37a2de18cfd0..98fa6c4a4bd6 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -645,6 +645,21 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 		} else
 			err = -ENODEV;
 		break;
+	case IMSETDEVNAME:
+		{
+			struct mISDN_devrename dn;
+			if (copy_from_user(&dn, (void __user *)arg,
+			    sizeof(dn))) {
+				err = -EFAULT;
+				break;
+			}
+			dev = get_mdevice(dn.id);
+			if (dev)
+				strlcpy(dev->name, dn.name, MISDN_MAX_IDLEN);
+			else
+				err = -ENODEV;
+		}
+		break;
 	default:
 		err = -EINVAL;
 	}
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index 74c903cd7a0a..be09476ed854 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -36,8 +36,8 @@
  *              - should be incremented on every checkin
  */
 #define	MISDN_MAJOR_VERSION	1
-#define	MISDN_MINOR_VERSION	0
-#define MISDN_RELEASE		19
+#define	MISDN_MINOR_VERSION	1
+#define MISDN_RELEASE		20
 
 /* primitives for information exchange
  * generell format
@@ -255,16 +255,6 @@ struct sockaddr_mISDN {
 	unsigned char	tei;
 };
 
-/* timer device ioctl */
-#define IMADDTIMER	_IOR('I', 64, int)
-#define IMDELTIMER	_IOR('I', 65, int)
-/* socket ioctls */
-#define	IMGETVERSION	_IOR('I', 66, int)
-#define	IMGETCOUNT	_IOR('I', 67, int)
-#define IMGETDEVINFO	_IOR('I', 68, int)
-#define IMCTRLREQ	_IOR('I', 69, int)
-#define IMCLEAR_L2	_IOR('I', 70, int)
-
 struct mISDNversion {
 	unsigned char	major;
 	unsigned char	minor;
@@ -281,6 +271,23 @@ struct mISDN_devinfo {
 	char			name[MISDN_MAX_IDLEN];
 };
 
+struct mISDN_devrename {
+	u_int			id;
+	char			name[MISDN_MAX_IDLEN]; /* new name */
+};
+
+/* timer device ioctl */
+#define IMADDTIMER	_IOR('I', 64, int)
+#define IMDELTIMER	_IOR('I', 65, int)
+
+/* socket ioctls */
+#define	IMGETVERSION	_IOR('I', 66, int)
+#define	IMGETCOUNT	_IOR('I', 67, int)
+#define IMGETDEVINFO	_IOR('I', 68, int)
+#define IMCTRLREQ	_IOR('I', 69, int)
+#define IMCLEAR_L2	_IOR('I', 70, int)
+#define IMSETDEVNAME	_IOR('I', 71, struct mISDN_devrename)
+
 static inline int
 test_channelmap(u_int nr, u_char *map)
 {
-- 
cgit v1.2.3


From 837468d135dcc49cdabc9fa92fc9550479f60704 Mon Sep 17 00:00:00 2001
From: Matthias Urlichs <smurf@smurf.noris.de>
Date: Sat, 16 Aug 2008 00:04:33 +0200
Subject: mISDN: Use struct device name field

struct device already has a 'name' member, use it.

Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/core.c   |  8 ++++----
 drivers/isdn/mISDN/layer1.c |  2 +-
 drivers/isdn/mISDN/socket.c |  6 +++---
 drivers/isdn/mISDN/stack.c  | 43 ++++++++++++++++++++++++-------------------
 drivers/isdn/mISDN/tei.c    |  6 +++---
 include/linux/mISDNif.h     |  1 -
 6 files changed, 35 insertions(+), 31 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
index 751665c448d0..43fd97b0fe42 100644
--- a/drivers/isdn/mISDN/core.c
+++ b/drivers/isdn/mISDN/core.c
@@ -82,12 +82,12 @@ mISDN_register_device(struct mISDNdevice *dev, char *name)
 	if (dev->id < 0)
 		return -EBUSY;
 	if (name && name[0])
-		strcpy(dev->name, name);
+		dev_set_name(&dev->dev, "%s", name);
 	else
-		sprintf(dev->name, "mISDN%d", dev->id);
+		dev_set_name(&dev->dev, "mISDN%d", dev->id);
 	if (debug & DEBUG_CORE)
 		printk(KERN_DEBUG "mISDN_register %s %d\n",
-			dev->name, dev->id);
+			dev_name(&dev->dev), dev->id);
 	err = create_stack(dev);
 	if (err)
 		return err;
@@ -104,7 +104,7 @@ mISDN_unregister_device(struct mISDNdevice *dev) {
 
 	if (debug & DEBUG_CORE)
 		printk(KERN_DEBUG "mISDN_unregister %s %d\n",
-			dev->name, dev->id);
+			dev_name(&dev->dev), dev->id);
 	write_lock_irqsave(&device_lock, flags);
 	list_del(&dev->D.list);
 	write_unlock_irqrestore(&device_lock, flags);
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c
index b73e952d12cf..e826eeb1ecec 100644
--- a/drivers/isdn/mISDN/layer1.c
+++ b/drivers/isdn/mISDN/layer1.c
@@ -101,7 +101,7 @@ l1m_debug(struct FsmInst *fi, char *fmt, ...)
 	va_list va;
 
 	va_start(va, fmt);
-	printk(KERN_DEBUG "%s: ", l1->dch->dev.name);
+	printk(KERN_DEBUG "%s: ", dev_name(&l1->dch->dev.dev));
 	vprintk(fmt, va);
 	printk("\n");
 	va_end(va);
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index 98fa6c4a4bd6..2f6d6e88ff2c 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -381,7 +381,7 @@ data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 			memcpy(di.channelmap, dev->channelmap,
 				sizeof(di.channelmap));
 			di.nrbchan = dev->nrbchan;
-			strcpy(di.name, dev->name);
+			strcpy(di.name, dev_name(&dev->dev));
 			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
 				err = -EFAULT;
 		} else
@@ -639,7 +639,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 			memcpy(di.channelmap, dev->channelmap,
 				sizeof(di.channelmap));
 			di.nrbchan = dev->nrbchan;
-			strcpy(di.name, dev->name);
+			strcpy(di.name, dev_name(&dev->dev));
 			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
 				err = -EFAULT;
 		} else
@@ -655,7 +655,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 			}
 			dev = get_mdevice(dn.id);
 			if (dev)
-				strlcpy(dev->name, dn.name, MISDN_MAX_IDLEN);
+				err = device_rename(&dev->dev, dn.name);
 			else
 				err = -ENODEV;
 		}
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index d55b14ae4e99..9b9fab47cb2b 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -172,7 +172,8 @@ send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
 		else
 			printk(KERN_WARNING
 			    "%s: dev(%s) prim(%x) id(%x) no channel\n",
-			    __func__, st->dev->name, hh->prim, hh->id);
+			    __func__, dev_name(&st->dev->dev), hh->prim,
+			    hh->id);
 	} else if (lm == 0x8) {
 		WARN_ON(lm == 0x8);
 		ch = get_channel4id(st, hh->id);
@@ -181,11 +182,12 @@ send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
 		else
 			printk(KERN_WARNING
 			    "%s: dev(%s) prim(%x) id(%x) no channel\n",
-			    __func__, st->dev->name, hh->prim, hh->id);
+			    __func__, dev_name(&st->dev->dev), hh->prim,
+			    hh->id);
 	} else {
 		/* broadcast not handled yet */
 		printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
-		    __func__, st->dev->name, hh->prim);
+		    __func__, dev_name(&st->dev->dev), hh->prim);
 	}
 	return -ESRCH;
 }
@@ -209,7 +211,8 @@ mISDNStackd(void *data)
 	unlock_kernel();
 #endif
 	if (*debug & DEBUG_MSG_THREAD)
-		printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name);
+		printk(KERN_DEBUG "mISDNStackd %s started\n",
+		    dev_name(&st->dev->dev));
 
 	if (st->notify != NULL) {
 		complete(st->notify);
@@ -245,7 +248,7 @@ mISDNStackd(void *data)
 					printk(KERN_DEBUG
 					    "%s: %s prim(%x) id(%x) "
 					    "send call(%d)\n",
-					    __func__, st->dev->name,
+					    __func__, dev_name(&st->dev->dev),
 					    mISDN_HEAD_PRIM(skb),
 					    mISDN_HEAD_ID(skb), err);
 				dev_kfree_skb(skb);
@@ -288,7 +291,7 @@ mISDNStackd(void *data)
 		    mISDN_STACK_ACTION_MASK));
 		if (*debug & DEBUG_MSG_THREAD)
 			printk(KERN_DEBUG "%s: %s wake status %08lx\n",
-			    __func__, st->dev->name, st->status);
+			    __func__, dev_name(&st->dev->dev), st->status);
 		test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
 
 		test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
@@ -303,15 +306,16 @@ mISDNStackd(void *data)
 #ifdef MISDN_MSG_STATS
 	printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
 	    "msg %d sleep %d stopped\n",
-	    st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt);
+	    dev_name(&st->dev->dev), st->msg_cnt, st->sleep_cnt,
+	    st->stopped_cnt);
 	printk(KERN_DEBUG
 	    "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n",
-	    st->dev->name, st->thread->utime, st->thread->stime);
+	    dev_name(&st->dev->dev), st->thread->utime, st->thread->stime);
 	printk(KERN_DEBUG
 	    "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
-	    st->dev->name, st->thread->nvcsw, st->thread->nivcsw);
+	    dev_name(&st->dev->dev), st->thread->nvcsw, st->thread->nivcsw);
 	printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
-	    st->dev->name);
+	    dev_name(&st->dev->dev));
 #endif
 	test_and_set_bit(mISDN_STACK_KILLED, &st->status);
 	test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
@@ -401,15 +405,16 @@ create_stack(struct mISDNdevice *dev)
 	newst->own.send = mISDN_queue_message;
 	newst->own.recv = mISDN_queue_message;
 	if (*debug & DEBUG_CORE_FUNC)
-		printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name);
+		printk(KERN_DEBUG "%s: st(%s)\n", __func__,
+		    dev_name(&newst->dev->dev));
 	newst->notify = &done;
 	newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
-		newst->dev->name);
+		dev_name(&newst->dev->dev));
 	if (IS_ERR(newst->thread)) {
 		err = PTR_ERR(newst->thread);
 		printk(KERN_ERR
 			"mISDN:cannot create kernel thread for %s (%d)\n",
-			newst->dev->name, err);
+			dev_name(&newst->dev->dev), err);
 		delete_teimanager(dev->teimgr);
 		kfree(newst);
 	} else
@@ -428,8 +433,8 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
 
 	if (*debug &  DEBUG_CORE_FUNC)
 		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
-			__func__, dev->name, protocol, adr->dev, adr->channel,
-			 adr->sapi, adr->tei);
+			__func__, dev_name(&dev->dev), protocol, adr->dev,
+			adr->channel, adr->sapi, adr->tei);
 	switch (protocol) {
 	case ISDN_P_NT_S0:
 	case ISDN_P_NT_E1:
@@ -473,7 +478,7 @@ connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
 
 	if (*debug &  DEBUG_CORE_FUNC)
 		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
-			__func__, dev->name, protocol,
+			__func__, dev_name(&dev->dev), protocol,
 			adr->dev, adr->channel, adr->sapi,
 			adr->tei);
 	ch->st = dev->D.st;
@@ -529,7 +534,7 @@ create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
 
 	if (*debug &  DEBUG_CORE_FUNC)
 		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
-			__func__, dev->name, protocol,
+			__func__, dev_name(&dev->dev), protocol,
 			adr->dev, adr->channel, adr->sapi,
 			adr->tei);
 	rq.protocol = ISDN_P_TE_S0;
@@ -590,7 +595,7 @@ delete_channel(struct mISDNchannel *ch)
 	}
 	if (*debug & DEBUG_CORE_FUNC)
 		printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
-		    ch->st->dev->name, ch->protocol);
+		    dev_name(&ch->st->dev->dev), ch->protocol);
 	if (ch->protocol >= ISDN_P_B_START) {
 		if (ch->peer) {
 			ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL);
@@ -643,7 +648,7 @@ delete_stack(struct mISDNdevice *dev)
 
 	if (*debug & DEBUG_CORE_FUNC)
 		printk(KERN_DEBUG "%s: st(%s)\n", __func__,
-		    st->dev->name);
+		    dev_name(&st->dev->dev));
 	if (dev->teimgr)
 		delete_teimanager(dev->teimgr);
 	if (st->thread) {
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
index 5c43d19e7c11..b452dead8fd0 100644
--- a/drivers/isdn/mISDN/tei.c
+++ b/drivers/isdn/mISDN/tei.c
@@ -968,9 +968,9 @@ create_teimgr(struct manager *mgr, struct channel_req *crq)
 
 	if (*debug & DEBUG_L2_TEI)
 		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
-			__func__, mgr->ch.st->dev->name, crq->protocol,
-			crq->adr.dev, crq->adr.channel, crq->adr.sapi,
-			crq->adr.tei);
+			__func__, dev_name(&mgr->ch.st->dev->dev),
+			crq->protocol, crq->adr.dev, crq->adr.channel,
+			crq->adr.sapi, crq->adr.tei);
 	if (crq->adr.sapi != 0) /* not supported yet */
 		return -EINVAL;
 	if (crq->adr.tei > GROUP_TEI)
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index be09476ed854..a59febb6143a 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -431,7 +431,6 @@ struct mISDN_sock {
 struct mISDNdevice {
 	struct mISDNchannel	D;
 	u_int			id;
-	char			name[MISDN_MAX_IDLEN];
 	u_int			Dprotocols;
 	u_int			Bprotocols;
 	u_int			nrbchan;
-- 
cgit v1.2.3


From e4cce225f05c1daf89d10565c654f4c11907a049 Mon Sep 17 00:00:00 2001
From: Peter Schlaile <root@asterisk.schlaile.de>
Date: Fri, 22 Aug 2008 19:44:21 +0200
Subject: mISDN: Fix HDLC DSP transmit

Fix HDLC DSP transmit (DL_DATA frames were bounced back upwards instead of
being sent down as PH_DATA frames)

Thanks to Andreas Eversberg for the fix!

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Peter Schlaile <root@asterisk.schlaile.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/dsp_core.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index 1d504ba954f5..c16cb7ac3d2a 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -867,11 +867,14 @@ dsp_function(struct mISDNchannel *ch,  struct sk_buff *skb)
 		}
 		if (dsp->hdlc) {
 			/* hdlc */
-			spin_lock_irqsave(&dsp_lock, flags);
-			if (dsp->b_active) {
-				skb_queue_tail(&dsp->sendq, skb);
-				schedule_work(&dsp->workq);
+			if (!dsp->b_active) {
+				ret = -EIO;
+				break;
 			}
+			hh->prim = PH_DATA_REQ;
+			spin_lock_irqsave(&dsp_lock, flags);
+			skb_queue_tail(&dsp->sendq, skb);
+			schedule_work(&dsp->workq);
 			spin_unlock_irqrestore(&dsp_lock, flags);
 			return 0;
 		}
-- 
cgit v1.2.3


From 0aafe75d5c9227f209c180012d799a9e19d7ee79 Mon Sep 17 00:00:00 2001
From: Peter Schlaile <root@asterisk.schlaile.de>
Date: Mon, 25 Aug 2008 17:55:53 +0200
Subject: mISDN: Make debug output a little bit more verbose

Add usefull info to debug output.

Signed-off-by: Peter Schlaile <root@asterisk.schlaile.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/dsp_cmx.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index 93fb819c5203..b16f76c2d684 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -1823,7 +1823,9 @@ dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
 		/* write to the space we have left */
 		ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */
 		if (dsp_debug & DEBUG_DSP_CLOCK)
-			printk(KERN_DEBUG "%s: TX overflow\n", __func__);
+			printk(KERN_DEBUG "%s: TX overflow space=%d skb->len="
+			    "%d, w=0x%04x, ww=0x%04x\n", __func__, space,
+			    skb->len, w, ww);
 	} else
 		/* write until all byte are copied */
 		ww = (w + skb->len) & CMX_BUFF_MASK;
-- 
cgit v1.2.3


From 83a8a55b1deb07ac1ce546a8f230a8ef8f3c5ce5 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sat, 30 Aug 2008 06:50:34 +0200
Subject: mISDN: Fixed more indexing bugs

Fix more indexing bugs  when checking free timeslots.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/dsp_cmx.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index b16f76c2d684..04dbb407f7a0 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -744,11 +744,11 @@ conf_software:
 					if (dsp->pcm_slot_rx >= 0 &&
 					    dsp->pcm_slot_rx <
 					    sizeof(freeslots))
-						freeslots[dsp->pcm_slot_tx] = 0;
+						freeslots[dsp->pcm_slot_rx] = 0;
 					if (dsp->pcm_slot_tx >= 0 &&
 					    dsp->pcm_slot_tx <
 					    sizeof(freeslots))
-						freeslots[dsp->pcm_slot_rx] = 0;
+						freeslots[dsp->pcm_slot_tx] = 0;
 				}
 			}
 			i = 0;
@@ -836,11 +836,11 @@ conf_software:
 					if (dsp->pcm_slot_rx >= 0 &&
 					    dsp->pcm_slot_rx <
 					    sizeof(freeslots))
-						freeslots[dsp->pcm_slot_tx] = 0;
+						freeslots[dsp->pcm_slot_rx] = 0;
 					if (dsp->pcm_slot_tx >= 0 &&
 					    dsp->pcm_slot_tx <
 					    sizeof(freeslots))
-						freeslots[dsp->pcm_slot_rx] = 0;
+						freeslots[dsp->pcm_slot_tx] = 0;
 				}
 			}
 			i1 = 0;
-- 
cgit v1.2.3


From a9b61830cd88a1d3d5e6d61adb737a3e20f7f0f5 Mon Sep 17 00:00:00 2001
From: Martin Bachem <m.bachem@gmx.de>
Date: Wed, 3 Sep 2008 18:08:30 +0200
Subject: mISDN: Use protocol to detect D-channel

Use protocol to detect D-channel not the channel number 0

Signed-off-by: Martin Bachem <m.bachem@gmx.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcpci.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index 8461477a3ac0..37c2e19c3b00 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -1964,7 +1964,8 @@ hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
 	switch (cmd) {
 	case OPEN_CHANNEL:
 		rq = arg;
-		if (rq->adr.channel == 0)
+		if ((rq->protocol == ISDN_P_TE_S0) ||
+		    (rq->protocol == ISDN_P_NT_S0))
 			err = open_dchannel(hc, ch, rq);
 		else
 			err = open_bchannel(hc, rq);
-- 
cgit v1.2.3


From 1f28fa19d34c0d9186f274e61e4b3dcfc6428c5c Mon Sep 17 00:00:00 2001
From: Martin Bachem <m.bachem@gmx.de>
Date: Wed, 3 Sep 2008 15:17:45 +0200
Subject: mISDN: Add E-Channel logging features

New prim PH_DATA_E_IND.

 - all E-ch frames are indicated by recv_Echannel(), which pushes E-Channel
   frames into dch's rqueue
 - if dchannel is opened with channel nr 0, no E-Channel logging
   is requested
 - if dchannel is opened with channel nr 1, E-Channel logging
   is requested. if layer1 does not support that, -EINVAL
   in return is appropriate

Signed-off-by: Martin Bachem <m.bachem@gmx.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/hwchannel.c | 19 +++++++++++++++++++
 drivers/isdn/mISDN/stack.c     |  2 +-
 include/linux/mISDNhw.h        |  1 +
 include/linux/mISDNif.h        |  1 +
 4 files changed, 22 insertions(+), 1 deletion(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c
index 535ceacc05b9..ab1168a110ae 100644
--- a/drivers/isdn/mISDN/hwchannel.c
+++ b/drivers/isdn/mISDN/hwchannel.c
@@ -165,6 +165,25 @@ recv_Dchannel(struct dchannel *dch)
 }
 EXPORT_SYMBOL(recv_Dchannel);
 
+void
+recv_Echannel(struct dchannel *ech, struct dchannel *dch)
+{
+	struct mISDNhead *hh;
+
+	if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */
+		dev_kfree_skb(ech->rx_skb);
+		ech->rx_skb = NULL;
+		return;
+	}
+	hh = mISDN_HEAD_P(ech->rx_skb);
+	hh->prim = PH_DATA_E_IND;
+	hh->id = get_sapi_tei(ech->rx_skb->data);
+	skb_queue_tail(&dch->rqueue, ech->rx_skb);
+	ech->rx_skb = NULL;
+	schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Echannel);
+
 void
 recv_Bchannel(struct bchannel *bch)
 {
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index 9b9fab47cb2b..8cff570bb8df 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -453,7 +453,7 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
 		ch->peer = &dev->D.st->own;
 		ch->st = dev->D.st;
 		rq.protocol = protocol;
-		rq.adr.channel = 0;
+		rq.adr.channel = adr->channel;
 		err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
 		printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
 		if (err)
diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h
index 9384b92dfc65..97ffdc1d3442 100644
--- a/include/linux/mISDNhw.h
+++ b/include/linux/mISDNhw.h
@@ -184,6 +184,7 @@ extern void	queue_ch_frame(struct mISDNchannel *, u_int,
 extern int	dchannel_senddata(struct dchannel *, struct sk_buff *);
 extern int	bchannel_senddata(struct bchannel *, struct sk_buff *);
 extern void	recv_Dchannel(struct dchannel *);
+extern void	recv_Echannel(struct dchannel *, struct dchannel *);
 extern void	recv_Bchannel(struct bchannel *);
 extern void	recv_Dchannel_skb(struct dchannel *, struct sk_buff *);
 extern void	recv_Bchannel_skb(struct bchannel *, struct sk_buff *);
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index a59febb6143a..f75d596c5316 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -80,6 +80,7 @@
 #define PH_DEACTIVATE_IND	0x0202
 #define PH_DEACTIVATE_CNF	0x4202
 #define PH_DATA_IND		0x2002
+#define PH_DATA_E_IND		0x3002
 #define MPH_ACTIVATE_IND	0x0502
 #define MPH_DEACTIVATE_IND	0x0602
 #define MPH_INFORMATION_IND	0x0702
-- 
cgit v1.2.3


From 55a6af9749d6a31e087f304f2ea39db3bb7b3f3e Mon Sep 17 00:00:00 2001
From: Martin Bachem <m.bachem@gmx.de>
Date: Thu, 4 Sep 2008 12:42:39 +0200
Subject: mISDN: Return error on E-channel access

Return error on D-channel access with E-channel data requested

Signed-off-by: Martin Bachem <m.bachem@gmx.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcpci.c | 4 ++++
 1 file changed, 4 insertions(+)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index 37c2e19c3b00..ea69d5862f6d 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -1894,6 +1894,10 @@ open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch,
 		    hc->dch.dev.id, __builtin_return_address(0));
 	if (rq->protocol == ISDN_P_NONE)
 		return -EINVAL;
+	if (rq->adr.channel == 1) {
+		/* TODO: E-Channel */
+		return -EINVAL;
+	}
 	if (!hc->initdone) {
 		if (rq->protocol == ISDN_P_TE_S0) {
 			err = create_l1(&hc->dch, hfc_l1callback);
-- 
cgit v1.2.3


From 3bd69ad197a4a3d0085a5dc3b5796111bf176b12 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sat, 6 Sep 2008 09:03:46 +0200
Subject: mISDN: Add ISDN sample clock API to mISDN core

Add ISDN sample clock API to mISDN core (new file clock.c)
hfcmulti and mISDNdsp use clock API.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfc_multi.h |   3 +
 drivers/isdn/hardware/mISDN/hfcmulti.c  |  45 +++++--
 drivers/isdn/mISDN/Makefile             |   2 +-
 drivers/isdn/mISDN/clock.c              | 216 ++++++++++++++++++++++++++++++++
 drivers/isdn/mISDN/core.c               |   1 +
 drivers/isdn/mISDN/core.h               |   2 +
 drivers/isdn/mISDN/dsp_cmx.c            |  40 +++---
 include/linux/mISDNif.h                 |  17 +++
 8 files changed, 294 insertions(+), 32 deletions(-)
 create mode 100644 drivers/isdn/mISDN/clock.c

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
index 4aa6a8b41f50..663b77f578be 100644
--- a/drivers/isdn/hardware/mISDN/hfc_multi.h
+++ b/drivers/isdn/hardware/mISDN/hfc_multi.h
@@ -197,6 +197,9 @@ struct hfc_multi {
 
 	spinlock_t	lock;	/* the lock */
 
+	struct mISDNclock *iclock; /* isdn clock support */
+	int		iclock_on;
+
 	/*
 	 * the channel index is counted from 0, regardless where the channel
 	 * is located on the hfc-channel.
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 3fc2e9d95341..592db93105f9 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -133,6 +133,12 @@
  *	Give the value of the clock control register (A_ST_CLK_DLY)
  *	of the S/T interfaces in TE mode.
  *	This register is needed for the TBR3 certification, so don't change it.
+ *
+ * clock:
+ *	NOTE: only one clockdelay_te value must be given once
+ *	Selects interface with clock source for mISDN and applications.
+ *	Set to card number starting with 1. Set to -1 to disable.
+ *	By default, the first card is used as clock source.
  */
 
 /*
@@ -190,12 +196,13 @@ static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30  };
  */
 
 static uint	type[MAX_CARDS];
-static uint	pcm[MAX_CARDS];
-static uint	dslot[MAX_CARDS];
+static int	pcm[MAX_CARDS];
+static int	dslot[MAX_CARDS];
 static uint	iomode[MAX_CARDS];
 static uint	port[MAX_PORTS];
 static uint	debug;
 static uint	poll;
+static int	clock;
 static uint	timer;
 static uint	clockdelay_te = CLKDEL_TE;
 static uint	clockdelay_nt = CLKDEL_NT;
@@ -207,12 +214,13 @@ MODULE_LICENSE("GPL");
 MODULE_VERSION(HFC_MULTI_VERSION);
 module_param(debug, uint, S_IRUGO | S_IWUSR);
 module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(clock, int, S_IRUGO | S_IWUSR);
 module_param(timer, uint, S_IRUGO | S_IWUSR);
 module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR);
 module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR);
 module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
-module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR);
-module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR);
+module_param_array(dslot, int, NULL, S_IRUGO | S_IWUSR);
 module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR);
 module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
 
@@ -2629,6 +2637,7 @@ hfcmulti_interrupt(int intno, void *dev_id)
 		iqcnt = 0;
 	}
 #endif
+
 	if (!r_irq_statech &&
 	    !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA |
 	    V_MISC_IRQSTA | V_FR_IRQSTA))) {
@@ -2683,11 +2692,13 @@ hfcmulti_interrupt(int intno, void *dev_id)
 					plxsd_checksync(hc, 0);
 			}
 		}
-		if (r_irq_misc & V_TI_IRQ)
+		if (r_irq_misc & V_TI_IRQ) {
+			if (hc->iclock_on)
+				mISDN_clock_update(hc->iclock, poll, NULL);
 			handle_timer_irq(hc);
+		}
 
 		if (r_irq_misc & V_DTMF_IRQ) {
-			/* -> DTMF IRQ */
 			hfcmulti_dtmf(hc);
 		}
 		if (r_irq_misc & V_IRQ_PROC) {
@@ -4075,6 +4086,15 @@ hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
 	return err;
 }
 
+static int
+clockctl(void *priv, int enable)
+{
+	struct hfc_multi *hc = priv;
+
+	hc->iclock_on = enable;
+	return 0;
+}
+
 /*
  * initialize the card
  */
@@ -4489,10 +4509,14 @@ release_card(struct hfc_multi *hc)
 		printk(KERN_WARNING "%s: release card (%d) entered\n",
 		    __func__, hc->id);
 
+	/* unregister clock source */
+	if (hc->iclock)
+		mISDN_unregister_clock(hc->iclock);
+
+	/* disable irq */
 	spin_lock_irqsave(&hc->lock, flags);
 	disable_hwirq(hc);
 	spin_unlock_irqrestore(&hc->lock, flags);
-
 	udelay(1000);
 
 	/* dimm leds */
@@ -5003,6 +5027,10 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 	list_add_tail(&hc->list, &HFClist);
 	spin_unlock_irqrestore(&HFClock, flags);
 
+	/* use as clock source */
+	if (clock == HFC_cnt + 1)
+		hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc);
+
 	/* initialize hardware */
 	ret_err = init_card(hc);
 	if (ret_err) {
@@ -5273,6 +5301,9 @@ HFCmulti_init(void)
 
 	}
 
+	if (!clock)
+		clock = 1;
+
 	err = pci_register_driver(&hfcmultipci_driver);
 	if (err < 0) {
 		printk(KERN_ERR "error registering pci driver: %x\n", err);
diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile
index 1cb5e633cf75..0a6bd2a9e730 100644
--- a/drivers/isdn/mISDN/Makefile
+++ b/drivers/isdn/mISDN/Makefile
@@ -8,6 +8,6 @@ obj-$(CONFIG_MISDN_L1OIP) += l1oip.o
 
 # multi objects
 
-mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o
+mISDN_core-objs := core.o fsm.o socket.o clock.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o
 mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o
 l1oip-objs := l1oip_core.o l1oip_codec.o
diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c
new file mode 100644
index 000000000000..44d9c3d5d33d
--- /dev/null
+++ b/drivers/isdn/mISDN/clock.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2008  by Andreas Eversberg <andreas@eversberg.eu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Quick API description:
+ *
+ * A clock source registers using mISDN_register_clock:
+ * 	name = text string to name clock source
+ *	priority = value to priorize clock sources (0 = default)
+ *	ctl = callback function to enable/disable clock source
+ *	priv = private pointer of clock source
+ * 	return = pointer to clock source structure;
+ *
+ * Note: Callback 'ctl' can be called before mISDN_register_clock returns!
+ *       Also it can be called during mISDN_unregister_clock.
+ *
+ * A clock source calls mISDN_clock_update with given samples elapsed, if
+ * enabled. If function call is delayed, tv must be set with the timestamp
+ * of the actual event.
+ *
+ * A clock source unregisters using mISDN_unregister_clock.
+ *
+ * To get current clock, call mISDN_clock_get. The signed short value
+ * counts the number of samples since. Time since last clock event is added.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static u_int *debug;
+static LIST_HEAD(iclock_list);
+DEFINE_RWLOCK(iclock_lock);
+u16	iclock_count;		/* counter of last clock */
+struct	timeval iclock_tv;	/* time stamp of last clock */
+int	iclock_tv_valid;	/* already received one timestamp */
+struct	mISDNclock *iclock_current;
+
+void
+mISDN_init_clock(u_int *dp)
+{
+	debug = dp;
+	do_gettimeofday(&iclock_tv);
+}
+
+static void
+select_iclock(void)
+{
+	struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL;
+	int pri = -128;
+
+	list_for_each_entry(iclock, &iclock_list, list) {
+		if (iclock->pri > pri) {
+			pri = iclock->pri;
+			bestclock = iclock;
+		}
+		if (iclock_current == iclock)
+			lastclock = iclock;
+	}
+	if (lastclock && bestclock != lastclock) {
+		/* last used clock source still exists but changes, disable */
+		if (*debug & DEBUG_CLOCK)
+			printk(KERN_DEBUG "Old clock source '%s' disable.\n",
+				lastclock->name);
+		lastclock->ctl(lastclock->priv, 0);
+	}
+	if (bestclock && bestclock != iclock_current) {
+		/* new clock source selected, enable */
+		if (*debug & DEBUG_CLOCK)
+			printk(KERN_DEBUG "New clock source '%s' enable.\n",
+				bestclock->name);
+		bestclock->ctl(bestclock->priv, 1);
+	}
+	if (bestclock != iclock_current) {
+		/* no clock received yet */
+		iclock_tv_valid = 0;
+	}
+	iclock_current = bestclock;
+}
+
+struct mISDNclock
+*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv)
+{
+	u_long			flags;
+	struct mISDNclock	*iclock;
+
+	if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
+		printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri);
+	iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC);
+	if (!iclock) {
+		printk(KERN_ERR "%s: No memory for clock entry.\n", __func__);
+		return NULL;
+	}
+	strncpy(iclock->name, name, sizeof(iclock->name)-1);
+	iclock->pri = pri;
+	iclock->priv = priv;
+	iclock->ctl = ctl;
+	write_lock_irqsave(&iclock_lock, flags);
+	list_add_tail(&iclock->list, &iclock_list);
+	select_iclock();
+	write_unlock_irqrestore(&iclock_lock, flags);
+	return iclock;
+}
+EXPORT_SYMBOL(mISDN_register_clock);
+
+void
+mISDN_unregister_clock(struct mISDNclock *iclock)
+{
+	u_long	flags;
+
+	if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
+		printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name,
+			iclock->pri);
+	write_lock_irqsave(&iclock_lock, flags);
+	if (iclock_current == iclock) {
+		if (*debug & DEBUG_CLOCK)
+			printk(KERN_DEBUG
+				"Current clock source '%s' unregisters.\n",
+				iclock->name);
+		iclock->ctl(iclock->priv, 0);
+	}
+	list_del(&iclock->list);
+	select_iclock();
+	write_unlock_irqrestore(&iclock_lock, flags);
+}
+EXPORT_SYMBOL(mISDN_unregister_clock);
+
+void
+mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv)
+{
+	u_long		flags;
+	struct timeval	tv_now;
+	time_t		elapsed_sec;
+	int		elapsed_8000th;
+
+	write_lock_irqsave(&iclock_lock, flags);
+	if (iclock_current != iclock) {
+		printk(KERN_ERR "%s: '%s' sends us clock updates, but we do "
+			"listen to '%s'. This is a bug!\n", __func__,
+			iclock->name,
+			iclock_current ? iclock_current->name : "nothing");
+		iclock->ctl(iclock->priv, 0);
+		write_unlock_irqrestore(&iclock_lock, flags);
+		return;
+	}
+	if (iclock_tv_valid) {
+		/* increment sample counter by given samples */
+		iclock_count += samples;
+		if (tv) { /* tv must be set, if function call is delayed */
+			iclock_tv.tv_sec = tv->tv_sec;
+			iclock_tv.tv_usec = tv->tv_usec;
+		} else
+			do_gettimeofday(&iclock_tv);
+	} else {
+		/* calc elapsed time by system clock */
+		if (tv) { /* tv must be set, if function call is delayed */
+			tv_now.tv_sec = tv->tv_sec;
+			tv_now.tv_usec = tv->tv_usec;
+		} else
+			do_gettimeofday(&tv_now);
+		elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec;
+		elapsed_8000th = (tv_now.tv_usec / 125)
+			- (iclock_tv.tv_usec / 125);
+		if (elapsed_8000th < 0) {
+			elapsed_sec -= 1;
+			elapsed_8000th += 8000;
+		}
+		/* add elapsed time to counter and set new timestamp */
+		iclock_count += elapsed_sec * 8000 + elapsed_8000th;
+		iclock_tv.tv_sec = tv_now.tv_sec;
+		iclock_tv.tv_usec = tv_now.tv_usec;
+		iclock_tv_valid = 1;
+		if (*debug & DEBUG_CLOCK)
+			printk("Received first clock from source '%s'.\n",
+			    iclock_current ? iclock_current->name : "nothing");
+	}
+	write_unlock_irqrestore(&iclock_lock, flags);
+}
+EXPORT_SYMBOL(mISDN_clock_update);
+
+unsigned short
+mISDN_clock_get(void)
+{
+	u_long		flags;
+	struct timeval	tv_now;
+	time_t		elapsed_sec;
+	int		elapsed_8000th;
+	u16		count;
+
+	read_lock_irqsave(&iclock_lock, flags);
+	/* calc elapsed time by system clock */
+	do_gettimeofday(&tv_now);
+	elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec;
+	elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125);
+	if (elapsed_8000th < 0) {
+		elapsed_sec -= 1;
+		elapsed_8000th += 8000;
+	}
+	/* add elapsed time to counter */
+	count =	iclock_count + elapsed_sec * 8000 + elapsed_8000th;
+	read_unlock_irqrestore(&iclock_lock, flags);
+	return count;
+}
+EXPORT_SYMBOL(mISDN_clock_get);
+
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
index 43fd97b0fe42..9116f54def2c 100644
--- a/drivers/isdn/mISDN/core.c
+++ b/drivers/isdn/mISDN/core.c
@@ -199,6 +199,7 @@ mISDNInit(void)
 
 	printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
 		MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
+	mISDN_init_clock(&debug);
 	mISDN_initstack(&debug);
 	err = mISDN_inittimer(&debug);
 	if (err)
diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h
index 7da7233b4c1a..7ac2f81a812b 100644
--- a/drivers/isdn/mISDN/core.h
+++ b/drivers/isdn/mISDN/core.h
@@ -74,4 +74,6 @@ extern void	l1_cleanup(void);
 extern int 	Isdnl2_Init(u_int *);
 extern void	Isdnl2_cleanup(void);
 
+extern void	mISDN_init_clock(u_int *);
+
 #endif
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index 04dbb407f7a0..efe4c7430e6d 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -1557,13 +1557,11 @@ send_packet:
 	schedule_work(&dsp->workq);
 }
 
-static u32	samplecount;
+static u32	jittercount; /* counter for jitter check */;
 struct timer_list dsp_spl_tl;
 u32	dsp_spl_jiffies; /* calculate the next time to fire */
-#ifdef UNUSED
-static u32	dsp_start_jiffies; /* jiffies at the time, the calculation begins */
-#endif /* UNUSED */
-static struct timeval dsp_start_tv; /* time at start of calculation */
+static u16	dsp_count; /* last sample count */
+static int	dsp_count_valid ; /* if we have last sample count */
 
 void
 dsp_cmx_send(void *arg)
@@ -1577,38 +1575,32 @@ dsp_cmx_send(void *arg)
 	int r, rr;
 	int jittercheck = 0, delay, i;
 	u_long flags;
-	struct timeval tv;
-	u32 elapsed;
-	s16 length;
+	u16 length, count;
 
 	/* lock */
 	spin_lock_irqsave(&dsp_lock, flags);
 
-	if (!dsp_start_tv.tv_sec) {
-		do_gettimeofday(&dsp_start_tv);
+	if (!dsp_count_valid) {
+		dsp_count = mISDN_clock_get();
 		length = dsp_poll;
+		dsp_count_valid = 1;
 	} else {
-		do_gettimeofday(&tv);
-		elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000)
-		    + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125));
-		dsp_start_tv.tv_sec = tv.tv_sec;
-		dsp_start_tv.tv_usec = tv.tv_usec;
-		length = elapsed;
+		count = mISDN_clock_get();
+		length = count - dsp_count;
+		dsp_count = count;
 	}
 	if (length > MAX_POLL + 100)
 		length = MAX_POLL + 100;
-/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n",
- length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16,
- dsp_poll_diff & 0xffff);
- */
+	/* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */
 
 	/*
-	 * check if jitter needs to be checked
-	 * (this is about every second = 8192 samples)
+	 * check if jitter needs to be checked (this is every second)
 	 */
-	samplecount += length;
-	if ((samplecount & 8191) < length)
+	jittercount += length;
+	if (jittercount >= 8000) {
+		jittercount -= 8000;
 		jittercheck = 1;
+	}
 
 	/* loop all members that do not require conference mixing */
 	list_for_each_entry(dsp, &dsp_ilist, list) {
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index f75d596c5316..364f1018f0d1 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -371,6 +371,7 @@ struct mISDN_ctrl_req {
 #define DEBUG_L2_TEI		0x00100000
 #define DEBUG_L2_TEIFSM		0x00200000
 #define DEBUG_TIMER		0x01000000
+#define DEBUG_CLOCK		0x02000000
 
 #define mISDN_HEAD_P(s)		((struct mISDNhead *)&s->cb[0])
 #define mISDN_HEAD_PRIM(s)	(((struct mISDNhead *)&s->cb[0])->prim)
@@ -384,6 +385,7 @@ struct mISDN_ctrl_req {
 struct mISDNchannel;
 struct mISDNdevice;
 struct mISDNstack;
+struct mISDNclock;
 
 struct channel_req {
 	u_int			protocol;
@@ -460,6 +462,16 @@ struct mISDNstack {
 #endif
 };
 
+typedef	int	(clockctl_func_t)(void *, int);
+
+struct	mISDNclock {
+	struct list_head	list;
+	char			name[64];
+	int			pri;
+	clockctl_func_t		*ctl;
+	void			*priv;
+};
+
 /* global alloc/queue functions */
 
 static inline struct sk_buff *
@@ -510,8 +522,13 @@ extern int	mISDN_register_device(struct mISDNdevice *, char *name);
 extern void	mISDN_unregister_device(struct mISDNdevice *);
 extern int	mISDN_register_Bprotocol(struct Bprotocol *);
 extern void	mISDN_unregister_Bprotocol(struct Bprotocol *);
+extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *,
+						void *);
+extern void	mISDN_unregister_clock(struct mISDNclock *);
 
 extern void	set_channel_address(struct mISDNchannel *, u_int, u_int);
+extern void	mISDN_clock_update(struct mISDNclock *, int, struct timeval *);
+extern unsigned short mISDN_clock_get(void);
 
 #endif /* __KERNEL__ */
 #endif /* mISDNIF_H */
-- 
cgit v1.2.3


From 9e6115f2f5a9f8568bc8565d1401086952f09736 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sat, 6 Sep 2008 09:11:03 +0200
Subject: mISDN: Fix irq detection

Fix false irq detection on disabled irqs.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcmulti.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 592db93105f9..4cfab20d71a9 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -2658,6 +2658,7 @@ hfcmulti_interrupt(int intno, void *dev_id)
 	if (status & V_MISC_IRQSTA) {
 		/* misc IRQ */
 		r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC);
+		r_irq_misc &= hc->hw.r_irqmsk_misc; /* ignore disabled irqs */
 		if (r_irq_misc & V_STA_IRQ) {
 			if (hc->type == 1) {
 				/* state machine */
-- 
cgit v1.2.3


From 1b4d33121f1d991f6ae226cc3333428ff87627bb Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sun, 14 Sep 2008 12:30:18 +0200
Subject: mISDN: Fix deactivation, if peer IP is removed from l1oip instance.

 Added GETPEER operation.
 Socket now checks if device is already busy at a differen mode.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/l1oip_core.c | 22 +++++++++++++++++++++-
 drivers/isdn/mISDN/socket.c     | 20 ++++++++++++++++++++
 drivers/isdn/mISDN/stack.c      | 18 ------------------
 include/linux/mISDNif.h         |  5 +++--
 4 files changed, 44 insertions(+), 21 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index 0884dd6892f8..3ddcd2e09dc9 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -777,6 +777,8 @@ fail:
 static void
 l1oip_socket_close(struct l1oip *hc)
 {
+	struct dchannel *dch = hc->chan[hc->d_idx].dch;
+
 	/* kill thread */
 	if (hc->socket_thread) {
 		if (debug & DEBUG_L1OIP_SOCKET)
@@ -785,6 +787,16 @@ l1oip_socket_close(struct l1oip *hc)
 		send_sig(SIGTERM, hc->socket_thread, 0);
 		wait_for_completion(&hc->socket_complete);
 	}
+
+	/* if active, we send up a PH_DEACTIVATE and deactivate */
+	if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+		if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+			printk(KERN_DEBUG "%s: interface become deactivated "
+				"due to timeout\n", __func__);
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_ATOMIC);
+	}
 }
 
 static int
@@ -944,7 +956,8 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
 
 	switch (cq->op) {
 	case MISDN_CTRL_GETOP:
-		cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER;
+		cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER
+			| MISDN_CTRL_GETPEER;
 		break;
 	case MISDN_CTRL_SETPEER:
 		hc->remoteip = (u32)cq->p1;
@@ -964,6 +977,13 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
 		hc->remoteip = 0;
 		l1oip_socket_open(hc);
 		break;
+	case MISDN_CTRL_GETPEER:
+		if (debug & DEBUG_L1OIP_SOCKET)
+			printk(KERN_DEBUG "%s: getting ip address.\n",
+				__func__);
+		(u32)cq->p1 = hc->remoteip;
+		cq->p2 = hc->remoteport | (hc->localport << 16);
+		break;
 	default:
 		printk(KERN_WARNING "%s: unknown Op %x\n",
 		    __func__, cq->op);
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index 2f6d6e88ff2c..916569ca156d 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -460,6 +460,8 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
 {
 	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
 	struct sock *sk = sock->sk;
+	struct hlist_node *node;
+	struct sock *csk;
 	int err = 0;
 
 	if (*debug & DEBUG_SOCKET)
@@ -480,6 +482,24 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
 		err = -ENODEV;
 		goto done;
 	}
+
+	read_lock_bh(&data_sockets.lock);
+	sk_for_each(csk, node, &data_sockets.head) {
+		if (sk == csk)
+			continue;
+		if (_pms(csk)->dev != _pms(sk)->dev)
+			continue;
+		if (csk->sk_protocol >= ISDN_P_B_START)
+			continue;
+		if (IS_ISDN_P_TE(csk->sk_protocol)
+				== IS_ISDN_P_TE(sk->sk_protocol))
+			continue;
+		read_unlock_bh(&data_sockets.lock);
+		err = -EBUSY;
+		goto done;
+	}
+	read_unlock_bh(&data_sockets.lock);
+
 	_pms(sk)->ch.send = mISDN_send;
 	_pms(sk)->ch.ctrl = mISDN_ctrl;
 
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index 8cff570bb8df..63afa8cf9e07 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -440,15 +440,6 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
 	case ISDN_P_NT_E1:
 	case ISDN_P_TE_S0:
 	case ISDN_P_TE_E1:
-#ifdef PROTOCOL_CHECK
-		/* this should be enhanced */
-		if (!list_empty(&dev->D.st->layer2)
-			&& dev->D.protocol != protocol)
-			return -EBUSY;
-		if (!hlist_empty(&dev->D.st->l1sock.head)
-			&& dev->D.protocol != protocol)
-			return -EBUSY;
-#endif
 		ch->recv = mISDN_queue_message;
 		ch->peer = &dev->D.st->own;
 		ch->st = dev->D.st;
@@ -546,15 +537,6 @@ create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
 		if (dev->Dprotocols & (1 << ISDN_P_NT_E1))
 			rq.protocol = ISDN_P_NT_E1;
 	case ISDN_P_LAPD_TE:
-#ifdef PROTOCOL_CHECK
-		/* this should be enhanced */
-		if (!list_empty(&dev->D.st->layer2)
-			&& dev->D.protocol != protocol)
-			return -EBUSY;
-		if (!hlist_empty(&dev->D.st->l1sock.head)
-			&& dev->D.protocol != protocol)
-			return -EBUSY;
-#endif
 		ch->recv = mISDN_queue_message;
 		ch->peer = &dev->D.st->own;
 		ch->st = dev->D.st;
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index 7f65aa0c1cc5..3f9988849f32 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -204,9 +204,9 @@
 #define ISDN_P_NT_UP0		0x06
 
 #define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \
-				(p == ISDN_P_TE_UP0))
+				(p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE))
 #define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \
-				(p == ISDN_P_NT_UP0))
+				(p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT))
 #define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0))
 #define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1))
 #define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0))
@@ -333,6 +333,7 @@ clear_channelmap(u_int nr, u_char *map)
 #define MISDN_CTRL_UNSETPEER		0x0080
 #define MISDN_CTRL_RX_OFF		0x0100
 #define MISDN_CTRL_FILL_EMPTY		0x0200
+#define MISDN_CTRL_GETPEER		0x0400
 #define MISDN_CTRL_HW_FEATURES_OP	0x2000
 #define MISDN_CTRL_HW_FEATURES		0x2001
 #define MISDN_CTRL_HFC_OP		0x4000
-- 
cgit v1.2.3


From 9a812553bdc097a566aa79df7fae3457449c555b Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sun, 14 Sep 2008 14:42:18 +0200
Subject: mISDN: Correct busy device detection

Correct busy device detection.
This fix belongs to last commit.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/socket.c | 30 ++++++++++++++++--------------
 drivers/isdn/mISDN/stack.c  |  3 ++-
 2 files changed, 18 insertions(+), 15 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index 916569ca156d..508945d1b9c1 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -483,22 +483,24 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
 		goto done;
 	}
 
-	read_lock_bh(&data_sockets.lock);
-	sk_for_each(csk, node, &data_sockets.head) {
-		if (sk == csk)
-			continue;
-		if (_pms(csk)->dev != _pms(sk)->dev)
-			continue;
-		if (csk->sk_protocol >= ISDN_P_B_START)
-			continue;
-		if (IS_ISDN_P_TE(csk->sk_protocol)
-				== IS_ISDN_P_TE(sk->sk_protocol))
-			continue;
+	if (sk->sk_protocol < ISDN_P_B_START) {
+		read_lock_bh(&data_sockets.lock);
+		sk_for_each(csk, node, &data_sockets.head) {
+			if (sk == csk)
+				continue;
+			if (_pms(csk)->dev != _pms(sk)->dev)
+				continue;
+			if (csk->sk_protocol >= ISDN_P_B_START)
+				continue;
+			if (IS_ISDN_P_TE(csk->sk_protocol)
+					== IS_ISDN_P_TE(sk->sk_protocol))
+				continue;
+			read_unlock_bh(&data_sockets.lock);
+			err = -EBUSY;
+			goto done;
+		}
 		read_unlock_bh(&data_sockets.lock);
-		err = -EBUSY;
-		goto done;
 	}
-	read_unlock_bh(&data_sockets.lock);
 
 	_pms(sk)->ch.send = mISDN_send;
 	_pms(sk)->ch.ctrl = mISDN_ctrl;
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index 63afa8cf9e07..e2f45019ebf0 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -446,7 +446,8 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
 		rq.protocol = protocol;
 		rq.adr.channel = adr->channel;
 		err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
-		printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+		printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err,
+			dev->id);
 		if (err)
 			return err;
 		write_lock_bh(&dev->D.st->l1sock.lock);
-- 
cgit v1.2.3


From 1b36c78f26bfa74c1782be98acb827afa95c3b0c Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sat, 20 Sep 2008 13:43:28 +0200
Subject: mISDN: Minor fixes

minor typo fix
compiler warning fix

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcmulti.c | 2 +-
 drivers/isdn/mISDN/l1oip_core.c        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 4cfab20d71a9..cd3f3994b7f8 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -135,7 +135,7 @@
  *	This register is needed for the TBR3 certification, so don't change it.
  *
  * clock:
- *	NOTE: only one clockdelay_te value must be given once
+ *	NOTE: only one clock value must be given once
  *	Selects interface with clock source for mISDN and applications.
  *	Set to card number starting with 1. Set to -1 to disable.
  *	By default, the first card is used as clock source.
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index 3ddcd2e09dc9..a6a9e1af2ad6 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -981,7 +981,7 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
 		if (debug & DEBUG_L1OIP_SOCKET)
 			printk(KERN_DEBUG "%s: getting ip address.\n",
 				__func__);
-		(u32)cq->p1 = hc->remoteip;
+		cq->p1 = hc->remoteip;
 		cq->p2 = hc->remoteport | (hc->localport << 16);
 		break;
 	default:
-- 
cgit v1.2.3


From 87c5fa1bb42624254a2013cbbc3b170d6017f5d6 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sun, 28 Sep 2008 13:01:01 +0200
Subject: mISDN: Add different different timer settings for hfc-pci

     - Poll-timer can now be set from 8 to 256 samples, depending on your kernel.
     - If default or 128 is used, the normal controller's clock is used as before.
       Usage: modprobe hfcpci poll=XXX
     - Added some debug code for dsp buffer size. (CMX_DELAY_DEBUG)

    Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
    Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfc_pci.h |   1 -
 drivers/isdn/hardware/mISDN/hfcpci.c  | 152 ++++++++++++++++++++++++++++------
 drivers/isdn/mISDN/dsp_cmx.c          |  23 +++++
 3 files changed, 148 insertions(+), 28 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h
index fd9241ab1802..3132ddc99fcd 100644
--- a/drivers/isdn/hardware/mISDN/hfc_pci.h
+++ b/drivers/isdn/hardware/mISDN/hfc_pci.h
@@ -26,7 +26,6 @@
  * change mask and threshold simultaneously
  */
 #define HFCPCI_BTRANS_THRESHOLD 128
-#define HFCPCI_BTRANS_MAX	256
 #define HFCPCI_FILLEMPTY	64
 #define HFCPCI_BTRANS_THRESMASK 0x00
 
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index ea69d5862f6d..cd5d26c9909f 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -23,6 +23,25 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
+ * Module options:
+ *
+ * debug:
+ *	NOTE: only one poll value must be given for all cards
+ *	See hfc_pci.h for debug flags.
+ *
+ * poll:
+ *	NOTE: only one poll value must be given for all cards
+ *	Give the number of samples for each fifo process.
+ *	By default 128 is used. Decrease to reduce delay, increase to
+ *	reduce cpu load. If unsure, don't mess with it!
+ *	A value of 128 will use controller's interrupt. Other values will
+ *	use kernel timer, because the controller will not allow lower values
+ *	than 128.
+ *	Also note that the value depends on the kernel timer frequency.
+ *	If kernel uses a frequency of 1000 Hz, steps of 8 samples are possible.
+ *	If the kernel uses 100 Hz, steps of 80 samples are possible.
+ *	If the kernel uses 300 Hz, steps of about 26 samples are possible.
+ *
  */
 
 #include <linux/module.h>
@@ -36,10 +55,14 @@ static const char *hfcpci_revision = "2.0";
 
 static int HFC_cnt;
 static uint debug;
+static uint poll, tics;
+struct timer_list hfc_tl;
+u32	hfc_jiffies;
 
 MODULE_AUTHOR("Karsten Keil");
 MODULE_LICENSE("GPL");
 module_param(debug, uint, 0);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
 
 static LIST_HEAD(HFClist);
 static DEFINE_RWLOCK(HFClock);
@@ -519,9 +542,9 @@ receive_dmsg(struct hfc_pci *hc)
 }
 
 /*
- * check for transparent receive data and read max one threshold size if avail
+ * check for transparent receive data and read max one 'poll' size if avail
  */
-static int
+static void
 hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata)
 {
 	 __le16 *z1r, *z2r;
@@ -533,17 +556,19 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata)
 
 	fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r);
 	if (!fcnt)
-		return 0;	/* no data avail */
+		return;	/* no data avail */
 
 	if (fcnt <= 0)
 		fcnt += B_FIFO_SIZE;	/* bytes actually buffered */
-	if (fcnt > HFCPCI_BTRANS_THRESHOLD)
-		fcnt = HFCPCI_BTRANS_THRESHOLD;		/* limit size */
-
 	new_z2 = le16_to_cpu(*z2r) + fcnt;	/* new position in fifo */
 	if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
 		new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
 
+	if (fcnt > MAX_DATA_SIZE) {	/* flush, if oversized */
+		*z2r = cpu_to_le16(new_z2);		/* new position */
+		return;
+	}
+
 	bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC);
 	if (bch->rx_skb) {
 		ptr = skb_put(bch->rx_skb, fcnt);
@@ -568,7 +593,6 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata)
 		printk(KERN_WARNING "HFCPCI: receive out of memory\n");
 
 	*z2r = cpu_to_le16(new_z2);		/* new position */
-	return 1;
 }
 
 /*
@@ -579,12 +603,11 @@ main_rec_hfcpci(struct bchannel *bch)
 {
 	struct hfc_pci	*hc = bch->hw;
 	int		rcnt, real_fifo;
-	int		receive, count = 5;
+	int		receive = 0, count = 5;
 	struct bzfifo	*bz;
 	u_char		*bdata;
 	struct zt	*zp;
 
-
 	if ((bch->nr & 2) && (!hc->hw.bswapped)) {
 		bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
 		bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2;
@@ -624,9 +647,10 @@ Begin:
 			receive = 1;
 		else
 			receive = 0;
-	} else if (test_bit(FLG_TRANSPARENT, &bch->Flags))
-		receive = hfcpci_empty_fifo_trans(bch, bz, bdata);
-	else
+	} else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+		hfcpci_empty_fifo_trans(bch, bz, bdata);
+		return;
+	} else
 		receive = 0;
 	if (count && receive)
 		goto Begin;
@@ -782,9 +806,9 @@ hfcpci_fill_fifo(struct bchannel *bch)
 
 next_t_frame:
 		count = bch->tx_skb->len - bch->tx_idx;
-		/* maximum fill shall be HFCPCI_BTRANS_MAX */
-		if (count > HFCPCI_BTRANS_MAX - fcnt)
-			count = HFCPCI_BTRANS_MAX - fcnt;
+		/* maximum fill shall be poll*2 */
+		if (count > (poll << 1) - fcnt)
+			count = (poll << 1) - fcnt;
 		if (count <= 0)
 			return;
 		/* data is suitable for fifo */
@@ -1164,37 +1188,37 @@ hfcpci_int(int intno, void *dev_id)
 		val &= ~0x80;
 		Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
 	}
-	if (val & 0x08) {
+	if (val & 0x08) { 	/* B1 rx */
 		bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
 		if (bch)
 			main_rec_hfcpci(bch);
 		else if (hc->dch.debug)
 			printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n");
 	}
-	if (val & 0x10) {
+	if (val & 0x10) {	/* B2 rx */
 		bch = Sel_BCS(hc, 2);
 		if (bch)
 			main_rec_hfcpci(bch);
 		else if (hc->dch.debug)
 			printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n");
 	}
-	if (val & 0x01) {
+	if (val & 0x01) {	/* B1 tx */
 		bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
 		if (bch)
 			tx_birq(bch);
 		else if (hc->dch.debug)
 			printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n");
 	}
-	if (val & 0x02) {
+	if (val & 0x02) {	/* B2 tx */
 		bch = Sel_BCS(hc, 2);
 		if (bch)
 			tx_birq(bch);
 		else if (hc->dch.debug)
 			printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n");
 	}
-	if (val & 0x20)
+	if (val & 0x20)		/* D rx */
 		receive_dmsg(hc);
-	if (val & 0x04) {	/* dframe transmitted */
+	if (val & 0x04) {	/* D tx */
 		if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags))
 			del_timer(&hc->dch.timer);
 		tx_dirq(&hc->dch);
@@ -1203,6 +1227,41 @@ hfcpci_int(int intno, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void
+hfcpci_softirq(void *arg)
+{
+	u_long		flags;
+	struct bchannel	*bch;
+	struct hfc_pci	*hc;
+
+	write_lock_irqsave(&HFClock, flags);
+	list_for_each_entry(hc, &HFClist, list) {
+		if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) {
+			spin_lock(&hc->lock);
+			bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+			if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */
+				main_rec_hfcpci(bch);
+				tx_birq(bch);
+			}
+			bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2);
+			if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */
+				main_rec_hfcpci(bch);
+				tx_birq(bch);
+			}
+			spin_unlock(&hc->lock);
+		}
+	}
+	write_unlock_irqrestore(&HFClock, flags);
+
+	/* if next event would be in the past ... */
+	if ((s32)(hfc_jiffies + tics - jiffies) <= 0)
+		hfc_jiffies = jiffies + 1;
+	else
+		hfc_jiffies += tics;
+	hfc_tl.expires = hfc_jiffies;
+	add_timer(&hfc_tl);
+}
+
 /*
  * timer callback for D-chan busy resolution. Currently no function
  */
@@ -1312,14 +1371,16 @@ mode_hfcpci(struct bchannel *bch, int bc, int protocol)
 		}
 		if (fifo2 & 2) {
 			hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
-			hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
-			    HFCPCI_INTS_B2REC);
+			if (!tics)
+				hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
+				    HFCPCI_INTS_B2REC);
 			hc->hw.ctmt |= 2;
 			hc->hw.conn &= ~0x18;
 		} else {
 			hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
-			hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
-			    HFCPCI_INTS_B1REC);
+			if (!tics)
+				hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
+				    HFCPCI_INTS_B1REC);
 			hc->hw.ctmt |= 1;
 			hc->hw.conn &= ~0x03;
 		}
@@ -1427,7 +1488,8 @@ set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan)
 		if (chan & 2) {
 			hc->hw.sctrl_r |= SCTRL_B2_ENA;
 			hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
-			hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
+			if (!tics)
+				hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
 			hc->hw.ctmt |= 2;
 			hc->hw.conn &= ~0x18;
 #ifdef REVERSE_BITORDER
@@ -1436,7 +1498,8 @@ set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan)
 		} else {
 			hc->hw.sctrl_r |= SCTRL_B1_ENA;
 			hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
-			hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
+			if (!tics)
+				hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
 			hc->hw.ctmt |= 1;
 			hc->hw.conn &= ~0x03;
 #ifdef REVERSE_BITORDER
@@ -2273,7 +2336,39 @@ HFC_init(void)
 {
 	int		err;
 
+	if (!poll)
+		poll = HFCPCI_BTRANS_THRESHOLD;
+
+	if (poll != HFCPCI_BTRANS_THRESHOLD) {
+		tics = poll * HZ / 8000;
+		if (tics < 1)
+			tics = 1;
+		poll = tics * 8000 / HZ;
+		if (poll > 256 || poll < 8) {
+			printk(KERN_ERR "%s: Wrong poll value %d not in range "
+				"of 8..256.\n", __func__, poll);
+			err = -EINVAL;
+			return err;
+		}
+	}
+	if (poll != HFCPCI_BTRANS_THRESHOLD) {
+		printk(KERN_INFO "%s: Using alternative poll value of %d\n",
+			__func__, poll);
+		hfc_tl.function = (void *)hfcpci_softirq;
+		hfc_tl.data = 0;
+		init_timer(&hfc_tl);
+		hfc_tl.expires = jiffies + tics;
+		hfc_jiffies = hfc_tl.expires;
+		add_timer(&hfc_tl);
+	} else
+		tics = 0; /* indicate the use of controller's timer */
+
 	err = pci_register_driver(&hfc_driver);
+	if (err) {
+		if (timer_pending(&hfc_tl))
+			del_timer(&hfc_tl);
+	}
+
 	return err;
 }
 
@@ -2282,6 +2377,9 @@ HFC_cleanup(void)
 {
 	struct hfc_pci	*card, *next;
 
+	if (timer_pending(&hfc_tl))
+		del_timer(&hfc_tl);
+
 	list_for_each_entry_safe(card, next, &HFClist, list) {
 		release_card(card);
 	}
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index efe4c7430e6d..02f643456b16 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -137,6 +137,7 @@
 /* #define CMX_CONF_DEBUG */
 
 /*#define CMX_DEBUG * massive read/write pointer output */
+/*#define CMX_DELAY_DEBUG * gives rx-buffer delay overview */
 /*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */
 
 static inline int
@@ -1135,6 +1136,25 @@ dsp_cmx_conf(struct dsp *dsp, u32 conf_id)
 	return 0;
 }
 
+#ifdef CMX_DELAY_DEBUG
+int delaycount;
+static void
+showdelay(struct dsp *dsp, int samples, int delay)
+{
+	char bar[] = "--------------------------------------------------|";
+	int sdelay;
+
+	delaycount += samples;
+	if (delaycount < 8000)
+		return;
+	delaycount = 0;
+
+	sdelay = delay * 50 / (dsp_poll << 2);
+
+	printk(KERN_DEBUG "DELAY (%s) %3d >%s\n", dsp->name, delay,
+		sdelay > 50 ? "..." : bar + 50 - sdelay);
+}
+#endif
 
 /*
  * audio data is received from card
@@ -1256,6 +1276,9 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
 
 	/* increase write-pointer */
 	dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
+#ifdef CMX_DELAY_DEBUG
+	showdelay(dsp, len, (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK);
+#endif
 }
 
 
-- 
cgit v1.2.3


From 808a14a1583ca3790bf0a9c20c7d4cbac212c775 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sun, 28 Sep 2008 15:40:15 +0200
Subject: mISDN: Add missing release functions

Add missing release function of ec-devices. Each device require a relase
function now. All destruction (memory and list entry) must be done within
the given release function of device, rather than after unregistering device.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/dsp_pipeline.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
index 83639be7f7ad..7a3a3e1e4d80 100644
--- a/drivers/isdn/mISDN/dsp_pipeline.c
+++ b/drivers/isdn/mISDN/dsp_pipeline.c
@@ -75,6 +75,15 @@ static struct device_attribute element_attributes[] = {
 	__ATTR(args, 0444, attr_show_args, NULL),
 };
 
+static void
+mISDN_dsp_dev_release(struct device *dev)
+{
+	struct dsp_element_entry *entry =
+		container_of(dev, struct dsp_element_entry, dev);
+	list_del(&entry->list);
+	kfree(entry);
+}
+
 int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
 {
 	struct dsp_element_entry *entry;
@@ -90,6 +99,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
 	entry->elem = elem;
 
 	entry->dev.class = elements_class;
+	entry->dev.release = mISDN_dsp_dev_release;
 	dev_set_drvdata(&entry->dev, elem);
 	dev_set_name(&entry->dev, elem->name);
 	ret = device_register(&entry->dev);
@@ -98,6 +108,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
 			__func__, elem->name);
 		goto err1;
 	}
+	list_add_tail(&entry->list, &dsp_elements);
 
 	for (i = 0; i < (sizeof(element_attributes)
 		/ sizeof(struct device_attribute)); ++i)
@@ -109,14 +120,15 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
 			goto err2;
 		}
 
-	list_add_tail(&entry->list, &dsp_elements);
-
+#ifdef PIPELINE_DEBUG
 	printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
+#endif
 
 	return 0;
 
 err2:
 	device_unregister(&entry->dev);
+	return ret;
 err1:
 	kfree(entry);
 	return ret;
@@ -132,11 +144,11 @@ void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
 
 	list_for_each_entry_safe(entry, n, &dsp_elements, list)
 		if (entry->elem == elem) {
-			list_del(&entry->list);
 			device_unregister(&entry->dev);
-			kfree(entry);
+#ifdef PIPELINE_DEBUG
 			printk(KERN_DEBUG "%s: %s unregistered\n",
 				__func__, elem->name);
+#endif
 			return;
 		}
 	printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
@@ -173,7 +185,9 @@ void dsp_pipeline_module_exit(void)
 		kfree(entry);
 	}
 
+#ifdef PIPELINE_DEBUG
 	printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
+#endif
 }
 
 int dsp_pipeline_init(struct dsp_pipeline *pipeline)
@@ -258,7 +272,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 				pipeline_entry = kmalloc(sizeof(struct
 					dsp_pipeline_entry), GFP_KERNEL);
 				if (!pipeline_entry) {
-					printk(KERN_DEBUG "%s: failed to add "
+					printk(KERN_ERR "%s: failed to add "
 					    "entry to pipeline: %s (out of "
 					    "memory)\n", __func__, elem->name);
 					incomplete = 1;
@@ -286,7 +300,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 						    args : "");
 #endif
 					} else {
-						printk(KERN_DEBUG "%s: failed "
+						printk(KERN_ERR "%s: failed "
 						  "to add entry to pipeline: "
 						  "%s (new() returned NULL)\n",
 						  __func__, elem->name);
@@ -301,7 +315,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 		if (found)
 			found = 0;
 		else {
-			printk(KERN_DEBUG "%s: element not found, skipping: "
+			printk(KERN_ERR "%s: element not found, skipping: "
 				"%s\n", __func__, name);
 			incomplete = 1;
 		}
-- 
cgit v1.2.3


From b36b654a7e82308cea063cdf909a7f246105c2a3 Mon Sep 17 00:00:00 2001
From: Matthias Urlichs <smurf@smurf.noris.de>
Date: Sat, 16 Aug 2008 00:09:24 +0200
Subject: mISDN: Create /sys/class/mISDN

Create /sys/class/mISDN and implement functions to handle
device renames.

Signed-Off-By: Matthias Urlichs <matthias@urlichs.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcmulti.c |   6 +-
 drivers/isdn/hardware/mISDN/hfcpci.c   | 100 ++++++------
 drivers/isdn/mISDN/core.c              | 271 ++++++++++++++++++++++++++-------
 drivers/isdn/mISDN/l1oip_core.c        |   3 +-
 include/linux/mISDNif.h                |   8 +-
 5 files changed, 273 insertions(+), 115 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index cd3f3994b7f8..97f4708b3879 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -4718,7 +4718,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m)
 	} else
 		hc->chan[hc->dslot].jitter = 2; /* default */
 	snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1);
-	ret = mISDN_register_device(&dch->dev, name);
+	ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name);
 	if (ret)
 		goto free_chan;
 	hc->created[0] = 1;
@@ -4826,9 +4826,9 @@ init_multi_port(struct hfc_multi *hc, int pt)
 		test_and_set_bit(HFC_CFG_DIS_ECHANNEL,
 		    &hc->chan[i + 2].cfg);
 	}
-	snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d",
+	snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d",
 		hc->type, HFC_cnt + 1, pt + 1);
-	ret = mISDN_register_device(&dch->dev, name);
+	ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name);
 	if (ret)
 		goto free_chan;
 	hc->created[pt] = 1;
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index cd5d26c9909f..8df12bf02af3 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -64,9 +64,6 @@ MODULE_LICENSE("GPL");
 module_param(debug, uint, 0);
 module_param(poll, uint, S_IRUGO | S_IWUSR);
 
-static LIST_HEAD(HFClist);
-static DEFINE_RWLOCK(HFClock);
-
 enum {
 	HFC_CCD_2BD0,
 	HFC_CCD_B000,
@@ -136,7 +133,6 @@ struct hfcPCI_hw {
 
 
 struct hfc_pci {
-	struct list_head	list;
 	u_char			subtype;
 	u_char			chanlimit;
 	u_char			initdone;
@@ -1227,41 +1223,6 @@ hfcpci_int(int intno, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static void
-hfcpci_softirq(void *arg)
-{
-	u_long		flags;
-	struct bchannel	*bch;
-	struct hfc_pci	*hc;
-
-	write_lock_irqsave(&HFClock, flags);
-	list_for_each_entry(hc, &HFClist, list) {
-		if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) {
-			spin_lock(&hc->lock);
-			bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
-			if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */
-				main_rec_hfcpci(bch);
-				tx_birq(bch);
-			}
-			bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2);
-			if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */
-				main_rec_hfcpci(bch);
-				tx_birq(bch);
-			}
-			spin_unlock(&hc->lock);
-		}
-	}
-	write_unlock_irqrestore(&HFClock, flags);
-
-	/* if next event would be in the past ... */
-	if ((s32)(hfc_jiffies + tics - jiffies) <= 0)
-		hfc_jiffies = jiffies + 1;
-	else
-		hfc_jiffies += tics;
-	hfc_tl.expires = hfc_jiffies;
-	add_timer(&hfc_tl);
-}
-
 /*
  * timer callback for D-chan busy resolution. Currently no function
  */
@@ -2131,7 +2092,6 @@ release_card(struct hfc_pci *hc) {
 	mISDN_freebchannel(&hc->bch[1]);
 	mISDN_freebchannel(&hc->bch[0]);
 	mISDN_freedchannel(&hc->dch);
-	list_del(&hc->list);
 	pci_set_drvdata(hc->pdev, NULL);
 	kfree(hc);
 }
@@ -2141,7 +2101,6 @@ setup_card(struct hfc_pci *card)
 {
 	int		err = -EINVAL;
 	u_int		i;
-	u_long		flags;
 	char		name[MISDN_MAX_IDLEN];
 
 	card->dch.debug = debug;
@@ -2169,13 +2128,10 @@ setup_card(struct hfc_pci *card)
 	if (err)
 		goto error;
 	snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1);
-	err = mISDN_register_device(&card->dch.dev, name);
+	err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name);
 	if (err)
 		goto error;
 	HFC_cnt++;
-	write_lock_irqsave(&HFClock, flags);
-	list_add_tail(&card->list, &HFClist);
-	write_unlock_irqrestore(&HFClock, flags);
 	printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt);
 	return 0;
 error:
@@ -2311,15 +2267,12 @@ static void __devexit
 hfc_remove_pci(struct pci_dev *pdev)
 {
 	struct hfc_pci	*card = pci_get_drvdata(pdev);
-	u_long		flags;
 
-	if (card) {
-		write_lock_irqsave(&HFClock, flags);
+	if (card)
 		release_card(card);
-		write_unlock_irqrestore(&HFClock, flags);
-	} else
+	else
 		if (debug)
-			printk(KERN_WARNING "%s: drvdata allready removed\n",
+			printk(KERN_WARNING "%s: drvdata already removed\n",
 			    __func__);
 }
 
@@ -2331,6 +2284,46 @@ static struct pci_driver hfc_driver = {
 	.id_table = hfc_ids,
 };
 
+static int
+_hfcpci_softirq(struct device *dev, void *arg)
+{
+	struct hfc_pci  *hc = dev_get_drvdata(dev);
+	struct bchannel *bch;
+	if (hc == NULL)
+		return 0;
+
+	if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) {
+		spin_lock(&hc->lock);
+		bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+		if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */
+			main_rec_hfcpci(bch);
+			tx_birq(bch);
+		}
+		bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2);
+		if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */
+			main_rec_hfcpci(bch);
+			tx_birq(bch);
+		}
+		spin_unlock(&hc->lock);
+	}
+	return 0;
+}
+
+static void
+hfcpci_softirq(void *arg)
+{
+	(void) driver_for_each_device(&hfc_driver.driver, NULL, arg,
+					_hfcpci_softirq);
+
+	/* if next event would be in the past ... */
+	if ((s32)(hfc_jiffies + tics - jiffies) <= 0)
+		hfc_jiffies = jiffies + 1;
+	else
+		hfc_jiffies += tics;
+	hfc_tl.expires = hfc_jiffies;
+	add_timer(&hfc_tl);
+}
+
 static int __init
 HFC_init(void)
 {
@@ -2375,14 +2368,9 @@ HFC_init(void)
 static void __exit
 HFC_cleanup(void)
 {
-	struct hfc_pci	*card, *next;
-
 	if (timer_pending(&hfc_tl))
 		del_timer(&hfc_tl);
 
-	list_for_each_entry_safe(card, next, &HFClist, list) {
-		release_card(card);
-	}
 	pci_unregister_driver(&hfc_driver);
 }
 
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
index 9116f54def2c..9426c9827e47 100644
--- a/drivers/isdn/mISDN/core.c
+++ b/drivers/isdn/mISDN/core.c
@@ -25,39 +25,183 @@ MODULE_AUTHOR("Karsten Keil");
 MODULE_LICENSE("GPL");
 module_param(debug, uint, S_IRUGO | S_IWUSR);
 
-static LIST_HEAD(devices);
-static DEFINE_RWLOCK(device_lock);
 static u64		device_ids;
 #define MAX_DEVICE_ID	63
 
 static LIST_HEAD(Bprotocols);
 static DEFINE_RWLOCK(bp_lock);
 
+static void mISDN_dev_release(struct device *dev)
+{
+	/* nothing to do: the device is part of its parent's data structure */
+}
+
+static ssize_t _show_id(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+
+	if (!mdev)
+		return -ENODEV;
+	return sprintf(buf, "%d\n", mdev->id);
+}
+
+static ssize_t _show_nrbchan(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+
+	if (!mdev)
+		return -ENODEV;
+	return sprintf(buf, "%d\n", mdev->nrbchan);
+}
+
+static ssize_t _show_d_protocols(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+
+	if (!mdev)
+		return -ENODEV;
+	return sprintf(buf, "%d\n", mdev->Dprotocols);
+}
+
+static ssize_t _show_b_protocols(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+
+	if (!mdev)
+		return -ENODEV;
+	return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
+}
+
+static ssize_t _show_protocol(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+
+	if (!mdev)
+		return -ENODEV;
+	return sprintf(buf, "%d\n", mdev->D.protocol);
+}
+
+static ssize_t _show_name(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	strcpy(buf, dev_name(dev));
+	return strlen(buf);
+}
+
+#if 0 /* hangs */
+static ssize_t _set_name(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int err = 0;
+	char *out = kmalloc(count + 1, GFP_KERNEL);
+
+	if (!out)
+		return -ENOMEM;
+
+	memcpy(out, buf, count);
+	if (count && out[count - 1] == '\n')
+		out[--count] = 0;
+	if (count)
+		err = device_rename(dev, out);
+	kfree(out);
+
+	return (err < 0) ? err : count;
+}
+#endif
+
+static ssize_t _show_channelmap(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+	char *bp = buf;
+	int i;
+
+	for (i = 0; i <= mdev->nrbchan; i++)
+		*bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
+
+	return bp - buf;
+}
+
+static struct device_attribute mISDN_dev_attrs[] = {
+	__ATTR(id,          S_IRUGO,         _show_id,          NULL),
+	__ATTR(d_protocols, S_IRUGO,         _show_d_protocols, NULL),
+	__ATTR(b_protocols, S_IRUGO,         _show_b_protocols, NULL),
+	__ATTR(protocol,    S_IRUGO,         _show_protocol,    NULL),
+	__ATTR(channelmap,  S_IRUGO,         _show_channelmap,  NULL),
+	__ATTR(nrbchan,     S_IRUGO,         _show_nrbchan,     NULL),
+	__ATTR(name,        S_IRUGO,         _show_name,        NULL),
+/*	__ATTR(name,        S_IRUGO|S_IWUSR, _show_name,       _set_name), */
+	{}
+};
+
+#ifdef CONFIG_HOTPLUG
+static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+
+	if (!mdev)
+		return 0;
+
+	if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
+		return -ENOMEM;
+
+	return 0;
+}
+#endif
+
+static void mISDN_class_release(struct class *cls)
+{
+	/* do nothing, it's static */
+}
+
+static struct class mISDN_class = {
+	.name = "mISDN",
+	.owner = THIS_MODULE,
+#ifdef CONFIG_HOTPLUG
+	.dev_uevent = mISDN_uevent,
+#endif
+	.dev_attrs = mISDN_dev_attrs,
+	.dev_release = mISDN_dev_release,
+	.class_release = mISDN_class_release,
+};
+
+static int
+_get_mdevice(struct device *dev, void *id)
+{
+	struct mISDNdevice *mdev = dev_to_mISDN(dev);
+
+	if (!mdev)
+		return 0;
+	if (mdev->id != *(u_int *)id)
+		return 0;
+	return 1;
+}
+
 struct mISDNdevice
 *get_mdevice(u_int id)
 {
-	struct mISDNdevice	*dev;
+	return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
+		_get_mdevice));
+}
 
-	read_lock(&device_lock);
-	list_for_each_entry(dev, &devices, D.list)
-		if (dev->id == id) {
-			read_unlock(&device_lock);
-			return dev;
-		}
-	read_unlock(&device_lock);
-	return NULL;
+static int
+_get_mdevice_count(struct device *dev, void *cnt)
+{
+	*(int *)cnt += 1;
+	return 0;
 }
 
 int
 get_mdevice_count(void)
 {
-	struct mISDNdevice	*dev;
-	int			cnt = 0;
+	int cnt = 0;
 
-	read_lock(&device_lock);
-	list_for_each_entry(dev, &devices, D.list)
-		cnt++;
-	read_unlock(&device_lock);
+	class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
 	return cnt;
 }
 
@@ -68,19 +212,24 @@ get_free_devid(void)
 
 	for (i = 0; i <= MAX_DEVICE_ID; i++)
 		if (!test_and_set_bit(i, (u_long *)&device_ids))
-			return i;
-	return -1;
+			break;
+	if (i > MAX_DEVICE_ID)
+		return -1;
+	return i;
 }
 
 int
-mISDN_register_device(struct mISDNdevice *dev, char *name)
+mISDN_register_device(struct mISDNdevice *dev,
+			struct device *parent, char *name)
 {
-	u_long	flags;
 	int	err;
 
 	dev->id = get_free_devid();
+	err = -EBUSY;
 	if (dev->id < 0)
-		return -EBUSY;
+		goto error1;
+
+	device_initialize(&dev->dev);
 	if (name && name[0])
 		dev_set_name(&dev->dev, "%s", name);
 	else
@@ -90,26 +239,39 @@ mISDN_register_device(struct mISDNdevice *dev, char *name)
 			dev_name(&dev->dev), dev->id);
 	err = create_stack(dev);
 	if (err)
-		return err;
-	write_lock_irqsave(&device_lock, flags);
-	list_add_tail(&dev->D.list, &devices);
-	write_unlock_irqrestore(&device_lock, flags);
+		goto error1;
+
+	dev->dev.class = &mISDN_class;
+	dev->dev.platform_data = dev;
+	dev->dev.parent = parent;
+	dev_set_drvdata(&dev->dev, dev);
+
+	err = device_add(&dev->dev);
+	if (err)
+		goto error3;
 	return 0;
+
+error3:
+	delete_stack(dev);
+	return err;
+error1:
+	return err;
+
 }
 EXPORT_SYMBOL(mISDN_register_device);
 
 void
 mISDN_unregister_device(struct mISDNdevice *dev) {
-	u_long	flags;
-
 	if (debug & DEBUG_CORE)
 		printk(KERN_DEBUG "mISDN_unregister %s %d\n",
 			dev_name(&dev->dev), dev->id);
-	write_lock_irqsave(&device_lock, flags);
-	list_del(&dev->D.list);
-	write_unlock_irqrestore(&device_lock, flags);
+	/* sysfs_remove_link(&dev->dev.kobj, "device"); */
+	device_del(&dev->dev);
+	dev_set_drvdata(&dev->dev, NULL);
+
 	test_and_clear_bit(dev->id, (u_long *)&device_ids);
 	delete_stack(dev);
+	put_device(&dev->dev);
 }
 EXPORT_SYMBOL(mISDN_unregister_device);
 
@@ -201,42 +363,43 @@ mISDNInit(void)
 		MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
 	mISDN_init_clock(&debug);
 	mISDN_initstack(&debug);
+	err = class_register(&mISDN_class);
+	if (err)
+		goto error1;
 	err = mISDN_inittimer(&debug);
 	if (err)
-		goto error;
+		goto error2;
 	err = l1_init(&debug);
-	if (err) {
-		mISDN_timer_cleanup();
-		goto error;
-	}
+	if (err)
+		goto error3;
 	err = Isdnl2_Init(&debug);
-	if (err) {
-		mISDN_timer_cleanup();
-		l1_cleanup();
-		goto error;
-	}
+	if (err)
+		goto error4;
 	err = misdn_sock_init(&debug);
-	if (err) {
-		mISDN_timer_cleanup();
-		l1_cleanup();
-		Isdnl2_cleanup();
-	}
-error:
+	if (err)
+		goto error5;
+	return 0;
+
+error5:
+	Isdnl2_cleanup();
+error4:
+	l1_cleanup();
+error3:
+	mISDN_timer_cleanup();
+error2:
+	class_unregister(&mISDN_class);
+error1:
 	return err;
 }
 
 static void mISDN_cleanup(void)
 {
 	misdn_sock_cleanup();
-	mISDN_timer_cleanup();
-	l1_cleanup();
 	Isdnl2_cleanup();
+	l1_cleanup();
+	mISDN_timer_cleanup();
+	class_unregister(&mISDN_class);
 
-	if (!list_empty(&devices))
-		printk(KERN_ERR "%s devices still registered\n", __func__);
-
-	if (!list_empty(&Bprotocols))
-		printk(KERN_ERR "%s Bprotocols still registered\n", __func__);
 	printk(KERN_DEBUG "mISDNcore unloaded\n");
 }
 
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index a6a9e1af2ad6..abe574989572 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -1433,7 +1433,8 @@ init_card(struct l1oip *hc, int pri, int bundle)
 		hc->chan[i + ch].bch = bch;
 		set_channelmap(bch->nr, dch->dev.channelmap);
 	}
-	ret = mISDN_register_device(&dch->dev, hc->name);
+	/* TODO: create a parent device for this driver */
+	ret = mISDN_register_device(&dch->dev, NULL, hc->name);
 	if (ret)
 		return ret;
 	hc->registered = 1;
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index 3f9988849f32..d4229aebf648 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -531,7 +531,8 @@ _queue_data(struct mISDNchannel *ch, u_int prim,
 
 /* global register/unregister functions */
 
-extern int	mISDN_register_device(struct mISDNdevice *, char *name);
+extern int	mISDN_register_device(struct mISDNdevice *,
+					struct device *parent, char *name);
 extern void	mISDN_unregister_device(struct mISDNdevice *);
 extern int	mISDN_register_Bprotocol(struct Bprotocol *);
 extern void	mISDN_unregister_Bprotocol(struct Bprotocol *);
@@ -539,6 +540,11 @@ extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *,
 						void *);
 extern void	mISDN_unregister_clock(struct mISDNclock *);
 
+static inline struct mISDNdevice *dev_to_mISDN(struct device *dev)
+{
+	return dev_get_drvdata(dev);
+}
+
 extern void	set_channel_address(struct mISDNchannel *, u_int, u_int);
 extern void	mISDN_clock_update(struct mISDNclock *, int, struct timeval *);
 extern unsigned short mISDN_clock_get(void);
-- 
cgit v1.2.3


From 400fd9783e7374bdf73b9cee9d7b90e35e3007da Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sat, 11 Oct 2008 08:13:29 +0200
Subject: mISDN: Minor cleanups

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcpci.c | 4 ++--
 drivers/isdn/mISDN/dsp_core.c        | 2 +-
 drivers/isdn/mISDN/dsp_pipeline.c    | 6 +++---
 3 files changed, 6 insertions(+), 6 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index 8df12bf02af3..2db06490b1ac 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -2333,10 +2333,10 @@ HFC_init(void)
 		poll = HFCPCI_BTRANS_THRESHOLD;
 
 	if (poll != HFCPCI_BTRANS_THRESHOLD) {
-		tics = poll * HZ / 8000;
+		tics = (poll * HZ) / 8000;
 		if (tics < 1)
 			tics = 1;
-		poll = tics * 8000 / HZ;
+		poll = (tics * 8000) / HZ;
 		if (poll > 256 || poll < 8) {
 			printk(KERN_ERR "%s: Wrong poll value %d not in range "
 				"of 8..256.\n", __func__, poll);
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index c16cb7ac3d2a..7e60cb94b5c0 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -1141,7 +1141,7 @@ static int dsp_init(void)
 	} else {
 		poll = 8;
 		while (poll <= MAX_POLL) {
-			tics = poll * HZ / 8000;
+			tics = (poll * HZ) / 8000;
 			if (tics * 8000 == poll * HZ) {
 				dsp_tics = tics;
 				dsp_poll = poll;
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
index 7a3a3e1e4d80..bf999bdc41c3 100644
--- a/drivers/isdn/mISDN/dsp_pipeline.c
+++ b/drivers/isdn/mISDN/dsp_pipeline.c
@@ -92,7 +92,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
 	if (!elem)
 		return -EINVAL;
 
-	entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL);
+	entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC);
 	if (!entry)
 		return -ENOMEM;
 
@@ -253,7 +253,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 	if (!len)
 		return 0;
 
-	dup = kmalloc(len + 1, GFP_KERNEL);
+	dup = kmalloc(len + 1, GFP_ATOMIC);
 	if (!dup)
 		return 0;
 	strcpy(dup, cfg);
@@ -270,7 +270,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
 				elem = entry->elem;
 
 				pipeline_entry = kmalloc(sizeof(struct
-					dsp_pipeline_entry), GFP_KERNEL);
+					dsp_pipeline_entry), GFP_ATOMIC);
 				if (!pipeline_entry) {
 					printk(KERN_ERR "%s: failed to add "
 					    "entry to pipeline: %s (out of "
-- 
cgit v1.2.3


From e314f89a62c1d74380455690325b1336ea0dca9d Mon Sep 17 00:00:00 2001
From: Matthias Urlichs <smurf@smurf.noris.de>
Date: Thu, 16 Oct 2008 13:58:54 +0200
Subject: mISDN: Add MODULE_DEVICE_TABLE() to hfcpci

Add missed table.

Signed-off-by: Matthias Urlichs <smurf@smurf.noris.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcpci.c | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index 2db06490b1ac..019dbe9b082c 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -2376,3 +2376,5 @@ HFC_cleanup(void)
 
 module_init(HFC_init);
 module_exit(HFC_cleanup);
+
+MODULE_DEVICE_TABLE(pci, hfc_ids);
-- 
cgit v1.2.3


From c3b3cdeba5a671ce5d0064c52c8a85f5b1e72e92 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sun, 9 Nov 2008 10:23:19 +0100
Subject: mISDN: Added missing create_l1() call

create_l1() was missed when changing mode to TE.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/hfcpci.c | 5 +++++
 1 file changed, 5 insertions(+)

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index 019dbe9b082c..917bf41a293b 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -1937,6 +1937,11 @@ open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch,
 		if (rq->protocol != ch->protocol) {
 			if (hc->hw.protocol == ISDN_P_TE_S0)
 				l1_event(hc->dch.l1, CLOSE_CHANNEL);
+			if (rq->protocol == ISDN_P_TE_S0) {
+				err = create_l1(&hc->dch, hfc_l1callback);
+				if (err)
+					return err;
+			}
 			hc->hw.protocol = rq->protocol;
 			ch->protocol = rq->protocol;
 			hfcpci_setmode(hc);
-- 
cgit v1.2.3


From c6a2e587e5b28177eabfc2db4d5abd25b87604f2 Mon Sep 17 00:00:00 2001
From: Andreas Eversberg <andreas@eversberg.eu>
Date: Sun, 28 Dec 2008 16:31:26 +0100
Subject: mISDN: Fix kernel crash when doing hardware conference with more than
 two members

Fix kernel crash when doing hardware conference with more than two members.
Removed DTMF threshold notice when debugging is disabled.

Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/mISDN/dsp_cmx.c  | 7 +++----
 drivers/isdn/mISDN/dsp_core.c | 5 +++--
 2 files changed, 6 insertions(+), 6 deletions(-)

(limited to 'drivers')

diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index 02f643456b16..0ac67bff303a 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -927,10 +927,6 @@ conf_software:
 
 	/* for more than two members.. */
 
-	/* in case of hdlc, we change to software */
-	if (dsp->hdlc)
-		goto conf_software;
-
 	/* if all members already have the same conference */
 	if (all_conf)
 		return;
@@ -941,6 +937,9 @@ conf_software:
 	if (current_conf >= 0) {
 join_members:
 		list_for_each_entry(member, &conf->mlist, list) {
+			/* in case of hdlc, change to software */
+			if (member->dsp->hdlc)
+				goto conf_software;
 			/* join to current conference */
 			if (member->dsp->hfc_conf == current_conf)
 				continue;
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index 7e60cb94b5c0..3083338716b2 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -301,8 +301,9 @@ dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb)
 		if (dsp_debug & DEBUG_DSP_CORE)
 			printk(KERN_DEBUG "%s: start dtmf\n", __func__);
 		if (len == sizeof(int)) {
-			printk(KERN_NOTICE "changing DTMF Threshold "
-				"to %d\n", *((int *)data));
+			if (dsp_debug & DEBUG_DSP_CORE)
+				printk(KERN_NOTICE "changing DTMF Threshold "
+					"to %d\n", *((int *)data));
 			dsp->dtmf.treshold = (*(int *)data) * 10000;
 		}
 		/* init goertzel */
-- 
cgit v1.2.3


From 69f52adb2d534afc41fcc658f155e01f0b322f9e Mon Sep 17 00:00:00 2001
From: Karsten Keil <kkeil@suse.de>
Date: Fri, 9 Jan 2009 16:20:51 +0100
Subject: mISDN: Add HFC USB driver

Enable support for USB ISDN TAs with Cologne Chip AG's
HFC-S USB ISDN Controller.

Signed-off-by: Martin Bachem <m.bachem@gmx.de>
Signed-off-by: Karsten Keil <kkeil@suse.de>
---
 drivers/isdn/hardware/mISDN/Kconfig   |    7 +
 drivers/isdn/hardware/mISDN/Makefile  |    1 +
 drivers/isdn/hardware/mISDN/hfcsusb.c | 2196 +++++++++++++++++++++++++++++++++
 drivers/isdn/hardware/mISDN/hfcsusb.h |  418 +++++++
 4 files changed, 2622 insertions(+)
 create mode 100644 drivers/isdn/hardware/mISDN/hfcsusb.c
 create mode 100644 drivers/isdn/hardware/mISDN/hfcsusb.h

(limited to 'drivers')

diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
index 14793480c453..fd112ae252cf 100644
--- a/drivers/isdn/hardware/mISDN/Kconfig
+++ b/drivers/isdn/hardware/mISDN/Kconfig
@@ -23,3 +23,10 @@ config MISDN_HFCMULTI
 	   * HFC-8S (8 S/T interfaces on one chip)
 	   * HFC-E1 (E1 interface for 2Mbit ISDN)
 
+config MISDN_HFCUSB
+	tristate "Support for HFC-S USB based TAs"
+	depends on USB
+	help
+	  Enable support for USB ISDN TAs with Cologne Chip AG's
+	  HFC-S USB ISDN Controller
+
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
index 1e7ca5332ad7..b0403526bbba 100644
--- a/drivers/isdn/hardware/mISDN/Makefile
+++ b/drivers/isdn/hardware/mISDN/Makefile
@@ -5,3 +5,4 @@
 
 obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
 obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
+obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o
diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c
new file mode 100644
index 000000000000..ba6925fbf38a
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfcsusb.c
@@ -0,0 +1,2196 @@
+/* hfcsusb.c
+ * mISDN driver for Colognechip HFC-S USB chip
+ *
+ * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de)
+ * Copyright 2008 by Martin Bachem (info@bachem-it.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * module params
+ *   debug=<n>, default=0, with n=0xHHHHGGGG
+ *      H - l1 driver flags described in hfcsusb.h
+ *      G - common mISDN debug flags described at mISDNhw.h
+ *
+ *   poll=<n>, default 128
+ *     n : burst size of PH_DATA_IND at transparent rx data
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/mISDNhw.h>
+#include "hfcsusb.h"
+
+const char *hfcsusb_rev = "Revision: 0.3.3 (socket), 2008-11-05";
+
+static unsigned int debug;
+static int poll = DEFAULT_TRANSP_BURST_SZ;
+
+static LIST_HEAD(HFClist);
+static DEFINE_RWLOCK(HFClock);
+
+
+MODULE_AUTHOR("Martin Bachem");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(poll, int, 0);
+
+static int hfcsusb_cnt;
+
+/* some function prototypes */
+static void hfcsusb_ph_command(struct hfcsusb *hw, u_char command);
+static void release_hw(struct hfcsusb *hw);
+static void reset_hfcsusb(struct hfcsusb *hw);
+static void setPortMode(struct hfcsusb *hw);
+static void hfcsusb_start_endpoint(struct hfcsusb *hw, int channel);
+static void hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel);
+static int  hfcsusb_setup_bch(struct bchannel *bch, int protocol);
+static void deactivate_bchannel(struct bchannel *bch);
+static void hfcsusb_ph_info(struct hfcsusb *hw);
+
+/* start next background transfer for control channel */
+static void
+ctrl_start_transfer(struct hfcsusb *hw)
+{
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
+
+	if (hw->ctrl_cnt) {
+		hw->ctrl_urb->pipe = hw->ctrl_out_pipe;
+		hw->ctrl_urb->setup_packet = (u_char *)&hw->ctrl_write;
+		hw->ctrl_urb->transfer_buffer = NULL;
+		hw->ctrl_urb->transfer_buffer_length = 0;
+		hw->ctrl_write.wIndex =
+		    cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].hfcs_reg);
+		hw->ctrl_write.wValue =
+		    cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].reg_val);
+
+		usb_submit_urb(hw->ctrl_urb, GFP_ATOMIC);
+	}
+}
+
+/*
+ * queue a control transfer request to write HFC-S USB
+ * chip register using CTRL resuest queue
+ */
+static int write_reg(struct hfcsusb *hw, __u8 reg, __u8 val)
+{
+	struct ctrl_buf *buf;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s reg(0x%02x) val(0x%02x)\n",
+			hw->name, __func__, reg, val);
+
+	spin_lock(&hw->ctrl_lock);
+	if (hw->ctrl_cnt >= HFC_CTRL_BUFSIZE)
+		return 1;
+	buf = &hw->ctrl_buff[hw->ctrl_in_idx];
+	buf->hfcs_reg = reg;
+	buf->reg_val = val;
+	if (++hw->ctrl_in_idx >= HFC_CTRL_BUFSIZE)
+		hw->ctrl_in_idx = 0;
+	if (++hw->ctrl_cnt == 1)
+		ctrl_start_transfer(hw);
+	spin_unlock(&hw->ctrl_lock);
+
+	return 0;
+}
+
+/* control completion routine handling background control cmds */
+static void
+ctrl_complete(struct urb *urb)
+{
+	struct hfcsusb *hw = (struct hfcsusb *) urb->context;
+	struct ctrl_buf *buf;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
+
+	urb->dev = hw->dev;
+	if (hw->ctrl_cnt) {
+		buf = &hw->ctrl_buff[hw->ctrl_out_idx];
+		hw->ctrl_cnt--;	/* decrement actual count */
+		if (++hw->ctrl_out_idx >= HFC_CTRL_BUFSIZE)
+			hw->ctrl_out_idx = 0;	/* pointer wrap */
+
+		ctrl_start_transfer(hw); /* start next transfer */
+	}
+}
+
+/* handle LED bits   */
+static void
+set_led_bit(struct hfcsusb *hw, signed short led_bits, int set_on)
+{
+	if (set_on) {
+		if (led_bits < 0)
+			hw->led_state &= ~abs(led_bits);
+		else
+			hw->led_state |= led_bits;
+	} else {
+		if (led_bits < 0)
+			hw->led_state |= abs(led_bits);
+		else
+			hw->led_state &= ~led_bits;
+	}
+}
+
+/* handle LED requests  */
+static void
+handle_led(struct hfcsusb *hw, int event)
+{
+	struct hfcsusb_vdata *driver_info = (struct hfcsusb_vdata *)
+		hfcsusb_idtab[hw->vend_idx].driver_info;
+	__u8 tmpled;
+
+	if (driver_info->led_scheme == LED_OFF)
+		return;
+	tmpled = hw->led_state;
+
+	switch (event) {
+	case LED_POWER_ON:
+		set_led_bit(hw, driver_info->led_bits[0], 1);
+		set_led_bit(hw, driver_info->led_bits[1], 0);
+		set_led_bit(hw, driver_info->led_bits[2], 0);
+		set_led_bit(hw, driver_info->led_bits[3], 0);
+		break;
+	case LED_POWER_OFF:
+		set_led_bit(hw, driver_info->led_bits[0], 0);
+		set_led_bit(hw, driver_info->led_bits[1], 0);
+		set_led_bit(hw, driver_info->led_bits[2], 0);
+		set_led_bit(hw, driver_info->led_bits[3], 0);
+		break;
+	case LED_S0_ON:
+		set_led_bit(hw, driver_info->led_bits[1], 1);
+		break;
+	case LED_S0_OFF:
+		set_led_bit(hw, driver_info->led_bits[1], 0);
+		break;
+	case LED_B1_ON:
+		set_led_bit(hw, driver_info->led_bits[2], 1);
+		break;
+	case LED_B1_OFF:
+		set_led_bit(hw, driver_info->led_bits[2], 0);
+		break;
+	case LED_B2_ON:
+		set_led_bit(hw, driver_info->led_bits[3], 1);
+		break;
+	case LED_B2_OFF:
+		set_led_bit(hw, driver_info->led_bits[3], 0);
+		break;
+	}
+
+	if (hw->led_state != tmpled) {
+		if (debug & DBG_HFC_CALL_TRACE)
+			printk(KERN_DEBUG "%s: %s reg(0x%02x) val(x%02x)\n",
+			    hw->name, __func__,
+			    HFCUSB_P_DATA, hw->led_state);
+
+		write_reg(hw, HFCUSB_P_DATA, hw->led_state);
+	}
+}
+
+/*
+ * Layer2 -> Layer 1 Bchannel data
+ */
+static int
+hfcusb_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel		*bch = container_of(ch, struct bchannel, ch);
+	struct hfcsusb		*hw = bch->hw;
+	int			ret = -EINVAL;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	u_long			flags;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(&hw->lock, flags);
+		ret = bchannel_senddata(bch, skb);
+		spin_unlock_irqrestore(&hw->lock, flags);
+		if (debug & DBG_HFC_CALL_TRACE)
+			printk(KERN_DEBUG "%s: %s PH_DATA_REQ ret(%i)\n",
+				hw->name, __func__, ret);
+		if (ret > 0) {
+			/*
+			 * other l1 drivers don't send early confirms on
+			 * transp data, but hfcsusb does because tx_next
+			 * skb is needed in tx_iso_complete()
+			 */
+			queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL);
+			ret = 0;
+		}
+		return ret;
+	case PH_ACTIVATE_REQ:
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) {
+			hfcsusb_start_endpoint(hw, bch->nr);
+			ret = hfcsusb_setup_bch(bch, ch->protocol);
+		} else
+			ret = 0;
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+				0, NULL, GFP_KERNEL);
+		break;
+	case PH_DEACTIVATE_REQ:
+		deactivate_bchannel(bch);
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY,
+			0, NULL, GFP_KERNEL);
+		ret = 0;
+		break;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+/*
+ * send full D/B channel status information
+ * as MPH_INFORMATION_IND
+ */
+static void
+hfcsusb_ph_info(struct hfcsusb *hw)
+{
+	struct ph_info *phi;
+	struct dchannel *dch = &hw->dch;
+	int i;
+
+	phi = kzalloc(sizeof(struct ph_info) +
+		dch->dev.nrbchan * sizeof(struct ph_info_ch), GFP_ATOMIC);
+	phi->dch.ch.protocol = hw->protocol;
+	phi->dch.ch.Flags = dch->Flags;
+	phi->dch.state = dch->state;
+	phi->dch.num_bch = dch->dev.nrbchan;
+	for (i = 0; i < dch->dev.nrbchan; i++) {
+		phi->bch[i].protocol = hw->bch[i].ch.protocol;
+		phi->bch[i].Flags = hw->bch[i].Flags;
+	}
+	_queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY,
+		sizeof(struct ph_info_dch) + dch->dev.nrbchan *
+		sizeof(struct ph_info_ch), phi, GFP_ATOMIC);
+}
+
+/*
+ * Layer2 -> Layer 1 Dchannel data
+ */
+static int
+hfcusb_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	struct hfcsusb		*hw = dch->hw;
+	int			ret = -EINVAL;
+	u_long			flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		if (debug & DBG_HFC_CALL_TRACE)
+			printk(KERN_DEBUG "%s: %s: PH_DATA_REQ\n",
+				hw->name, __func__);
+
+		spin_lock_irqsave(&hw->lock, flags);
+		ret = dchannel_senddata(dch, skb);
+		spin_unlock_irqrestore(&hw->lock, flags);
+		if (ret > 0) {
+			ret = 0;
+			queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL);
+		}
+		break;
+
+	case PH_ACTIVATE_REQ:
+		if (debug & DBG_HFC_CALL_TRACE)
+			printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n",
+				hw->name, __func__,
+				(hw->protocol == ISDN_P_NT_S0) ? "NT" : "TE");
+
+		if (hw->protocol == ISDN_P_NT_S0) {
+			ret = 0;
+			if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+				_queue_data(&dch->dev.D,
+					PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+					NULL, GFP_ATOMIC);
+			} else {
+				hfcsusb_ph_command(hw,
+					HFC_L1_ACTIVATE_NT);
+				test_and_set_bit(FLG_L2_ACTIVATED,
+					&dch->Flags);
+			}
+		} else {
+			hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE);
+			ret = l1_event(dch->l1, hh->prim);
+		}
+		break;
+
+	case PH_DEACTIVATE_REQ:
+		if (debug & DBG_HFC_CALL_TRACE)
+			printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ\n",
+				hw->name, __func__);
+		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+
+		if (hw->protocol == ISDN_P_NT_S0) {
+			hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT);
+			spin_lock_irqsave(&hw->lock, flags);
+			skb_queue_purge(&dch->squeue);
+			if (dch->tx_skb) {
+				dev_kfree_skb(dch->tx_skb);
+				dch->tx_skb = NULL;
+			}
+			dch->tx_idx = 0;
+			if (dch->rx_skb) {
+				dev_kfree_skb(dch->rx_skb);
+				dch->rx_skb = NULL;
+			}
+			test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+			spin_unlock_irqrestore(&hw->lock, flags);
+#ifdef FIXME
+			if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+				dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+			ret = 0;
+		} else
+			ret = l1_event(dch->l1, hh->prim);
+		break;
+	case MPH_INFORMATION_REQ:
+		hfcsusb_ph_info(hw);
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfc_l1callback(struct dchannel *dch, u_int cmd)
+{
+	struct hfcsusb *hw = dch->hw;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s cmd 0x%x\n",
+			hw->name, __func__, cmd);
+
+	switch (cmd) {
+	case INFO3_P8:
+	case INFO3_P10:
+	case HW_RESET_REQ:
+	case HW_POWERUP_REQ:
+		break;
+
+	case HW_DEACT_REQ:
+		skb_queue_purge(&dch->squeue);
+		if (dch->tx_skb) {
+			dev_kfree_skb(dch->tx_skb);
+			dch->tx_skb = NULL;
+		}
+		dch->tx_idx = 0;
+		if (dch->rx_skb) {
+			dev_kfree_skb(dch->rx_skb);
+			dch->rx_skb = NULL;
+		}
+		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+		break;
+	case PH_ACTIVATE_IND:
+		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	case PH_DEACTIVATE_IND:
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	default:
+		if (dch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: %s: unknown cmd %x\n",
+			hw->name, __func__, cmd);
+		return -1;
+	}
+	hfcsusb_ph_info(hw);
+	return 0;
+}
+
+static int
+open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch,
+    struct channel_req *rq)
+{
+	int err = 0;
+
+	if (debug & DEBUG_HW_OPEN)
+		printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n",
+		    hw->name, __func__, hw->dch.dev.id, rq->adr.channel,
+		    __builtin_return_address(0));
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+
+	test_and_clear_bit(FLG_ACTIVE, &hw->dch.Flags);
+	test_and_clear_bit(FLG_ACTIVE, &hw->ech.Flags);
+	hfcsusb_start_endpoint(hw, HFC_CHAN_D);
+
+	/* E-Channel logging */
+	if (rq->adr.channel == 1) {
+		if (hw->fifos[HFCUSB_PCM_RX].pipe) {
+			hfcsusb_start_endpoint(hw, HFC_CHAN_E);
+			set_bit(FLG_ACTIVE, &hw->ech.Flags);
+			_queue_data(&hw->ech.dev.D, PH_ACTIVATE_IND,
+				     MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+		} else
+			return -EINVAL;
+	}
+
+	if (!hw->initdone) {
+		hw->protocol = rq->protocol;
+		if (rq->protocol == ISDN_P_TE_S0) {
+			err = create_l1(&hw->dch, hfc_l1callback);
+			if (err)
+				return err;
+		}
+		setPortMode(hw);
+		ch->protocol = rq->protocol;
+		hw->initdone = 1;
+	} else {
+		if (rq->protocol != ch->protocol)
+			return -EPROTONOSUPPORT;
+	}
+
+	if (((ch->protocol == ISDN_P_NT_S0) && (hw->dch.state == 3)) ||
+	    ((ch->protocol == ISDN_P_TE_S0) && (hw->dch.state == 7)))
+		_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+		    0, NULL, GFP_KERNEL);
+	rq->ch = ch;
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s: %s: cannot get module\n",
+		    hw->name, __func__);
+	return 0;
+}
+
+static int
+open_bchannel(struct hfcsusb *hw, struct channel_req *rq)
+{
+	struct bchannel		*bch;
+
+	if (rq->adr.channel > 2)
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s B%i\n",
+			hw->name, __func__, rq->adr.channel);
+
+	bch = &hw->bch[rq->adr.channel - 1];
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch;
+
+	/* start USB endpoint for bchannel */
+	if (rq->adr.channel  == 1)
+		hfcsusb_start_endpoint(hw, HFC_CHAN_B1);
+	else
+		hfcsusb_start_endpoint(hw, HFC_CHAN_B2);
+
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s: %s:cannot get module\n",
+		    hw->name, __func__);
+	return 0;
+}
+
+static int
+channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq)
+{
+	int ret = 0;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n",
+		    hw->name, __func__, (cq->op), (cq->channel));
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT |
+			 MISDN_CTRL_DISCONNECT;
+		break;
+	default:
+		printk(KERN_WARNING "%s: %s: unknown Op %x\n",
+			hw->name, __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * device control function
+ */
+static int
+hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct hfcsusb		*hw = dch->hw;
+	struct channel_req	*rq;
+	int			err = 0;
+
+	if (dch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: %s: cmd:%x %p\n",
+		    hw->name, __func__, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		if ((rq->protocol == ISDN_P_TE_S0) ||
+		    (rq->protocol == ISDN_P_NT_S0))
+			err = open_dchannel(hw, ch, rq);
+		else
+			err = open_bchannel(hw, rq);
+		if (!err)
+			hw->open++;
+		break;
+	case CLOSE_CHANNEL:
+		hw->open--;
+		if (debug & DEBUG_HW_OPEN)
+			printk(KERN_DEBUG
+				"%s: %s: dev(%d) close from %p (open %d)\n",
+				hw->name, __func__, hw->dch.dev.id,
+				__builtin_return_address(0), hw->open);
+		if (!hw->open) {
+			hfcsusb_stop_endpoint(hw, HFC_CHAN_D);
+			if (hw->fifos[HFCUSB_PCM_RX].pipe)
+				hfcsusb_stop_endpoint(hw, HFC_CHAN_E);
+			handle_led(hw, LED_POWER_ON);
+		}
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_ctrl(hw, arg);
+		break;
+	default:
+		if (dch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: %s: unknown command %x\n",
+				hw->name, __func__, cmd);
+		return -EINVAL;
+	}
+	return err;
+}
+
+/*
+ * S0 TE state change event handler
+ */
+static void
+ph_state_te(struct dchannel *dch)
+{
+	struct hfcsusb *hw = dch->hw;
+
+	if (debug & DEBUG_HW) {
+		if (dch->state <= HFC_MAX_TE_LAYER1_STATE)
+			printk(KERN_DEBUG "%s: %s: %s\n", hw->name, __func__,
+			    HFC_TE_LAYER1_STATES[dch->state]);
+		else
+			printk(KERN_DEBUG "%s: %s: TE F%d\n",
+			    hw->name, __func__, dch->state);
+	}
+
+	switch (dch->state) {
+	case 0:
+		l1_event(dch->l1, HW_RESET_IND);
+		break;
+	case 3:
+		l1_event(dch->l1, HW_DEACT_IND);
+		break;
+	case 5:
+	case 8:
+		l1_event(dch->l1, ANYSIGNAL);
+		break;
+	case 6:
+		l1_event(dch->l1, INFO2);
+		break;
+	case 7:
+		l1_event(dch->l1, INFO4_P8);
+		break;
+	}
+	if (dch->state == 7)
+		handle_led(hw, LED_S0_ON);
+	else
+		handle_led(hw, LED_S0_OFF);
+}
+
+/*
+ * S0 NT state change event handler
+ */
+static void
+ph_state_nt(struct dchannel *dch)
+{
+	struct hfcsusb *hw = dch->hw;
+
+	if (debug & DEBUG_HW) {
+		if (dch->state <= HFC_MAX_NT_LAYER1_STATE)
+			printk(KERN_DEBUG "%s: %s: %s\n",
+			    hw->name, __func__,
+			    HFC_NT_LAYER1_STATES[dch->state]);
+
+		else
+			printk(KERN_INFO DRIVER_NAME "%s: %s: NT G%d\n",
+			    hw->name, __func__, dch->state);
+	}
+
+	switch (dch->state) {
+	case (1):
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+		hw->nt_timer = 0;
+		hw->timers &= ~NT_ACTIVATION_TIMER;
+		handle_led(hw, LED_S0_OFF);
+		break;
+
+	case (2):
+		if (hw->nt_timer < 0) {
+			hw->nt_timer = 0;
+			hw->timers &= ~NT_ACTIVATION_TIMER;
+			hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT);
+		} else {
+			hw->timers |= NT_ACTIVATION_TIMER;
+			hw->nt_timer = NT_T1_COUNT;
+			/* allow G2 -> G3 transition */
+			write_reg(hw, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3);
+		}
+		break;
+	case (3):
+		hw->nt_timer = 0;
+		hw->timers &= ~NT_ACTIVATION_TIMER;
+		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+			MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+		handle_led(hw, LED_S0_ON);
+		break;
+	case (4):
+		hw->nt_timer = 0;
+		hw->timers &= ~NT_ACTIVATION_TIMER;
+		break;
+	default:
+		break;
+	}
+	hfcsusb_ph_info(hw);
+}
+
+static void
+ph_state(struct dchannel *dch)
+{
+	struct hfcsusb *hw = dch->hw;
+
+	if (hw->protocol == ISDN_P_NT_S0)
+		ph_state_nt(dch);
+	else if (hw->protocol == ISDN_P_TE_S0)
+		ph_state_te(dch);
+}
+
+/*
+ * disable/enable BChannel for desired protocoll
+ */
+static int
+hfcsusb_setup_bch(struct bchannel *bch, int protocol)
+{
+	struct hfcsusb *hw = bch->hw;
+	__u8 conhdlc, sctrl, sctrl_r;
+
+	if (debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n",
+		    hw->name, __func__, bch->state, protocol,
+		    bch->nr);
+
+	/* setup val for CON_HDLC */
+	conhdlc = 0;
+	if (protocol > ISDN_P_NONE)
+		conhdlc = 8;	/* enable FIFO */
+
+	switch (protocol) {
+	case (-1):	/* used for init */
+		bch->state = -1;
+		/* fall trough */
+	case (ISDN_P_NONE):
+		if (bch->state == ISDN_P_NONE)
+			return 0; /* already in idle state */
+		bch->state = ISDN_P_NONE;
+		clear_bit(FLG_HDLC, &bch->Flags);
+		clear_bit(FLG_TRANSPARENT, &bch->Flags);
+		break;
+	case (ISDN_P_B_RAW):
+		conhdlc |= 2;
+		bch->state = protocol;
+		set_bit(FLG_TRANSPARENT, &bch->Flags);
+		break;
+	case (ISDN_P_B_HDLC):
+		bch->state = protocol;
+		set_bit(FLG_HDLC, &bch->Flags);
+		break;
+	default:
+		if (debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: %s: prot not known %x\n",
+				hw->name, __func__, protocol);
+		return -ENOPROTOOPT;
+	}
+
+	if (protocol >= ISDN_P_NONE) {
+		write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 0 : 2);
+		write_reg(hw, HFCUSB_CON_HDLC, conhdlc);
+		write_reg(hw, HFCUSB_INC_RES_F, 2);
+		write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 1 : 3);
+		write_reg(hw, HFCUSB_CON_HDLC, conhdlc);
+		write_reg(hw, HFCUSB_INC_RES_F, 2);
+
+		sctrl = 0x40 + ((hw->protocol == ISDN_P_TE_S0) ? 0x00 : 0x04);
+		sctrl_r = 0x0;
+		if (test_bit(FLG_ACTIVE, &hw->bch[0].Flags)) {
+			sctrl |= 1;
+			sctrl_r |= 1;
+		}
+		if (test_bit(FLG_ACTIVE, &hw->bch[1].Flags)) {
+			sctrl |= 2;
+			sctrl_r |= 2;
+		}
+		write_reg(hw, HFCUSB_SCTRL, sctrl);
+		write_reg(hw, HFCUSB_SCTRL_R, sctrl_r);
+
+		if (protocol > ISDN_P_NONE)
+			handle_led(hw, (bch->nr == 1) ? LED_B1_ON : LED_B2_ON);
+		else
+			handle_led(hw, (bch->nr == 1) ? LED_B1_OFF :
+				LED_B2_OFF);
+	}
+	hfcsusb_ph_info(hw);
+	return 0;
+}
+
+static void
+hfcsusb_ph_command(struct hfcsusb *hw, u_char command)
+{
+	if (debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: %s: %x\n",
+		   hw->name, __func__, command);
+
+	switch (command) {
+	case HFC_L1_ACTIVATE_TE:
+		/* force sending sending INFO1 */
+		write_reg(hw, HFCUSB_STATES, 0x14);
+		/* start l1 activation */
+		write_reg(hw, HFCUSB_STATES, 0x04);
+		break;
+
+	case HFC_L1_FORCE_DEACTIVATE_TE:
+		write_reg(hw, HFCUSB_STATES, 0x10);
+		write_reg(hw, HFCUSB_STATES, 0x03);
+		break;
+
+	case HFC_L1_ACTIVATE_NT:
+		if (hw->dch.state == 3)
+			_queue_data(&hw->dch.dev.D, PH_ACTIVATE_IND,
+				MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+		else
+			write_reg(hw, HFCUSB_STATES, HFCUSB_ACTIVATE |
+				HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3);
+		break;
+
+	case HFC_L1_DEACTIVATE_NT:
+		write_reg(hw, HFCUSB_STATES,
+			HFCUSB_DO_ACTION);
+		break;
+	}
+}
+
+/*
+ * Layer 1 B-channel hardware access
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_FILL_EMPTY;
+		break;
+	case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */
+		test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
+		if (debug & DEBUG_HW_OPEN)
+			printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d "
+				"off=%d)\n", __func__, bch->nr, !!cq->p1);
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+/* collect data from incoming interrupt or isochron USB data */
+static void
+hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
+	int finish)
+{
+	struct hfcsusb	*hw = fifo->hw;
+	struct sk_buff	*rx_skb = NULL;
+	int		maxlen = 0;
+	int		fifon = fifo->fifonum;
+	int		i;
+	int		hdlc = 0;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) "
+		    "dch(%p) bch(%p) ech(%p)\n",
+		    hw->name, __func__, fifon, len,
+		    fifo->dch, fifo->bch, fifo->ech);
+
+	if (!len)
+		return;
+
+	if ((!!fifo->dch + !!fifo->bch + !!fifo->ech) != 1) {
+		printk(KERN_DEBUG "%s: %s: undefined channel\n",
+		       hw->name, __func__);
+		return;
+	}
+
+	spin_lock(&hw->lock);
+	if (fifo->dch) {
+		rx_skb = fifo->dch->rx_skb;
+		maxlen = fifo->dch->maxlen;
+		hdlc = 1;
+	}
+	if (fifo->bch) {
+		rx_skb = fifo->bch->rx_skb;
+		maxlen = fifo->bch->maxlen;
+		hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags);
+	}
+	if (fifo->ech) {
+		rx_skb = fifo->ech->rx_skb;
+		maxlen = fifo->ech->maxlen;
+		hdlc = 1;
+	}
+
+	if (!rx_skb) {
+		rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC);
+		if (rx_skb) {
+			if (fifo->dch)
+				fifo->dch->rx_skb = rx_skb;
+			if (fifo->bch)
+				fifo->bch->rx_skb = rx_skb;
+			if (fifo->ech)
+				fifo->ech->rx_skb = rx_skb;
+			skb_trim(rx_skb, 0);
+		} else {
+			printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n",
+			    hw->name, __func__);
+			spin_unlock(&hw->lock);
+			return;
+		}
+	}
+
+	if (fifo->dch || fifo->ech) {
+		/* D/E-Channel SKB range check */
+		if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) {
+			printk(KERN_DEBUG "%s: %s: sbk mem exceeded "
+			    "for fifo(%d) HFCUSB_D_RX\n",
+			    hw->name, __func__, fifon);
+			skb_trim(rx_skb, 0);
+			spin_unlock(&hw->lock);
+			return;
+		}
+	} else if (fifo->bch) {
+		/* B-Channel SKB range check */
+		if ((rx_skb->len + len) >= (MAX_BCH_SIZE + 3)) {
+			printk(KERN_DEBUG "%s: %s: sbk mem exceeded "
+			    "for fifo(%d) HFCUSB_B_RX\n",
+			    hw->name, __func__, fifon);
+			skb_trim(rx_skb, 0);
+			spin_unlock(&hw->lock);
+			return;
+		}
+	}
+
+	memcpy(skb_put(rx_skb, len), data, len);
+
+	if (hdlc) {
+		/* we have a complete hdlc packet */
+		if (finish) {
+			if ((rx_skb->len > 3) &&
+			   (!(rx_skb->data[rx_skb->len - 1]))) {
+				if (debug & DBG_HFC_FIFO_VERBOSE) {
+					printk(KERN_DEBUG "%s: %s: fifon(%i)"
+					    " new RX len(%i): ",
+					    hw->name, __func__, fifon,
+					    rx_skb->len);
+					i = 0;
+					while (i < rx_skb->len)
+						printk("%02x ",
+						    rx_skb->data[i++]);
+					printk("\n");
+				}
+
+				/* remove CRC & status */
+				skb_trim(rx_skb, rx_skb->len - 3);
+
+				if (fifo->dch)
+					recv_Dchannel(fifo->dch);
+				if (fifo->bch)
+					recv_Bchannel(fifo->bch);
+				if (fifo->ech)
+					recv_Echannel(fifo->ech,
+						     &hw->dch);
+			} else {
+				if (debug & DBG_HFC_FIFO_VERBOSE) {
+					printk(KERN_DEBUG
+					    "%s: CRC or minlen ERROR fifon(%i) "
+					    "RX len(%i): ",
+					    hw->name, fifon, rx_skb->len);
+					i = 0;
+					while (i < rx_skb->len)
+						printk("%02x ",
+						    rx_skb->data[i++]);
+					printk("\n");
+				}
+				skb_trim(rx_skb, 0);
+			}
+		}
+	} else {
+		/* deliver transparent data to layer2 */
+		if (rx_skb->len >= poll)
+			recv_Bchannel(fifo->bch);
+	}
+	spin_unlock(&hw->lock);
+}
+
+void
+fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,
+	      void *buf, int num_packets, int packet_size, int interval,
+	      usb_complete_t complete, void *context)
+{
+	int k;
+
+	usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets,
+	    complete, context);
+
+	urb->number_of_packets = num_packets;
+	urb->transfer_flags = URB_ISO_ASAP;
+	urb->actual_length = 0;
+	urb->interval = interval;
+
+	for (k = 0; k < num_packets; k++) {
+		urb->iso_frame_desc[k].offset = packet_size * k;
+		urb->iso_frame_desc[k].length = packet_size;
+		urb->iso_frame_desc[k].actual_length = 0;
+	}
+}
+
+/* receive completion routine for all ISO tx fifos   */
+static void
+rx_iso_complete(struct urb *urb)
+{
+	struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context;
+	struct usb_fifo *fifo = context_iso_urb->owner_fifo;
+	struct hfcsusb *hw = fifo->hw;
+	int k, len, errcode, offset, num_isoc_packets, fifon, maxlen,
+	    status, iso_status, i;
+	__u8 *buf;
+	static __u8 eof[8];
+	__u8 s0_state;
+
+	fifon = fifo->fifonum;
+	status = urb->status;
+
+	spin_lock(&hw->lock);
+	if (fifo->stop_gracefull) {
+		fifo->stop_gracefull = 0;
+		fifo->active = 0;
+		spin_unlock(&hw->lock);
+		return;
+	}
+	spin_unlock(&hw->lock);
+
+	/*
+	 * ISO transfer only partially completed,
+	 * look at individual frame status for details
+	 */
+	if (status == -EXDEV) {
+		if (debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: %s: with -EXDEV "
+			    "urb->status %d, fifonum %d\n",
+			    hw->name, __func__,  status, fifon);
+
+		/* clear status, so go on with ISO transfers */
+		status = 0;
+	}
+
+	s0_state = 0;
+	if (fifo->active && !status) {
+		num_isoc_packets = iso_packets[fifon];
+		maxlen = fifo->usb_packet_maxlen;
+
+		for (k = 0; k < num_isoc_packets; ++k) {
+			len = urb->iso_frame_desc[k].actual_length;
+			offset = urb->iso_frame_desc[k].offset;
+			buf = context_iso_urb->buffer + offset;
+			iso_status = urb->iso_frame_desc[k].status;
+
+			if (iso_status && (debug & DBG_HFC_FIFO_VERBOSE)) {
+				printk(KERN_DEBUG "%s: %s: "
+				    "ISO packet %i, status: %i\n",
+				    hw->name, __func__, k, iso_status);
+			}
+
+			/* USB data log for every D ISO in */
+			if ((fifon == HFCUSB_D_RX) &&
+			    (debug & DBG_HFC_USB_VERBOSE)) {
+				printk(KERN_DEBUG
+				    "%s: %s: %d (%d/%d) len(%d) ",
+				    hw->name, __func__, urb->start_frame,
+				    k, num_isoc_packets-1,
+				    len);
+				for (i = 0; i < len; i++)
+					printk("%x ", buf[i]);
+				printk("\n");
+			}
+
+			if (!iso_status) {
+				if (fifo->last_urblen != maxlen) {
+					/*
+					 * save fifo fill-level threshold bits
+					 * to use them later in TX ISO URB
+					 * completions
+					 */
+					hw->threshold_mask = buf[1];
+
+					if (fifon == HFCUSB_D_RX)
+						s0_state = (buf[0] >> 4);
+
+					eof[fifon] = buf[0] & 1;
+					if (len > 2)
+						hfcsusb_rx_frame(fifo, buf + 2,
+							len - 2, (len < maxlen)
+							? eof[fifon] : 0);
+				} else
+					hfcsusb_rx_frame(fifo, buf, len,
+						(len < maxlen) ?
+						eof[fifon] : 0);
+				fifo->last_urblen = len;
+			}
+		}
+
+		/* signal S0 layer1 state change */
+		if ((s0_state) && (hw->initdone) &&
+		    (s0_state != hw->dch.state)) {
+			hw->dch.state = s0_state;
+			schedule_event(&hw->dch, FLG_PHCHANGE);
+		}
+
+		fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe,
+			      context_iso_urb->buffer, num_isoc_packets,
+			      fifo->usb_packet_maxlen, fifo->intervall,
+			      (usb_complete_t)rx_iso_complete, urb->context);
+		errcode = usb_submit_urb(urb, GFP_ATOMIC);
+		if (errcode < 0) {
+			if (debug & DEBUG_HW)
+				printk(KERN_DEBUG "%s: %s: error submitting "
+				    "ISO URB: %d\n",
+				    hw->name, __func__, errcode);
+		}
+	} else {
+		if (status && (debug & DBG_HFC_URB_INFO))
+			printk(KERN_DEBUG "%s: %s: rx_iso_complete : "
+			    "urb->status %d, fifonum %d\n",
+			    hw->name, __func__, status, fifon);
+	}
+}
+
+/* receive completion routine for all interrupt rx fifos */
+static void
+rx_int_complete(struct urb *urb)
+{
+	int len, status, i;
+	__u8 *buf, maxlen, fifon;
+	struct usb_fifo *fifo = (struct usb_fifo *) urb->context;
+	struct hfcsusb *hw = fifo->hw;
+	static __u8 eof[8];
+
+	spin_lock(&hw->lock);
+	if (fifo->stop_gracefull) {
+		fifo->stop_gracefull = 0;
+		fifo->active = 0;
+		spin_unlock(&hw->lock);
+		return;
+	}
+	spin_unlock(&hw->lock);
+
+	fifon = fifo->fifonum;
+	if ((!fifo->active) || (urb->status)) {
+		if (debug & DBG_HFC_URB_ERROR)
+			printk(KERN_DEBUG
+			    "%s: %s: RX-Fifo %i is going down (%i)\n",
+			    hw->name, __func__, fifon, urb->status);
+
+		fifo->urb->interval = 0; /* cancel automatic rescheduling */
+		return;
+	}
+	len = urb->actual_length;
+	buf = fifo->buffer;
+	maxlen = fifo->usb_packet_maxlen;
+
+	/* USB data log for every D INT in */
+	if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) {
+		printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ",
+		    hw->name, __func__, len);
+		for (i = 0; i < len; i++)
+			printk("%02x ", buf[i]);
+		printk("\n");
+	}
+
+	if (fifo->last_urblen != fifo->usb_packet_maxlen) {
+		/* the threshold mask is in the 2nd status byte */
+		hw->threshold_mask = buf[1];
+
+		/* signal S0 layer1 state change */
+		if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) {
+			hw->dch.state = (buf[0] >> 4);
+			schedule_event(&hw->dch, FLG_PHCHANGE);
+		}
+
+		eof[fifon] = buf[0] & 1;
+		/* if we have more than the 2 status bytes -> collect data */
+		if (len > 2)
+			hfcsusb_rx_frame(fifo, buf + 2,
+			   urb->actual_length - 2,
+			   (len < maxlen) ? eof[fifon] : 0);
+	} else {
+		hfcsusb_rx_frame(fifo, buf, urb->actual_length,
+				 (len < maxlen) ? eof[fifon] : 0);
+	}
+	fifo->last_urblen = urb->actual_length;
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		if (debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: %s: error resubmitting USB\n",
+			    hw->name, __func__);
+	}
+}
+
+/* transmit completion routine for all ISO tx fifos */
+static void
+tx_iso_complete(struct urb *urb)
+{
+	struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context;
+	struct usb_fifo *fifo = context_iso_urb->owner_fifo;
+	struct hfcsusb *hw = fifo->hw;
+	struct sk_buff *tx_skb;
+	int k, tx_offset, num_isoc_packets, sink, remain, current_len,
+	    errcode, hdlc, i;
+	int *tx_idx;
+	int frame_complete, fifon, status;
+	__u8 threshbit;
+
+	spin_lock(&hw->lock);
+	if (fifo->stop_gracefull) {
+		fifo->stop_gracefull = 0;
+		fifo->active = 0;
+		spin_unlock(&hw->lock);
+		return;
+	}
+
+	if (fifo->dch) {
+		tx_skb = fifo->dch->tx_skb;
+		tx_idx = &fifo->dch->tx_idx;
+		hdlc = 1;
+	} else if (fifo->bch) {
+		tx_skb = fifo->bch->tx_skb;
+		tx_idx = &fifo->bch->tx_idx;
+		hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags);
+	} else {
+		printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n",
+		    hw->name, __func__);
+		spin_unlock(&hw->lock);
+		return;
+	}
+
+	fifon = fifo->fifonum;
+	status = urb->status;
+
+	tx_offset = 0;
+
+	/*
+	 * ISO transfer only partially completed,
+	 * look at individual frame status for details
+	 */
+	if (status == -EXDEV) {
+		if (debug & DBG_HFC_URB_ERROR)
+			printk(KERN_DEBUG "%s: %s: "
+			    "-EXDEV (%i) fifon (%d)\n",
+			    hw->name, __func__, status, fifon);
+
+		/* clear status, so go on with ISO transfers */
+		status = 0;
+	}
+
+	if (fifo->active && !status) {
+		/* is FifoFull-threshold set for our channel? */
+		threshbit = (hw->threshold_mask & (1 << fifon));
+		num_isoc_packets = iso_packets[fifon];
+
+		/* predict dataflow to avoid fifo overflow */
+		if (fifon >= HFCUSB_D_TX)
+			sink = (threshbit) ? SINK_DMIN : SINK_DMAX;
+		else
+			sink = (threshbit) ? SINK_MIN : SINK_MAX;
+		fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe,
+			      context_iso_urb->buffer, num_isoc_packets,
+			      fifo->usb_packet_maxlen, fifo->intervall,
+			      (usb_complete_t)tx_iso_complete, urb->context);
+		memset(context_iso_urb->buffer, 0,
+		       sizeof(context_iso_urb->buffer));
+		frame_complete = 0;
+
+		for (k = 0; k < num_isoc_packets; ++k) {
+			/* analyze tx success of previous ISO packets */
+			if (debug & DBG_HFC_URB_ERROR) {
+				errcode = urb->iso_frame_desc[k].status;
+				if (errcode) {
+					printk(KERN_DEBUG "%s: %s: "
+					    "ISO packet %i, status: %i\n",
+					     hw->name, __func__, k, errcode);
+				}
+			}
+
+			/* Generate next ISO Packets */
+			if (tx_skb)
+				remain = tx_skb->len - *tx_idx;
+			else
+				remain = 0;
+
+			if (remain > 0) {
+				fifo->bit_line -= sink;
+				current_len = (0 - fifo->bit_line) / 8;
+				if (current_len > 14)
+					current_len = 14;
+				if (current_len < 0)
+					current_len = 0;
+				if (remain < current_len)
+					current_len = remain;
+
+				/* how much bit do we put on the line? */
+				fifo->bit_line += current_len * 8;
+
+				context_iso_urb->buffer[tx_offset] = 0;
+				if (current_len == remain) {
+					if (hdlc) {
+						/* signal frame completion */
+						context_iso_urb->
+						    buffer[tx_offset] = 1;
+						/* add 2 byte flags and 16bit
+						 * CRC at end of ISDN frame */
+						fifo->bit_line += 32;
+					}
+					frame_complete = 1;
+				}
+
+				/* copy tx data to iso-urb buffer */
+				memcpy(context_iso_urb->buffer + tx_offset + 1,
+				       (tx_skb->data + *tx_idx), current_len);
+				*tx_idx += current_len;
+
+				urb->iso_frame_desc[k].offset = tx_offset;
+				urb->iso_frame_desc[k].length = current_len + 1;
+
+				/* USB data log for every D ISO out */
+				if ((fifon == HFCUSB_D_RX) &&
+				    (debug & DBG_HFC_USB_VERBOSE)) {
+					printk(KERN_DEBUG
+					    "%s: %s (%d/%d) offs(%d) len(%d) ",
+					    hw->name, __func__,
+					    k, num_isoc_packets-1,
+					    urb->iso_frame_desc[k].offset,
+					    urb->iso_frame_desc[k].length);
+
+					for (i = urb->iso_frame_desc[k].offset;
+					     i < (urb->iso_frame_desc[k].offset
+					     + urb->iso_frame_desc[k].length);
+					     i++)
+						printk("%x ",
+						    context_iso_urb->buffer[i]);
+
+					printk(" skb->len(%i) tx-idx(%d)\n",
+					    tx_skb->len, *tx_idx);
+				}
+
+				tx_offset += (current_len + 1);
+			} else {
+				urb->iso_frame_desc[k].offset = tx_offset++;
+				urb->iso_frame_desc[k].length = 1;
+				/* we lower data margin every msec */
+				fifo->bit_line -= sink;
+				if (fifo->bit_line < BITLINE_INF)
+					fifo->bit_line = BITLINE_INF;
+			}
+
+			if (frame_complete) {
+				frame_complete = 0;
+
+				if (debug & DBG_HFC_FIFO_VERBOSE) {
+					printk(KERN_DEBUG  "%s: %s: "
+					    "fifon(%i) new TX len(%i): ",
+					    hw->name, __func__,
+					    fifon, tx_skb->len);
+					i = 0;
+					while (i < tx_skb->len)
+						printk("%02x ",
+						    tx_skb->data[i++]);
+					printk("\n");
+				}
+
+				dev_kfree_skb(tx_skb);
+				tx_skb = NULL;
+				if (fifo->dch && get_next_dframe(fifo->dch))
+					tx_skb = fifo->dch->tx_skb;
+				else if (fifo->bch &&
+				    get_next_bframe(fifo->bch)) {
+					if (test_bit(FLG_TRANSPARENT,
+					    &fifo->bch->Flags))
+						confirm_Bsend(fifo->bch);
+					tx_skb = fifo->bch->tx_skb;
+				}
+			}
+		}
+		errcode = usb_submit_urb(urb, GFP_ATOMIC);
+		if (errcode < 0) {
+			if (debug & DEBUG_HW)
+				printk(KERN_DEBUG
+				    "%s: %s: error submitting ISO URB: %d \n",
+				    hw->name, __func__, errcode);
+		}
+
+		/*
+		 * abuse DChannel tx iso completion to trigger NT mode state
+		 * changes tx_iso_complete is assumed to be called every
+		 * fifo->intervall (ms)
+		 */
+		if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0)
+		    && (hw->timers & NT_ACTIVATION_TIMER)) {
+			if ((--hw->nt_timer) < 0)
+				schedule_event(&hw->dch, FLG_PHCHANGE);
+		}
+
+	} else {
+		if (status && (debug & DBG_HFC_URB_ERROR))
+			printk(KERN_DEBUG  "%s: %s: urb->status %s (%i)"
+			    "fifonum=%d\n",
+			    hw->name, __func__,
+			    symbolic(urb_errlist, status), status, fifon);
+	}
+	spin_unlock(&hw->lock);
+}
+
+/*
+ * allocs urbs and start isoc transfer with two pending urbs to avoid
+ * gaps in the transfer chain
+ */
+static int
+start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb,
+		 usb_complete_t complete, int packet_size)
+{
+	struct hfcsusb *hw = fifo->hw;
+	int i, k, errcode;
+
+	if (debug)
+		printk(KERN_DEBUG "%s: %s: fifo %i\n",
+		    hw->name, __func__, fifo->fifonum);
+
+	/* allocate Memory for Iso out Urbs */
+	for (i = 0; i < 2; i++) {
+		if (!(fifo->iso[i].urb)) {
+			fifo->iso[i].urb =
+			    usb_alloc_urb(num_packets_per_urb, GFP_KERNEL);
+			if (!(fifo->iso[i].urb)) {
+				printk(KERN_DEBUG
+				    "%s: %s: alloc urb for fifo %i failed",
+				    hw->name, __func__, fifo->fifonum);
+			}
+			fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo;
+			fifo->iso[i].indx = i;
+
+			/* Init the first iso */
+			if (ISO_BUFFER_SIZE >=
+			    (fifo->usb_packet_maxlen *
+			     num_packets_per_urb)) {
+				fill_isoc_urb(fifo->iso[i].urb,
+				    fifo->hw->dev, fifo->pipe,
+				    fifo->iso[i].buffer,
+				    num_packets_per_urb,
+				    fifo->usb_packet_maxlen,
+				    fifo->intervall, complete,
+				    &fifo->iso[i]);
+				memset(fifo->iso[i].buffer, 0,
+				       sizeof(fifo->iso[i].buffer));
+
+				for (k = 0; k < num_packets_per_urb; k++) {
+					fifo->iso[i].urb->
+					    iso_frame_desc[k].offset =
+					    k * packet_size;
+					fifo->iso[i].urb->
+					    iso_frame_desc[k].length =
+					    packet_size;
+				}
+			} else {
+				printk(KERN_DEBUG
+				    "%s: %s: ISO Buffer size to small!\n",
+				    hw->name, __func__);
+			}
+		}
+		fifo->bit_line = BITLINE_INF;
+
+		errcode = usb_submit_urb(fifo->iso[i].urb, GFP_KERNEL);
+		fifo->active = (errcode >= 0) ? 1 : 0;
+		fifo->stop_gracefull = 0;
+		if (errcode < 0) {
+			printk(KERN_DEBUG "%s: %s: %s URB nr:%d\n",
+			    hw->name, __func__,
+			    symbolic(urb_errlist, errcode), i);
+		}
+	}
+	return fifo->active;
+}
+
+static void
+stop_iso_gracefull(struct usb_fifo *fifo)
+{
+	struct hfcsusb *hw = fifo->hw;
+	int i, timeout;
+	u_long flags;
+
+	for (i = 0; i < 2; i++) {
+		spin_lock_irqsave(&hw->lock, flags);
+		if (debug)
+			printk(KERN_DEBUG "%s: %s for fifo %i.%i\n",
+			       hw->name, __func__, fifo->fifonum, i);
+		fifo->stop_gracefull = 1;
+		spin_unlock_irqrestore(&hw->lock, flags);
+	}
+
+	for (i = 0; i < 2; i++) {
+		timeout = 3;
+		while (fifo->stop_gracefull && timeout--)
+			schedule_timeout_interruptible((HZ/1000)*16);
+		if (debug && fifo->stop_gracefull)
+			printk(KERN_DEBUG "%s: ERROR %s for fifo %i.%i\n",
+				hw->name, __func__, fifo->fifonum, i);
+	}
+}
+
+static void
+stop_int_gracefull(struct usb_fifo *fifo)
+{
+	struct hfcsusb *hw = fifo->hw;
+	int timeout;
+	u_long flags;
+
+	spin_lock_irqsave(&hw->lock, flags);
+	if (debug)
+		printk(KERN_DEBUG "%s: %s for fifo %i\n",
+		       hw->name, __func__, fifo->fifonum);
+	fifo->stop_gracefull = 1;
+	spin_unlock_irqrestore(&hw->lock, flags);
+
+	timeout = 3;
+	while (fifo->stop_gracefull && timeout--)
+		schedule_timeout_interruptible((HZ/1000)*3);
+	if (debug && fifo->stop_gracefull)
+		printk(KERN_DEBUG "%s: ERROR %s for fifo %i\n",
+		       hw->name, __func__, fifo->fifonum);
+}
+
+/* start the interrupt transfer for the given fifo */
+static void
+start_int_fifo(struct usb_fifo *fifo)
+{
+	struct hfcsusb *hw = fifo->hw;
+	int errcode;
+
+	if (debug)
+		printk(KERN_DEBUG "%s: %s: INT IN fifo:%d\n",
+		    hw->name, __func__, fifo->fifonum);
+
+	if (!fifo->urb) {
+		fifo->urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!fifo->urb)
+			return;
+	}
+	usb_fill_int_urb(fifo->urb, fifo->hw->dev, fifo->pipe,
+	    fifo->buffer, fifo->usb_packet_maxlen,
+	    (usb_complete_t)rx_int_complete, fifo, fifo->intervall);
+	fifo->active = 1;
+	fifo->stop_gracefull = 0;
+	errcode = usb_submit_urb(fifo->urb, GFP_KERNEL);
+	if (errcode) {
+		printk(KERN_DEBUG "%s: %s: submit URB: status:%i\n",
+		    hw->name, __func__, errcode);
+		fifo->active = 0;
+	}
+}
+
+static void
+setPortMode(struct hfcsusb *hw)
+{
+	if (debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: %s %s\n", hw->name, __func__,
+		   (hw->protocol == ISDN_P_TE_S0) ? "TE" : "NT");
+
+	if (hw->protocol == ISDN_P_TE_S0) {
+		write_reg(hw, HFCUSB_SCTRL, 0x40);
+		write_reg(hw, HFCUSB_SCTRL_E, 0x00);
+		write_reg(hw, HFCUSB_CLKDEL, CLKDEL_TE);
+		write_reg(hw, HFCUSB_STATES, 3 | 0x10);
+		write_reg(hw, HFCUSB_STATES, 3);
+	} else {
+		write_reg(hw, HFCUSB_SCTRL, 0x44);
+		write_reg(hw, HFCUSB_SCTRL_E, 0x09);
+		write_reg(hw, HFCUSB_CLKDEL, CLKDEL_NT);
+		write_reg(hw, HFCUSB_STATES, 1 | 0x10);
+		write_reg(hw, HFCUSB_STATES, 1);
+	}
+}
+
+static void
+reset_hfcsusb(struct hfcsusb *hw)
+{
+	struct usb_fifo *fifo;
+	int i;
+
+	if (debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
+
+	/* do Chip reset */
+	write_reg(hw, HFCUSB_CIRM, 8);
+
+	/* aux = output, reset off */
+	write_reg(hw, HFCUSB_CIRM, 0x10);
+
+	/* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */
+	write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) |
+	    ((hw->packet_size / 8) << 4));
+
+	/* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */
+	write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size);
+
+	/* enable PCM/GCI master mode */
+	write_reg(hw, HFCUSB_MST_MODE1, 0);	/* set default values */
+	write_reg(hw, HFCUSB_MST_MODE0, 1);	/* enable master mode */
+
+	/* init the fifos */
+	write_reg(hw, HFCUSB_F_THRES,
+	    (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4));
+
+	fifo = hw->fifos;
+	for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+		write_reg(hw, HFCUSB_FIFO, i);	/* select the desired fifo */
+		fifo[i].max_size =
+		    (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN;
+		fifo[i].last_urblen = 0;
+
+		/* set 2 bit for D- & E-channel */
+		write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2));
+
+		/* enable all fifos */
+		if (i == HFCUSB_D_TX)
+			write_reg(hw, HFCUSB_CON_HDLC,
+			    (hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09);
+		else
+			write_reg(hw, HFCUSB_CON_HDLC, 0x08);
+		write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */
+	}
+
+	write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */
+	handle_led(hw, LED_POWER_ON);
+}
+
+/* start USB data pipes dependand on device's endpoint configuration */
+static void
+hfcsusb_start_endpoint(struct hfcsusb *hw, int channel)
+{
+	/* quick check if endpoint already running */
+	if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active))
+		return;
+	if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active))
+		return;
+	if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active))
+		return;
+	if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active))
+		return;
+
+	/* start rx endpoints using USB INT IN method */
+	if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO)
+		start_int_fifo(hw->fifos + channel*2 + 1);
+
+	/* start rx endpoints using USB ISO IN method */
+	if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) {
+		switch (channel) {
+		case HFC_CHAN_D:
+			start_isoc_chain(hw->fifos + HFCUSB_D_RX,
+				ISOC_PACKETS_D,
+				(usb_complete_t)rx_iso_complete,
+				16);
+			break;
+		case HFC_CHAN_E:
+			start_isoc_chain(hw->fifos + HFCUSB_PCM_RX,
+				ISOC_PACKETS_D,
+				(usb_complete_t)rx_iso_complete,
+				16);
+			break;
+		case HFC_CHAN_B1:
+			start_isoc_chain(hw->fifos + HFCUSB_B1_RX,
+				ISOC_PACKETS_B,
+				(usb_complete_t)rx_iso_complete,
+				16);
+			break;
+		case HFC_CHAN_B2:
+			start_isoc_chain(hw->fifos + HFCUSB_B2_RX,
+				ISOC_PACKETS_B,
+				(usb_complete_t)rx_iso_complete,
+				16);
+			break;
+		}
+	}
+
+	/* start tx endpoints using USB ISO OUT method */
+	switch (channel) {
+	case HFC_CHAN_D:
+		start_isoc_chain(hw->fifos + HFCUSB_D_TX,
+			ISOC_PACKETS_B,
+			(usb_complete_t)tx_iso_complete, 1);
+		break;
+	case HFC_CHAN_B1:
+		start_isoc_chain(hw->fifos + HFCUSB_B1_TX,
+			ISOC_PACKETS_D,
+			(usb_complete_t)tx_iso_complete, 1);
+		break;
+	case HFC_CHAN_B2:
+		start_isoc_chain(hw->fifos + HFCUSB_B2_TX,
+			ISOC_PACKETS_B,
+			(usb_complete_t)tx_iso_complete, 1);
+		break;
+	}
+}
+
+/* stop USB data pipes dependand on device's endpoint configuration */
+static void
+hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel)
+{
+	/* quick check if endpoint currently running */
+	if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active))
+		return;
+	if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active))
+		return;
+	if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active))
+		return;
+	if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active))
+		return;
+
+	/* rx endpoints using USB INT IN method */
+	if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO)
+		stop_int_gracefull(hw->fifos + channel*2 + 1);
+
+	/* rx endpoints using USB ISO IN method */
+	if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO)
+		stop_iso_gracefull(hw->fifos + channel*2 + 1);
+
+	/* tx endpoints using USB ISO OUT method */
+	if (channel != HFC_CHAN_E)
+		stop_iso_gracefull(hw->fifos + channel*2);
+}
+
+
+/* Hardware Initialization */
+int
+setup_hfcsusb(struct hfcsusb *hw)
+{
+	int err;
+	u_char b;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
+
+	/* check the chip id */
+	if (read_reg_atomic(hw, HFCUSB_CHIP_ID, &b) != 1) {
+		printk(KERN_DEBUG "%s: %s: cannot read chip id\n",
+		    hw->name, __func__);
+		return 1;
+	}
+	if (b != HFCUSB_CHIPID) {
+		printk(KERN_DEBUG "%s: %s: Invalid chip id 0x%02x\n",
+		    hw->name, __func__, b);
+		return 1;
+	}
+
+	/* first set the needed config, interface and alternate */
+	err = usb_set_interface(hw->dev, hw->if_used, hw->alt_used);
+
+	hw->led_state = 0;
+
+	/* init the background machinery for control requests */
+	hw->ctrl_read.bRequestType = 0xc0;
+	hw->ctrl_read.bRequest = 1;
+	hw->ctrl_read.wLength = cpu_to_le16(1);
+	hw->ctrl_write.bRequestType = 0x40;
+	hw->ctrl_write.bRequest = 0;
+	hw->ctrl_write.wLength = 0;
+	usb_fill_control_urb(hw->ctrl_urb, hw->dev, hw->ctrl_out_pipe,
+	    (u_char *)&hw->ctrl_write, NULL, 0,
+	    (usb_complete_t)ctrl_complete, hw);
+
+	reset_hfcsusb(hw);
+	return 0;
+}
+
+static void
+release_hw(struct hfcsusb *hw)
+{
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
+
+	/*
+	 * stop all endpoints gracefully
+	 * TODO: mISDN_core should generate CLOSE_CHANNEL
+	 *       signals after calling mISDN_unregister_device()
+	 */
+	hfcsusb_stop_endpoint(hw, HFC_CHAN_D);
+	hfcsusb_stop_endpoint(hw, HFC_CHAN_B1);
+	hfcsusb_stop_endpoint(hw, HFC_CHAN_B2);
+	if (hw->fifos[HFCUSB_PCM_RX].pipe)
+		hfcsusb_stop_endpoint(hw, HFC_CHAN_E);
+	if (hw->protocol == ISDN_P_TE_S0)
+		l1_event(hw->dch.l1, CLOSE_CHANNEL);
+
+	mISDN_unregister_device(&hw->dch.dev);
+	mISDN_freebchannel(&hw->bch[1]);
+	mISDN_freebchannel(&hw->bch[0]);
+	mISDN_freedchannel(&hw->dch);
+
+	if (hw->ctrl_urb) {
+		usb_kill_urb(hw->ctrl_urb);
+		usb_free_urb(hw->ctrl_urb);
+		hw->ctrl_urb = NULL;
+	}
+
+	if (hw->intf)
+		usb_set_intfdata(hw->intf, NULL);
+	list_del(&hw->list);
+	kfree(hw);
+	hw = NULL;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+	struct hfcsusb *hw = bch->hw;
+	u_long flags;
+
+	if (bch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n",
+		    hw->name, __func__, bch->nr);
+
+	spin_lock_irqsave(&hw->lock, flags);
+	if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+		dev_kfree_skb(bch->next_skb);
+		bch->next_skb = NULL;
+	}
+	if (bch->tx_skb) {
+		dev_kfree_skb(bch->tx_skb);
+		bch->tx_skb = NULL;
+	}
+	bch->tx_idx = 0;
+	if (bch->rx_skb) {
+		dev_kfree_skb(bch->rx_skb);
+		bch->rx_skb = NULL;
+	}
+	clear_bit(FLG_ACTIVE, &bch->Flags);
+	clear_bit(FLG_TX_BUSY, &bch->Flags);
+	spin_unlock_irqrestore(&hw->lock, flags);
+	hfcsusb_setup_bch(bch, ISDN_P_NONE);
+	hfcsusb_stop_endpoint(hw, bch->nr);
+}
+
+/*
+ * Layer 1 B-channel hardware access
+ */
+static int
+hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct bchannel	*bch = container_of(ch, struct bchannel, ch);
+	int		ret = -EINVAL;
+
+	if (bch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg);
+
+	switch (cmd) {
+	case HW_TESTRX_RAW:
+	case HW_TESTRX_HDLC:
+	case HW_TESTRX_OFF:
+		ret = -EINVAL;
+		break;
+
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags))
+			deactivate_bchannel(bch);
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(THIS_MODULE);
+		ret = 0;
+		break;
+	case CONTROL_CHANNEL:
+		ret = channel_bctrl(bch, arg);
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown prim(%x)\n",
+			__func__, cmd);
+	}
+	return ret;
+}
+
+static int
+setup_instance(struct hfcsusb *hw, struct device *parent)
+{
+	u_long	flags;
+	int	err, i;
+
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_DEBUG "%s: %s\n", hw->name, __func__);
+
+	spin_lock_init(&hw->ctrl_lock);
+	spin_lock_init(&hw->lock);
+
+	mISDN_initdchannel(&hw->dch, MAX_DFRAME_LEN_L1, ph_state);
+	hw->dch.debug = debug & 0xFFFF;
+	hw->dch.hw = hw;
+	hw->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+	hw->dch.dev.D.send = hfcusb_l2l1D;
+	hw->dch.dev.D.ctrl = hfc_dctrl;
+
+	/* enable E-Channel logging */
+	if (hw->fifos[HFCUSB_PCM_RX].pipe)
+		mISDN_initdchannel(&hw->ech, MAX_DFRAME_LEN_L1, NULL);
+
+	hw->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+	    (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	hw->dch.dev.nrbchan = 2;
+	for (i = 0; i < 2; i++) {
+		hw->bch[i].nr = i + 1;
+		set_channelmap(i + 1, hw->dch.dev.channelmap);
+		hw->bch[i].debug = debug;
+		mISDN_initbchannel(&hw->bch[i], MAX_DATA_MEM);
+		hw->bch[i].hw = hw;
+		hw->bch[i].ch.send = hfcusb_l2l1B;
+		hw->bch[i].ch.ctrl = hfc_bctrl;
+		hw->bch[i].ch.nr = i + 1;
+		list_add(&hw->bch[i].ch.list, &hw->dch.dev.bchannels);
+	}
+
+	hw->fifos[HFCUSB_B1_TX].bch = &hw->bch[0];
+	hw->fifos[HFCUSB_B1_RX].bch = &hw->bch[0];
+	hw->fifos[HFCUSB_B2_TX].bch = &hw->bch[1];
+	hw->fifos[HFCUSB_B2_RX].bch = &hw->bch[1];
+	hw->fifos[HFCUSB_D_TX].dch = &hw->dch;
+	hw->fifos[HFCUSB_D_RX].dch = &hw->dch;
+	hw->fifos[HFCUSB_PCM_RX].ech = &hw->ech;
+	hw->fifos[HFCUSB_PCM_TX].ech = &hw->ech;
+
+	err = setup_hfcsusb(hw);
+	if (err)
+		goto out;
+
+	snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME,
+	    hfcsusb_cnt + 1);
+	printk(KERN_INFO "%s: registered as '%s'\n",
+	    DRIVER_NAME, hw->name);
+
+	err = mISDN_register_device(&hw->dch.dev, parent, hw->name);
+	if (err)
+		goto out;
+
+	hfcsusb_cnt++;
+	write_lock_irqsave(&HFClock, flags);
+	list_add_tail(&hw->list, &HFClist);
+	write_unlock_irqrestore(&HFClock, flags);
+	return 0;
+
+out:
+	mISDN_freebchannel(&hw->bch[1]);
+	mISDN_freebchannel(&hw->bch[0]);
+	mISDN_freedchannel(&hw->dch);
+	kfree(hw);
+	return err;
+}
+
+static int
+hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct hfcsusb			*hw;
+	struct usb_device		*dev = interface_to_usbdev(intf);
+	struct usb_host_interface	*iface = intf->cur_altsetting;
+	struct usb_host_interface	*iface_used = NULL;
+	struct usb_host_endpoint	*ep;
+	struct hfcsusb_vdata		*driver_info;
+	int ifnum = iface->desc.bInterfaceNumber, i, idx, alt_idx,
+	    probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found,
+	    ep_addr, cmptbl[16], small_match, iso_packet_size, packet_size,
+	    alt_used = 0;
+
+	vend_idx = 0xffff;
+	for (i = 0; hfcsusb_idtab[i].idVendor; i++) {
+		if ((le16_to_cpu(dev->descriptor.idVendor)
+		       == hfcsusb_idtab[i].idVendor) &&
+		    (le16_to_cpu(dev->descriptor.idProduct)
+		       == hfcsusb_idtab[i].idProduct)) {
+			vend_idx = i;
+			continue;
+		}
+	}
+
+	printk(KERN_DEBUG
+	    "%s: interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n",
+	    __func__, ifnum, iface->desc.bAlternateSetting,
+	    intf->minor, vend_idx);
+
+	if (vend_idx == 0xffff) {
+		printk(KERN_WARNING
+		    "%s: no valid vendor found in USB descriptor\n",
+		    __func__);
+		return -EIO;
+	}
+	/* if vendor and product ID is OK, start probing alternate settings */
+	alt_idx = 0;
+	small_match = -1;
+
+	/* default settings */
+	iso_packet_size = 16;
+	packet_size = 64;
+
+	while (alt_idx < intf->num_altsetting) {
+		iface = intf->altsetting + alt_idx;
+		probe_alt_setting = iface->desc.bAlternateSetting;
+		cfg_used = 0;
+
+		while (validconf[cfg_used][0]) {
+			cfg_found = 1;
+			vcf = validconf[cfg_used];
+			ep = iface->endpoint;
+			memcpy(cmptbl, vcf, 16 * sizeof(int));
+
+			/* check for all endpoints in this alternate setting */
+			for (i = 0; i < iface->desc.bNumEndpoints; i++) {
+				ep_addr = ep->desc.bEndpointAddress;
+
+				/* get endpoint base */
+				idx = ((ep_addr & 0x7f) - 1) * 2;
+				if (ep_addr & 0x80)
+					idx++;
+				attr = ep->desc.bmAttributes;
+
+				if (cmptbl[idx] != EP_NOP) {
+					if (cmptbl[idx] == EP_NUL)
+						cfg_found = 0;
+					if (attr == USB_ENDPOINT_XFER_INT
+						&& cmptbl[idx] == EP_INT)
+						cmptbl[idx] = EP_NUL;
+					if (attr == USB_ENDPOINT_XFER_BULK
+						&& cmptbl[idx] == EP_BLK)
+						cmptbl[idx] = EP_NUL;
+					if (attr == USB_ENDPOINT_XFER_ISOC
+						&& cmptbl[idx] == EP_ISO)
+						cmptbl[idx] = EP_NUL;
+
+					if (attr == USB_ENDPOINT_XFER_INT &&
+						ep->desc.bInterval < vcf[17]) {
+						cfg_found = 0;
+					}
+				}
+				ep++;
+			}
+
+			for (i = 0; i < 16; i++)
+				if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL)
+					cfg_found = 0;
+
+			if (cfg_found) {
+				if (small_match < cfg_used) {
+					small_match = cfg_used;
+					alt_used = probe_alt_setting;
+					iface_used = iface;
+				}
+			}
+			cfg_used++;
+		}
+		alt_idx++;
+	}	/* (alt_idx < intf->num_altsetting) */
+
+	/* not found a valid USB Ta Endpoint config */
+	if (small_match == -1)
+		return -EIO;
+
+	iface = iface_used;
+	hw = kzalloc(sizeof(struct hfcsusb), GFP_KERNEL);
+	if (!hw)
+		return -ENOMEM;	/* got no mem */
+	snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s", DRIVER_NAME);
+
+	ep = iface->endpoint;
+	vcf = validconf[small_match];
+
+	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
+		struct usb_fifo *f;
+
+		ep_addr = ep->desc.bEndpointAddress;
+		/* get endpoint base */
+		idx = ((ep_addr & 0x7f) - 1) * 2;
+		if (ep_addr & 0x80)
+			idx++;
+		f = &hw->fifos[idx & 7];
+
+		/* init Endpoints */
+		if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) {
+			ep++;
+			continue;
+		}
+		switch (ep->desc.bmAttributes) {
+		case USB_ENDPOINT_XFER_INT:
+			f->pipe = usb_rcvintpipe(dev,
+				ep->desc.bEndpointAddress);
+			f->usb_transfer_mode = USB_INT;
+			packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
+			break;
+		case USB_ENDPOINT_XFER_BULK:
+			if (ep_addr & 0x80)
+				f->pipe = usb_rcvbulkpipe(dev,
+					ep->desc.bEndpointAddress);
+			else
+				f->pipe = usb_sndbulkpipe(dev,
+					ep->desc.bEndpointAddress);
+			f->usb_transfer_mode = USB_BULK;
+			packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			if (ep_addr & 0x80)
+				f->pipe = usb_rcvisocpipe(dev,
+					ep->desc.bEndpointAddress);
+			else
+				f->pipe = usb_sndisocpipe(dev,
+					ep->desc.bEndpointAddress);
+			f->usb_transfer_mode = USB_ISOC;
+			iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
+			break;
+		default:
+			f->pipe = 0;
+		}
+
+		if (f->pipe) {
+			f->fifonum = idx & 7;
+			f->hw = hw;
+			f->usb_packet_maxlen =
+			    le16_to_cpu(ep->desc.wMaxPacketSize);
+			f->intervall = ep->desc.bInterval;
+		}
+		ep++;
+	}
+	hw->dev = dev; /* save device */
+	hw->if_used = ifnum; /* save used interface */
+	hw->alt_used = alt_used; /* and alternate config */
+	hw->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */
+	hw->cfg_used = vcf[16];	/* store used config */
+	hw->vend_idx = vend_idx; /* store found vendor */
+	hw->packet_size = packet_size;
+	hw->iso_packet_size = iso_packet_size;
+
+	/* create the control pipes needed for register access */
+	hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0);
+	hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0);
+	hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+	driver_info =
+		(struct hfcsusb_vdata *)hfcsusb_idtab[vend_idx].driver_info;
+	printk(KERN_DEBUG "%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n",
+	    hw->name, __func__, driver_info->vend_name,
+	    conf_str[small_match], ifnum, alt_used);
+
+	if (setup_instance(hw, dev->dev.parent))
+		return -EIO;
+
+	hw->intf = intf;
+	usb_set_intfdata(hw->intf, hw);
+	return 0;
+}
+
+/* function called when an active device is removed */
+static void
+hfcsusb_disconnect(struct usb_interface *intf)
+{
+	struct hfcsusb *hw = usb_get_intfdata(intf);
+	struct hfcsusb *next;
+	int cnt = 0;
+
+	printk(KERN_INFO "%s: device disconnected\n", hw->name);
+
+	handle_led(hw, LED_POWER_OFF);
+	release_hw(hw);
+
+	list_for_each_entry_safe(hw, next, &HFClist, list)
+		cnt++;
+	if (!cnt)
+		hfcsusb_cnt = 0;
+
+	usb_set_intfdata(intf, NULL);
+}
+
+static struct usb_driver hfcsusb_drv = {
+	.name = DRIVER_NAME,
+	.id_table = hfcsusb_idtab,
+	.probe = hfcsusb_probe,
+	.disconnect = hfcsusb_disconnect,
+};
+
+static int __init
+hfcsusb_init(void)
+{
+	printk(KERN_INFO DRIVER_NAME " driver Rev. %s debug(0x%x) poll(%i)\n",
+	    hfcsusb_rev, debug, poll);
+
+	if (usb_register(&hfcsusb_drv)) {
+		printk(KERN_INFO DRIVER_NAME
+		    ": Unable to register hfcsusb module at usb stack\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit
+hfcsusb_cleanup(void)
+{
+	if (debug & DBG_HFC_CALL_TRACE)
+		printk(KERN_INFO DRIVER_NAME ": %s\n", __func__);
+
+	/* unregister Hardware */
+	usb_deregister(&hfcsusb_drv);	/* release our driver */
+}
+
+module_init(hfcsusb_init);
+module_exit(hfcsusb_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h
new file mode 100644
index 000000000000..098486b8e8d2
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfcsusb.h
@@ -0,0 +1,418 @@
+/*
+ * hfcsusb.h, HFC-S USB mISDN driver
+ */
+
+#ifndef __HFCSUSB_H__
+#define __HFCSUSB_H__
+
+
+#define DRIVER_NAME "HFC-S_USB"
+
+#define DBG_HFC_CALL_TRACE	0x00010000
+#define DBG_HFC_FIFO_VERBOSE	0x00020000
+#define DBG_HFC_USB_VERBOSE	0x00100000
+#define DBG_HFC_URB_INFO	0x00200000
+#define DBG_HFC_URB_ERROR	0x00400000
+
+#define DEFAULT_TRANSP_BURST_SZ 128
+
+#define HFC_CTRL_TIMEOUT	20	/* 5ms timeout writing/reading regs */
+#define CLKDEL_TE		0x0f	/* CLKDEL in TE mode */
+#define CLKDEL_NT		0x6c	/* CLKDEL in NT mode */
+
+/* hfcsusb Layer1 commands */
+#define HFC_L1_ACTIVATE_TE		1
+#define HFC_L1_ACTIVATE_NT		2
+#define HFC_L1_DEACTIVATE_NT		3
+#define HFC_L1_FORCE_DEACTIVATE_TE	4
+
+/* cmd FLAGS in HFCUSB_STATES register */
+#define HFCUSB_LOAD_STATE	0x10
+#define HFCUSB_ACTIVATE		0x20
+#define HFCUSB_DO_ACTION	0x40
+#define HFCUSB_NT_G2_G3		0x80
+
+/* timers */
+#define NT_ACTIVATION_TIMER	0x01	/* enables NT mode activation Timer */
+#define NT_T1_COUNT		10
+
+#define MAX_BCH_SIZE 		2048	/* allowed B-channel packet size */
+
+#define HFCUSB_RX_THRESHOLD 	64	/* threshold for fifo report bit rx */
+#define HFCUSB_TX_THRESHOLD 	96	/* threshold for fifo report bit tx */
+
+#define HFCUSB_CHIP_ID		0x16	/* Chip ID register index */
+#define HFCUSB_CIRM		0x00	/* cirm register index */
+#define HFCUSB_USB_SIZE		0x07	/* int length register */
+#define HFCUSB_USB_SIZE_I	0x06	/* iso length register */
+#define HFCUSB_F_CROSS		0x0b	/* bit order register */
+#define HFCUSB_CLKDEL		0x37	/* bit delay register */
+#define HFCUSB_CON_HDLC		0xfa	/* channel connect register */
+#define HFCUSB_HDLC_PAR		0xfb
+#define HFCUSB_SCTRL		0x31	/* S-bus control register (tx) */
+#define HFCUSB_SCTRL_E		0x32	/* same for E and special funcs */
+#define HFCUSB_SCTRL_R		0x33	/* S-bus control register (rx) */
+#define HFCUSB_F_THRES		0x0c	/* threshold register */
+#define HFCUSB_FIFO		0x0f	/* fifo select register */
+#define HFCUSB_F_USAGE		0x1a	/* fifo usage register */
+#define HFCUSB_MST_MODE0	0x14
+#define HFCUSB_MST_MODE1	0x15
+#define HFCUSB_P_DATA		0x1f
+#define HFCUSB_INC_RES_F	0x0e
+#define HFCUSB_B1_SSL		0x20
+#define HFCUSB_B2_SSL		0x21
+#define HFCUSB_B1_RSL		0x24
+#define HFCUSB_B2_RSL		0x25
+#define HFCUSB_STATES		0x30
+
+
+#define HFCUSB_CHIPID		0x40	/* ID value of HFC-S USB */
+
+/* fifo registers */
+#define HFCUSB_NUM_FIFOS	8	/* maximum number of fifos */
+#define HFCUSB_B1_TX		0	/* index for B1 transmit bulk/int */
+#define HFCUSB_B1_RX		1	/* index for B1 receive bulk/int */
+#define HFCUSB_B2_TX		2
+#define HFCUSB_B2_RX		3
+#define HFCUSB_D_TX		4
+#define HFCUSB_D_RX		5
+#define HFCUSB_PCM_TX		6
+#define HFCUSB_PCM_RX		7
+
+
+#define USB_INT		0
+#define USB_BULK	1
+#define USB_ISOC	2
+
+#define ISOC_PACKETS_D	8
+#define ISOC_PACKETS_B	8
+#define ISO_BUFFER_SIZE	128
+
+/* defines how much ISO packets are handled in one URB */
+static int iso_packets[8] =
+    { ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B,
+	ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D
+};
+
+
+/* Fifo flow Control for TX ISO */
+#define SINK_MAX	68
+#define SINK_MIN	48
+#define SINK_DMIN	12
+#define SINK_DMAX	18
+#define BITLINE_INF	(-96*8)
+
+/* HFC-S USB register access by Control-URSs */
+#define write_reg_atomic(a, b, c) \
+	usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \
+		0, 0, HFC_CTRL_TIMEOUT)
+#define read_reg_atomic(a, b, c) \
+	usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \
+		1, HFC_CTRL_TIMEOUT)
+#define HFC_CTRL_BUFSIZE 64
+
+struct ctrl_buf {
+	__u8 hfcs_reg;		/* register number */
+	__u8 reg_val;		/* value to be written (or read) */
+};
+
+/*
+ * URB error codes
+ * Used to represent a list of values and their respective symbolic names
+ */
+struct hfcusb_symbolic_list {
+	const int num;
+	const char *name;
+};
+
+static struct hfcusb_symbolic_list urb_errlist[] = {
+	{-ENOMEM, "No memory for allocation of internal structures"},
+	{-ENOSPC, "The host controller's bandwidth is already consumed"},
+	{-ENOENT, "URB was canceled by unlink_urb"},
+	{-EXDEV, "ISO transfer only partially completed"},
+	{-EAGAIN, "Too match scheduled for the future"},
+	{-ENXIO, "URB already queued"},
+	{-EFBIG, "Too much ISO frames requested"},
+	{-ENOSR, "Buffer error (overrun)"},
+	{-EPIPE, "Specified endpoint is stalled (device not responding)"},
+	{-EOVERFLOW, "Babble (bad cable?)"},
+	{-EPROTO, "Bit-stuff error (bad cable?)"},
+	{-EILSEQ, "CRC/Timeout"},
+	{-ETIMEDOUT, "NAK (device does not respond)"},
+	{-ESHUTDOWN, "Device unplugged"},
+	{-1, NULL}
+};
+
+static inline const char *
+symbolic(struct hfcusb_symbolic_list list[], const int num)
+{
+	int i;
+	for (i = 0; list[i].name != NULL; i++)
+		if (list[i].num == num)
+			return list[i].name;
+	return "<unkown USB Error>";
+}
+
+/* USB descriptor need to contain one of the following EndPoint combination: */
+#define CNF_4INT3ISO	1	/* 4 INT IN, 3 ISO OUT */
+#define CNF_3INT3ISO	2	/* 3 INT IN, 3 ISO OUT */
+#define CNF_4ISO3ISO	3	/* 4 ISO IN, 3 ISO OUT */
+#define CNF_3ISO3ISO	4	/* 3 ISO IN, 3 ISO OUT */
+
+#define EP_NUL 1	/* Endpoint at this position not allowed */
+#define EP_NOP 2	/* all type of endpoints allowed at this position */
+#define EP_ISO 3	/* Isochron endpoint mandatory at this position */
+#define EP_BLK 4	/* Bulk endpoint mandatory at this position */
+#define EP_INT 5	/* Interrupt endpoint mandatory at this position */
+
+#define HFC_CHAN_B1	0
+#define HFC_CHAN_B2	1
+#define HFC_CHAN_D	2
+#define HFC_CHAN_E	3
+
+
+/*
+ * List of all supported enpoints configiration sets, used to find the
+ * best matching endpoint configuration within a devices' USB descriptor.
+ * We need at least 3 RX endpoints, and 3 TX endpoints, either
+ * INT-in and ISO-out, or ISO-in and ISO-out)
+ * with 4 RX endpoints even E-Channel logging is possible
+ */
+static int
+validconf[][19] = {
+	/* INT in, ISO out config */
+	{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT,
+	 EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+	 CNF_4INT3ISO, 2, 1},
+	{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL,
+	 EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+	 CNF_3INT3ISO, 2, 0},
+	/* ISO in, ISO out config */
+	{EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP,
+	 EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO,
+	 CNF_4ISO3ISO, 2, 1},
+	{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+	 EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL,
+	 CNF_3ISO3ISO, 2, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */
+};
+
+/* string description of chosen config */
+char *conf_str[] = {
+	"4 Interrupt IN + 3 Isochron OUT",
+	"3 Interrupt IN + 3 Isochron OUT",
+	"4 Isochron IN + 3 Isochron OUT",
+	"3 Isochron IN + 3 Isochron OUT"
+};
+
+
+#define LED_OFF		0	/* no LED support */
+#define LED_SCHEME1	1	/* LED standard scheme */
+#define LED_SCHEME2	2	/* not used yet... */
+
+#define LED_POWER_ON	1
+#define LED_POWER_OFF	2
+#define LED_S0_ON	3
+#define LED_S0_OFF	4
+#define LED_B1_ON	5
+#define LED_B1_OFF	6
+#define LED_B1_DATA	7
+#define LED_B2_ON	8
+#define LED_B2_OFF	9
+#define LED_B2_DATA	10
+
+#define LED_NORMAL	0	/* LEDs are normal */
+#define LED_INVERTED 	1	/* LEDs are inverted */
+
+/* time in ms to perform a Flashing LED when B-Channel has traffic */
+#define LED_TIME      250
+
+
+
+struct hfcsusb;
+struct usb_fifo;
+
+/* structure defining input+output fifos (interrupt/bulk mode) */
+struct iso_urb {
+	struct urb *urb;
+	__u8 buffer[ISO_BUFFER_SIZE];	/* buffer rx/tx USB URB data */
+	struct usb_fifo *owner_fifo;	/* pointer to owner fifo */
+	__u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */
+#ifdef ISO_FRAME_START_DEBUG
+	int start_frames[ISO_FRAME_START_RING_COUNT];
+	__u8 iso_frm_strt_pos; /* index in start_frame[] */
+#endif
+};
+
+struct usb_fifo {
+	int fifonum;		/* fifo index attached to this structure */
+	int active;		/* fifo is currently active */
+	struct hfcsusb *hw;	/* pointer to main structure */
+	int pipe;		/* address of endpoint */
+	__u8 usb_packet_maxlen;	/* maximum length for usb transfer */
+	unsigned int max_size;	/* maximum size of receive/send packet */
+	__u8 intervall;		/* interrupt interval */
+	struct urb *urb;	/* transfer structure for usb routines */
+	__u8 buffer[128];	/* buffer USB INT OUT URB data */
+	int bit_line;		/* how much bits are in the fifo? */
+
+	__u8 usb_transfer_mode; /* switched between ISO and INT */
+	struct iso_urb	iso[2]; /* two urbs to have one always
+					 one pending */
+
+	struct dchannel *dch;	/* link to hfcsusb_t->dch */
+	struct bchannel *bch;	/* link to hfcsusb_t->bch */
+	struct dchannel *ech;	/* link to hfcsusb_t->ech, TODO: E-CHANNEL */
+	int last_urblen;	/* remember length of last packet */
+	__u8 stop_gracefull;	/* stops URB retransmission */
+};
+
+struct hfcsusb {
+	struct list_head	list;
+	struct dchannel		dch;
+	struct bchannel		bch[2];
+	struct dchannel		ech; /* TODO : wait for struct echannel ;) */
+
+	struct usb_device	*dev;		/* our device */
+	struct usb_interface	*intf;		/* used interface */
+	int			if_used;	/* used interface number */
+	int			alt_used;	/* used alternate config */
+	int			cfg_used;	/* configuration index used */
+	int			vend_idx;	/* index in hfcsusb_idtab */
+	int			packet_size;
+	int			iso_packet_size;
+	struct usb_fifo		fifos[HFCUSB_NUM_FIFOS];
+
+	/* control pipe background handling */
+	struct ctrl_buf		ctrl_buff[HFC_CTRL_BUFSIZE];
+	int			ctrl_in_idx, ctrl_out_idx, ctrl_cnt;
+	struct urb		*ctrl_urb;
+	struct usb_ctrlrequest	ctrl_write;
+	struct usb_ctrlrequest	ctrl_read;
+	int			ctrl_paksize;
+	int			ctrl_in_pipe, ctrl_out_pipe;
+	spinlock_t		ctrl_lock; /* lock for ctrl */
+	spinlock_t              lock;
+
+	__u8			threshold_mask;
+	__u8			led_state;
+
+	__u8			protocol;
+	int			nt_timer;
+	int			open;
+	__u8			timers;
+	__u8			initdone;
+	char			name[MISDN_MAX_IDLEN];
+};
+
+/* private vendor specific data */
+struct hfcsusb_vdata {
+	__u8		led_scheme;  /* led display scheme */
+	signed short	led_bits[8]; /* array of 8 possible LED bitmask */
+	char		*vend_name;  /* device name */
+};
+
+
+#define HFC_MAX_TE_LAYER1_STATE 8
+#define HFC_MAX_NT_LAYER1_STATE 4
+
+const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = {
+	"TE F0 - Reset",
+	"TE F1 - Reset",
+	"TE F2 - Sensing",
+	"TE F3 - Deactivated",
+	"TE F4 - Awaiting signal",
+	"TE F5 - Identifying input",
+	"TE F6 - Synchronized",
+	"TE F7 - Activated",
+	"TE F8 - Lost framing",
+};
+
+const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = {
+	"NT G0 - Reset",
+	"NT G1 - Deactive",
+	"NT G2 - Pending activation",
+	"NT G3 - Active",
+	"NT G4 - Pending deactivation",
+};
+
+/* supported devices */
+static struct usb_device_id hfcsusb_idtab[] = {
+	{
+	 USB_DEVICE(0x0959, 0x2bd0),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_OFF, {4, 0, 2, 1},
+			   "ISDN USB TA (Cologne Chip HFC-S USB based)"}),
+	},
+	{
+	 USB_DEVICE(0x0675, 0x1688),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {1, 2, 0, 0},
+			   "DrayTek miniVigor 128 USB ISDN TA"}),
+	},
+	{
+	 USB_DEVICE(0x07b0, 0x0007),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {0x80, -64, -32, -16},
+			   "Billion tiny USB ISDN TA 128"}),
+	},
+	{
+	 USB_DEVICE(0x0742, 0x2008),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {4, 0, 2, 1},
+			   "Stollmann USB TA"}),
+	},
+	{
+	 USB_DEVICE(0x0742, 0x2009),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {4, 0, 2, 1},
+			   "Aceex USB ISDN TA"}),
+	},
+	{
+	 USB_DEVICE(0x0742, 0x200A),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {4, 0, 2, 1},
+			   "OEM USB ISDN TA"}),
+	},
+	{
+	 USB_DEVICE(0x08e3, 0x0301),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {2, 0, 1, 4},
+			   "Olitec USB RNIS"}),
+	},
+	{
+	 USB_DEVICE(0x07fa, 0x0846),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {0x80, -64, -32, -16},
+			   "Bewan Modem RNIS USB"}),
+	},
+	{
+	 USB_DEVICE(0x07fa, 0x0847),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {0x80, -64, -32, -16},
+			   "Djinn Numeris USB"}),
+	},
+	{
+	 USB_DEVICE(0x07b0, 0x0006),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {0x80, -64, -32, -16},
+			   "Twister ISDN TA"}),
+	},
+	{
+	 USB_DEVICE(0x071d, 0x1005),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {0x02, 0, 0x01, 0x04},
+			   "Eicon DIVA USB 4.0"}),
+	},
+	{
+	 USB_DEVICE(0x0586, 0x0102),
+	 .driver_info = (unsigned long) &((struct hfcsusb_vdata)
+			  {LED_SCHEME1, {0x88, -64, -32, -16},
+			   "ZyXEL OMNI.NET USB II"}),
+	},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, hfcsusb_idtab);
+
+#endif	/* __HFCSUSB_H__ */
-- 
cgit v1.2.3