patch-2.0.31 linux/drivers/isdn/hisax/isdnl1.c

Next file: linux/drivers/isdn/hisax/isdnl1.h
Previous file: linux/drivers/isdn/hisax/hisax.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
@@ -0,0 +1,1378 @@
+/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz Exp $
+
+ * isdnl1.c     common low level stuff for Siemens Chipsetbased isdn cards
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ *
+ * $Log: isdnl1.c,v $
+ * Revision 1.15  1997/05/27 15:17:55  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.14  1997/04/07 23:00:08  keil
+ * GFP_KERNEL ---> GFP_ATOMIC
+ *
+ * Revision 1.13  1997/04/06 22:55:50  keil
+ * Using SKB's
+ *
+ * Revision 1.12  1997/03/26 13:43:57  keil
+ * small cosmetics
+ *
+ * Revision 1.11  1997/03/25 23:11:23  keil
+ * US NI-1 protocol
+ *
+ * Revision 1.10  1997/03/13 14:45:05  keil
+ * using IRQ proof queue_task
+ *
+ * Revision 1.9  1997/03/12 21:44:21  keil
+ * change Interrupt routine from atomic quick to normal
+ *
+ * Revision 1.8  1997/02/09 00:24:31  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.7  1997/01/27 15:56:03  keil
+ * PCMCIA Teles card and ITK ix1 micro added
+ *
+ * Revision 1.6  1997/01/21 22:20:00  keil
+ * changes for D-channel log; Elsa Quickstep support
+ *
+ * Revision 1.5  1997/01/10 12:51:19  keil
+ * cleanup; set newversion
+ *
+ * Revision 1.4  1996/12/08 19:44:53  keil
+ * L2FRAME_DEBUG and other changes from Pekka Sarnila
+ *
+ * Revision 1.3  1996/11/18 15:34:47  keil
+ * fix HSCX version code
+ *
+ * Revision 1.2  1996/10/27 22:16:54  keil
+ * ISAC/HSCX version lookup
+ *
+ * Revision 1.1  1996/10/13 20:04:53  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+
+const char *l1_revision = "$Revision: 1.15 $";
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isdnl1.h"
+
+#if CARD_TELES0
+#include "teles0.h"
+#endif
+
+#if CARD_TELES3
+#include "teles3.h"
+#endif
+
+#if CARD_AVM_A1
+#include "avm_a1.h"
+#endif
+
+#if CARD_ELSA
+#include "elsa.h"
+#endif
+
+#if CARD_IX1MICROR2
+#include "ix1_micro.h"
+#endif
+
+/* #define I4L_IRQ_FLAG SA_INTERRUPT */
+#define I4L_IRQ_FLAG    0
+
+#define HISAX_STATUS_BUFSIZE 4096
+
+#define INCLUDE_INLINE_FUNCS
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+const char *CardType[] =
+{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3",
+ "Creatix/Teles PnP", "AVM A1", "Elsa ML",
+#ifdef CONFIG_HISAX_ELSA_PCMCIA
+ "Elsa PCMCIA",
+#else
+ "Elsa Quickstep",
+#endif
+ "Teles PCMCIA", "ITK ix1-micro Rev.2"};
+
+static char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+static char *ISACVer[] =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+extern char *HiSax_id;
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*
+hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp)
+			if (cards[i].sp->myid == driverid)
+				return (cards[i].sp);
+	return (struct IsdnCardState *) 0;
+}
+
+int
+HiSax_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	int count;
+	u_char *p;
+	struct IsdnCardState *csta = hisax_findcard(id);
+
+	if (csta) {
+		for (p = buf, count = 0; count < len; p++, count++) {
+			if (user)
+				put_user(*csta->status_read++, p);
+			else
+				*p++ = *csta->status_read++;
+			if (csta->status_read > csta->status_end)
+				csta->status_read = csta->status_buf;
+		}
+		return count;
+	} else {
+		printk(KERN_ERR
+		 "HiSax: if_readstatus called with invalid driverId!\n");
+		return -ENODEV;
+	}
+}
+
+void
+HiSax_putstatus(struct IsdnCardState *csta, char *buf)
+{
+	long flags;
+	int len, count, i;
+	u_char *p;
+	isdn_ctrl ic;
+
+	save_flags(flags);
+	cli();
+	count = 0;
+	len = strlen(buf);
+
+	if (!csta) {
+		printk(KERN_WARNING "HiSax: No CardStatus for message %s", buf);
+		restore_flags(flags);
+		return;
+	}
+	for (p = buf, i = len; i > 0; i--, p++) {
+		*csta->status_write++ = *p;
+		if (csta->status_write > csta->status_end)
+			csta->status_write = csta->status_buf;
+		count++;
+	}
+	restore_flags(flags);
+	if (count) {
+		ic.command = ISDN_STAT_STAVAIL;
+		ic.driver = csta->myid;
+		ic.arg = count;
+		csta->iif.statcallb(&ic);
+	}
+}
+
+int
+ll_run(struct IsdnCardState *csta)
+{
+	long flags;
+	isdn_ctrl ic;
+
+	save_flags(flags);
+	cli();
+	ic.driver = csta->myid;
+	ic.command = ISDN_STAT_RUN;
+	csta->iif.statcallb(&ic);
+	restore_flags(flags);
+	return 0;
+}
+
+void
+ll_stop(struct IsdnCardState *csta)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_STOP;
+	ic.driver = csta->myid;
+	csta->iif.statcallb(&ic);
+	CallcFreeChan(csta);
+}
+
+static void
+ll_unload(struct IsdnCardState *csta)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_UNLOAD;
+	ic.driver = csta->myid;
+	csta->iif.statcallb(&ic);
+	if (csta->status_buf)
+		kfree(csta->status_buf);
+	csta->status_read = NULL;
+	csta->status_write = NULL;
+	csta->status_end = NULL;
+	kfree(csta->dlogspace);
+}
+
+void
+debugl1(struct IsdnCardState *sp, char *msg)
+{
+	char tmp[256], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Card %d %s\n", tm, sp->cardnr + 1, msg);
+	HiSax_putstatus(sp, tmp);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+
+char *
+HscxVersion(u_char v)
+{
+	return (HSCXVer[v & 0xf]);
+}
+
+void
+hscx_sched_event(struct HscxState *hsp, int event)
+{
+	hsp->event |= 1 << event;
+	queue_task(&hsp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+char *
+ISACVersion(u_char v)
+{
+	return (ISACVer[(v >> 5) & 3]);
+}
+
+void
+isac_sched_event(struct IsdnCardState *sp, int event)
+{
+	sp->event |= 1 << event;
+	queue_task(&sp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+int
+act_wanted(struct IsdnCardState *sp)
+{
+	struct PStack *st;
+
+	st = sp->stlist;
+	while (st)
+		if (st->l1.act_state)
+			return (!0);
+		else
+			st = st->next;
+	return (0);
+}
+
+void
+isac_new_ph(struct IsdnCardState *sp)
+{
+	int enq;
+
+	enq = act_wanted(sp);
+
+	switch (sp->ph_state) {
+		case (6):
+			sp->ph_active = 0;
+			sp->ph_command(sp, 15);
+			break;
+		case (15):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 0);
+			break;
+		case (0):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 0);
+#if 0
+			else
+				sp->ph_command(sp, 15);
+#endif
+			break;
+		case (7):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 9);
+			break;
+		case (12):
+			sp->ph_command(sp, 8);
+			sp->ph_active = 5;
+			isac_sched_event(sp, ISAC_PHCHANGE);
+			if (!sp->tx_skb)
+				sp->tx_skb = skb_dequeue(&sp->sq);
+			if (sp->tx_skb) {
+				sp->tx_cnt = 0;
+				sp->isac_fill_fifo(sp);
+			}
+			break;
+		case (13):
+			sp->ph_command(sp, 9);
+			sp->ph_active = 5;
+			isac_sched_event(sp, ISAC_PHCHANGE);
+			if (!sp->tx_skb)
+				sp->tx_skb = skb_dequeue(&sp->sq);
+			if (sp->tx_skb) {
+				sp->tx_cnt = 0;
+				sp->isac_fill_fifo(sp);
+			}
+			break;
+		case (4):
+		case (8):
+			sp->ph_active = 0;
+			break;
+		default:
+			sp->ph_active = 0;
+			break;
+	}
+}
+
+static void
+restart_ph(struct IsdnCardState *sp)
+{
+	if (!sp->ph_active) {
+		if ((sp->ph_state == 6) || (sp->ph_state == 0)) {
+			sp->ph_command(sp, 0);
+			sp->ph_active = 2;
+		} else {
+			sp->ph_command(sp, 1);
+			sp->ph_active = 1;
+		}
+	} else if (sp->ph_active == 2) {
+		sp->ph_command(sp, 1);
+		sp->ph_active = 1;
+	}
+}
+
+
+static void
+act_ivated(struct IsdnCardState *sp)
+{
+	struct PStack *st;
+
+	st = sp->stlist;
+	while (st) {
+		if (st->l1.act_state == 1) {
+			st->l1.act_state = 2;
+			st->l1.l1man(st, PH_ACTIVATE, NULL);
+		}
+		st = st->next;
+	}
+}
+
+static void
+process_new_ph(struct IsdnCardState *sp)
+{
+	if (sp->ph_active == 5)
+		act_ivated(sp);
+}
+
+static void
+process_xmt(struct IsdnCardState *sp)
+{
+	struct PStack *stptr;
+
+	if (sp->tx_skb)
+		return;
+
+	stptr = sp->stlist;
+	while (stptr != NULL)
+		if (stptr->l1.requestpull) {
+			stptr->l1.requestpull = 0;
+			stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+			break;
+		} else
+			stptr = stptr->next;
+}
+
+static void
+process_rcv(struct IsdnCardState *sp)
+{
+	struct sk_buff *skb, *nskb;
+	struct PStack *stptr;
+	int found, broadc;
+	char tmp[64];
+
+	while ((skb = skb_dequeue(&sp->rq))) {
+#ifdef L2FRAME_DEBUG		/* psa */
+		if (sp->debug & L1_DEB_LAPD)
+			Logl2Frame(sp, skb, "PH_DATA", 1);
+#endif
+		stptr = sp->stlist;
+		broadc = (skb->data[1] >> 1) == 127;
+
+		if (broadc) {
+			if (!(skb->data[0] >> 2)) {	/* sapi 0 */
+				sp->CallFlags = 3;
+				if (sp->dlogflag) {
+					LogFrame(sp, skb->data, skb->len);
+					dlogframe(sp, skb->data + 3, skb->len - 3,
+						  "Q.931 frame network->user broadcast");
+				}
+			}
+			while (stptr != NULL) {
+				if ((skb->data[0] >> 2) == stptr->l2.sap)
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1l2(stptr, PH_DATA, nskb);
+					else
+						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
+				stptr = stptr->next;
+			}
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, FREE_READ);
+		} else {
+			found = 0;
+			while (stptr != NULL)
+				if (((skb->data[0] >> 2) == stptr->l2.sap) &&
+				((skb->data[1] >> 1) == stptr->l2.tei)) {
+					stptr->l1.l1l2(stptr, PH_DATA, skb);
+					found = !0;
+					break;
+				} else
+					stptr = stptr->next;
+			if (!found) {
+				/* BD 10.10.95
+				 * Print out D-Channel msg not processed
+				 * by isdn4linux
+				 */
+
+				if ((!(skb->data[0] >> 2)) && (!(skb->data[2] & 0x01))) {
+					sprintf(tmp,
+						"Q.931 frame network->user with tei %d (not for us)",
+						skb->data[1] >> 1);
+					LogFrame(sp, skb->data, skb->len);
+					dlogframe(sp, skb->data + 4, skb->len - 4, tmp);
+				}
+				SET_SKB_FREE(skb);
+				dev_kfree_skb(skb, FREE_READ);
+			}
+		}
+
+	}
+
+}
+
+static void
+isac_bh(struct IsdnCardState *sp)
+{
+	if (!sp)
+		return;
+
+	if (test_and_clear_bit(ISAC_PHCHANGE, &sp->event))
+		process_new_ph(sp);
+	if (test_and_clear_bit(ISAC_RCVBUFREADY, &sp->event))
+		process_rcv(sp);
+	if (test_and_clear_bit(ISAC_XMTBUFREADY, &sp->event))
+		process_xmt(sp);
+}
+
+static void
+l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	char str[64];
+
+	switch (pr) {
+		case (PH_DATA):
+			if (sp->tx_skb) {
+				skb_queue_tail(&sp->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (sp->debug & L1_DEB_LAPD)
+					Logl2Frame(sp, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				if ((sp->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+					LogFrame(sp, skb->data, skb->len);
+					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+					dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize,
+						  str);
+				}
+				sp->tx_skb = skb;
+				sp->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (sp->debug & L1_DEB_LAPD)
+					Logl2Frame(sp, skb, "PH_DATA", 0);
+#endif
+				sp->isac_fill_fifo(sp);
+			}
+			break;
+		case (PH_DATA_PULLED):
+			if (sp->tx_skb) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, " l2l1 tx_skb exist this shouldn't happen");
+				break;
+			}
+			if ((sp->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+				LogFrame(sp, skb->data, skb->len);
+				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+				dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize,
+					  str);
+			}
+			sp->tx_skb = skb;
+			sp->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (sp->debug & L1_DEB_LAPD)
+				Logl2Frame(sp, skb, "PH_DATA_PULLED", 0);
+#endif
+			sp->isac_fill_fifo(sp);
+			break;
+		case (PH_REQUEST_PULL):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (sp->debug & L1_DEB_LAPD)
+				debugl1(sp, "-> PH_REQUEST_PULL");
+#endif
+			if (!sp->tx_skb) {
+				st->l1.requestpull = 0;
+				st->l1.l1l2(st, PH_PULL_ACK, NULL);
+			} else
+				st->l1.requestpull = !0;
+			break;
+	}
+}
+
+
+static void
+hscx_process_xmt(struct HscxState *hsp)
+{
+	struct PStack *st = hsp->st;
+
+	if (hsp->tx_skb)
+		return;
+
+	if (st->l1.requestpull) {
+		st->l1.requestpull = 0;
+		st->l1.l1l2(st, PH_PULL_ACK, NULL);
+	}
+	if (!hsp->active)
+		if ((!hsp->tx_skb) && (!skb_queue_len(&hsp->squeue)))
+			hsp->sp->modehscx(hsp, 0, 0);
+}
+
+static void
+hscx_process_rcv(struct HscxState *hsp)
+{
+	struct sk_buff *skb;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
+		return;
+	}
+#endif
+	while ((skb = skb_dequeue(&hsp->rqueue))) {
+		hsp->st->l1.l1l2(hsp->st, PH_DATA, skb);
+	}
+}
+
+static void
+hscx_bh(struct HscxState *hsp)
+{
+
+	if (!hsp)
+		return;
+
+	if (test_and_clear_bit(HSCX_RCVBUFREADY, &hsp->event))
+		hscx_process_rcv(hsp);
+	if (test_and_clear_bit(HSCX_XMTBUFREADY, &hsp->event))
+		hscx_process_xmt(hsp);
+
+}
+
+/*
+ * interrupt stuff ends here
+ */
+
+void
+HiSax_addlist(struct IsdnCardState *sp,
+	      struct PStack *st)
+{
+	st->next = sp->stlist;
+	sp->stlist = st;
+}
+
+void
+HiSax_rmlist(struct IsdnCardState *sp,
+	     struct PStack *st)
+{
+	struct PStack *p;
+
+	if (sp->stlist == st)
+		sp->stlist = st->next;
+	else {
+		p = sp->stlist;
+		while (p)
+			if (p->next == st) {
+				p->next = st->next;
+				return;
+			} else
+				p = p->next;
+	}
+}
+
+static void
+check_ph_act(struct IsdnCardState *sp)
+{
+	struct PStack *st = sp->stlist;
+
+	while (st) {
+		if (st->l1.act_state)
+			return;
+		st = st->next;
+	}
+	if (sp->ph_active == 5)
+		sp->ph_active = 4;
+}
+
+static void
+HiSax_manl1(struct PStack *st, int pr,
+	    void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	long flags;
+	char tmp[32];
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			if (sp->debug) {
+				sprintf(tmp, "PH_ACT ph_active %d", sp->ph_active);
+				debugl1(sp, tmp);
+			}
+			save_flags(flags);
+			cli();
+			if (sp->ph_active & 4) {
+				sp->ph_active = 5;
+				st->l1.act_state = 2;
+				restore_flags(flags);
+				st->l1.l1man(st, PH_ACTIVATE, NULL);
+			} else {
+				st->l1.act_state = 1;
+				if (sp->ph_active == 0)
+					restart_ph(sp);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_DEACTIVATE):
+			st->l1.act_state = 0;
+			if (sp->debug) {
+				sprintf(tmp, "PH_DEACT ph_active %d", sp->ph_active);
+				debugl1(sp, tmp);
+			}
+			check_ph_act(sp);
+			break;
+	}
+}
+
+static void
+HiSax_l2l1discardq(struct PStack *st, int pr,
+		   void *heldby, int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb;
+
+#ifdef DEBUG_MAGIC
+	if (sp->magic != 301271) {
+		printk(KERN_DEBUG "isac_discardq magic not 301271\n");
+		return;
+	}
+#endif
+
+	while ((skb = skb_dequeue(&sp->sq)))
+		dev_kfree_skb(skb, FREE_WRITE);
+}
+
+void
+setstack_HiSax(struct PStack *st, struct IsdnCardState *sp)
+{
+	st->l1.hardware = sp;
+	st->protocol = sp->protocol;
+
+	setstack_tei(st);
+
+	st->l1.stlistp = &(sp->stlist);
+	st->l1.act_state = 0;
+	st->l2.l2l1 = l2l1;
+	st->l2.l2l1discardq = HiSax_l2l1discardq;
+	st->ma.manl1 = HiSax_manl1;
+	st->l1.requestpull = 0;
+}
+
+void
+init_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	hsp->sp = sp;
+	hsp->hscx = hscx;
+
+	hsp->tqueue.next = 0;
+	hsp->tqueue.sync = 0;
+	hsp->tqueue.routine = (void *) (void *) hscx_bh;
+	hsp->tqueue.data = hsp;
+
+	hsp->inuse = 0;
+	hsp->init = 0;
+	hsp->active = 0;
+
+#ifdef DEBUG_MAGIC
+	hsp->magic = 301270;
+#endif
+}
+
+int
+get_irq(int cardnr, void *routine)
+{
+	struct IsdnCard *card = cards + cardnr;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	if (request_irq(card->sp->irq, routine,
+			I4L_IRQ_FLAG, "HiSax", NULL)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+		       card->sp->irq);
+		restore_flags(flags);
+		return (0);
+	}
+	irq2dev_map[card->sp->irq] = (void *) card->sp;
+	restore_flags(flags);
+	return (1);
+}
+
+static void
+release_irq(int cardnr)
+{
+	struct IsdnCard *card = cards + cardnr;
+
+	irq2dev_map[card->sp->irq] = NULL;
+	free_irq(card->sp->irq, NULL);
+}
+
+void
+close_hscxstate(struct HscxState *hs)
+{
+	struct sk_buff *skb;
+
+	hs->sp->modehscx(hs, 0, 0);
+	hs->inuse = 0;
+	if (hs->init) {
+		if (hs->rcvbuf) {
+			kfree(hs->rcvbuf);
+			hs->rcvbuf = NULL;
+		}
+		while ((skb = skb_dequeue(&hs->rqueue))) {
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, FREE_READ);
+		}
+		while ((skb = skb_dequeue(&hs->squeue)))
+			dev_kfree_skb(skb, FREE_WRITE);
+		if (hs->tx_skb) {
+			dev_kfree_skb(hs->tx_skb, FREE_WRITE);
+			hs->tx_skb = NULL;
+		}
+	}
+	hs->init = 0;
+}
+
+static void
+closecard(int cardnr)
+{
+	struct IsdnCardState *csta = cards[cardnr].sp;
+	struct sk_buff *skb;
+
+	close_hscxstate(csta->hs + 1);
+	close_hscxstate(csta->hs);
+
+	if (csta->rcvbuf) {
+		kfree(csta->rcvbuf);
+		csta->rcvbuf = NULL;
+	}
+	while ((skb = skb_dequeue(&csta->rq))) {
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+	}
+	while ((skb = skb_dequeue(&csta->sq)))
+		dev_kfree_skb(skb, FREE_WRITE);
+	if (csta->tx_skb) {
+		dev_kfree_skb(csta->tx_skb, FREE_WRITE);
+		csta->tx_skb = NULL;
+	}
+	switch (csta->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			release_io_teles0(cards + cardnr);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_TELESPCMCIA:
+			release_io_teles3(cards + cardnr);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			release_io_avm_a1(cards + cardnr);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			release_io_elsa(cards + cardnr);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			release_io_ix1micro(cards + cardnr);
+			break;
+#endif
+		default:
+			break;
+	}
+	ll_unload(csta);
+}
+
+static int
+checkcard(int cardnr, char *id)
+{
+	long flags;
+	int ret = 0;
+	struct IsdnCard *card = cards + cardnr;
+	struct IsdnCardState *sp;
+
+	save_flags(flags);
+	cli();
+	if (!(sp = (struct IsdnCardState *)
+	      kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for IsdnCardState(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	card->sp = sp;
+	sp->cardnr = cardnr;
+	sp->cfg_reg = 0;
+	sp->protocol = card->protocol;
+
+	if ((card->typ > 0) && (card->typ < 31)) {
+		if (!((1 << card->typ) & SUPORTED_CARDS)) {
+			printk(KERN_WARNING
+			     "HiSax: Support for %s Card not selected\n",
+			       CardType[card->typ]);
+			restore_flags(flags);
+			return (0);
+		}
+	} else {
+		printk(KERN_WARNING
+		       "HiSax: Card Type %d out of range\n",
+		       card->typ);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(sp->dlogspace = kmalloc(4096, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for dlogspace(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(sp->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for status_buf(card %d)\n",
+		       cardnr + 1);
+		kfree(sp->dlogspace);
+		restore_flags(flags);
+		return (0);
+	}
+	sp->status_read = sp->status_buf;
+	sp->status_write = sp->status_buf;
+	sp->status_end = sp->status_buf + HISAX_STATUS_BUFSIZE - 1;
+	sp->typ = card->typ;
+	sp->CallFlags = 0;
+	strcpy(sp->iif.id, id);
+	sp->iif.channels = 2;
+	sp->iif.maxbufsize = MAX_DATA_SIZE;
+	sp->iif.hl_hdrlen = MAX_HEADER_LEN;
+	sp->iif.features =
+	    ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L2_TRANS |
+	    ISDN_FEATURE_L3_TRANS |
+#ifdef	CONFIG_HISAX_1TR6
+	    ISDN_FEATURE_P_1TR6 |
+#endif
+#ifdef	CONFIG_HISAX_EURO
+	    ISDN_FEATURE_P_EURO |
+#endif
+#ifdef        CONFIG_HISAX_NI1
+	    ISDN_FEATURE_P_NI1 |
+#endif
+	    0;
+
+	sp->iif.command = HiSax_command;
+	sp->iif.writebuf = NULL;
+	sp->iif.writecmd = NULL;
+	sp->iif.writebuf_skb = HiSax_writebuf_skb;
+	sp->iif.readstat = HiSax_readstatus;
+	register_isdn(&sp->iif);
+	sp->myid = sp->iif.channels;
+	restore_flags(flags);
+	printk(KERN_NOTICE
+	       "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
+	       (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
+	       (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
+	       (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
+	       (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
+	       "NONE", sp->iif.id, sp->myid);
+	switch (card->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			ret = setup_teles0(card);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_TELESPCMCIA:
+			ret = setup_teles3(card);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = setup_avm_a1(card);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			ret = setup_elsa(card);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = setup_ix1micro(card);
+			break;
+#endif
+		default:
+			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
+			       card->typ);
+			ll_unload(sp);
+			return (0);
+	}
+	if (!ret) {
+		ll_unload(sp);
+		return (0);
+	}
+	if (!(sp->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for isac rcvbuf\n");
+		return (1);
+	}
+	sp->rcvidx = 0;
+	sp->tx_skb = NULL;
+	sp->tx_cnt = 0;
+	sp->event = 0;
+	sp->tqueue.next = 0;
+	sp->tqueue.sync = 0;
+	sp->tqueue.routine = (void *) (void *) isac_bh;
+	sp->tqueue.data = sp;
+
+	skb_queue_head_init(&sp->rq);
+	skb_queue_head_init(&sp->sq);
+
+	sp->stlist = NULL;
+	sp->ph_active = 0;
+	sp->dlogflag = 0;
+	sp->debug = L1_DEB_WARN;
+#ifdef DEBUG_MAGIC
+	sp->magic = 301271;
+#endif
+
+	init_hscxstate(sp, 0);
+	init_hscxstate(sp, 1);
+
+	switch (card->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			ret = initteles0(sp);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_TELESPCMCIA:
+			ret = initteles3(sp);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = initavm_a1(sp);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			ret = initelsa(sp);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = initix1micro(sp);
+			break;
+#endif
+		default:
+			ret = 0;
+			break;
+	}
+	if (!ret) {
+		closecard(cardnr);
+		return (0);
+	}
+	init_tei(sp, sp->protocol);
+	CallcNewChan(sp);
+	ll_run(sp);
+	return (1);
+}
+
+void
+HiSax_shiftcards(int idx)
+{
+	int i;
+
+	for (i = idx; i < 15; i++)
+		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+int
+HiSax_inithardware(void)
+{
+	int foundcards = 0;
+	int i = 0;
+	int t = ',';
+	int flg = 0;
+	char *id;
+	char *next_id = HiSax_id;
+	char ids[20];
+
+	if (strchr(HiSax_id, ','))
+		t = ',';
+	else if (strchr(HiSax_id, '%'))
+		t = '%';
+
+	while (i < nrcards) {
+		if (cards[i].typ < 1)
+			break;
+		id = next_id;
+		if ((next_id = strchr(id, t))) {
+			*next_id++ = 0;
+			strcpy(ids, id);
+			flg = i + 1;
+		} else {
+			next_id = id;
+			if (flg >= i)
+				strcpy(ids, id);
+			else
+				sprintf(ids, "%s%d", id, i);
+		}
+		if (checkcard(i, ids)) {
+			foundcards++;
+			i++;
+		} else {
+			printk(KERN_WARNING "HiSax: Card %s not installed !\n",
+			       CardType[cards[i].typ]);
+			if (cards[i].sp)
+				kfree((void *) cards[i].sp);
+			cards[i].sp = NULL;
+			HiSax_shiftcards(i);
+		}
+	}
+	return foundcards;
+}
+
+void
+HiSax_closehardware(void)
+{
+	int i;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp) {
+			ll_stop(cards[i].sp);
+			CallcFreeChan(cards[i].sp);
+			release_tei(cards[i].sp);
+			release_irq(i);
+			closecard(i);
+			kfree((void *) cards[i].sp);
+			cards[i].sp = NULL;
+		}
+	Isdnl2Free();
+	CallcFree();
+	restore_flags(flags);
+}
+
+static void
+hscx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA):
+			save_flags(flags);
+			cli();
+			if (hsp->tx_skb) {
+				skb_queue_tail(&hsp->squeue, skb);
+				restore_flags(flags);
+			} else {
+				restore_flags(flags);
+				hsp->tx_skb = skb;
+				hsp->count = 0;
+				sp->hscx_fill_fifo(hsp);
+			}
+			break;
+		case (PH_DATA_PULLED):
+			if (hsp->tx_skb) {
+				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
+				break;
+			}
+			hsp->tx_skb = skb;
+			hsp->count = 0;
+			sp->hscx_fill_fifo(hsp);
+			break;
+		case (PH_REQUEST_PULL):
+			if (!hsp->tx_skb) {
+				st->l1.requestpull = 0;
+				st->l1.l1l2(st, PH_PULL_ACK, NULL);
+			} else
+				st->l1.requestpull = !0;
+			break;
+	}
+
+}
+extern struct IsdnBuffers *tracebuf;
+
+static void
+hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
+		  int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+	struct sk_buff *skb;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
+		return;
+	}
+#endif
+
+	while ((skb = skb_dequeue(&hsp->squeue)))
+		dev_kfree_skb(skb, FREE_WRITE);
+}
+
+static int
+open_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	if (!hsp->init) {
+		if (!(hsp->rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hscx_rcvbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&hsp->rqueue);
+		skb_queue_head_init(&hsp->squeue);
+	}
+	hsp->init = !0;
+
+	hsp->tx_skb = NULL;
+	hsp->event = 0;
+	hsp->rcvidx = 0;
+	hsp->tx_cnt = 0;
+	return (0);
+}
+
+static void
+hscx_manl1(struct PStack *st, int pr,
+	   void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			hsp->active = !0;
+			sp->modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
+			st->l1.l1man(st, PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			if (!hsp->tx_skb)
+				sp->modehscx(hsp, 0, 0);
+
+			hsp->active = 0;
+			break;
+	}
+}
+
+int
+setstack_hscx(struct PStack *st, struct HscxState *hs)
+{
+	if (open_hscxstate(st->l1.hardware, hs->hscx))
+		return (-1);
+
+	st->l1.hscx = hs->hscx;
+	st->l2.l2l1 = hscx_l2l1;
+	st->ma.manl1 = hscx_manl1;
+	st->l2.l2l1discardq = hscx_l2l1discardq;
+
+	st->l1.act_state = 0;
+	st->l1.requestpull = 0;
+
+	hs->st = st;
+	return (0);
+}
+
+void
+HiSax_reportcard(int cardnr)
+{
+	struct IsdnCardState *sp = cards[cardnr].sp;
+
+	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[sp->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", sp->debug);
+	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+	       (ulong) & HiSax_reportcard);
+}
+
+#ifdef L2FRAME_DEBUG		/* psa */
+
+char *
+l2cmd(u_char cmd)
+{
+	switch (cmd & ~0x10) {
+		case 1:
+			return "RR";
+		case 5:
+			return "RNR";
+		case 9:
+			return "REJ";
+		case 0x6f:
+			return "SABME";
+		case 0x0f:
+			return "DM";
+		case 3:
+			return "UI";
+		case 0x43:
+			return "DISC";
+		case 0x63:
+			return "UA";
+		case 0x87:
+			return "FRMR";
+		case 0xaf:
+			return "XID";
+		default:
+			if (!(cmd & 1))
+				return "I";
+			else
+				return "invalid command";
+	}
+}
+
+static char tmp[20];
+
+char *
+l2frames(u_char * ptr)
+{
+	switch (ptr[2] & ~0x10) {
+		case 1:
+		case 5:
+		case 9:
+			sprintf(tmp, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+			break;
+		case 0x6f:
+		case 0x0f:
+		case 3:
+		case 0x43:
+		case 0x63:
+		case 0x87:
+		case 0xaf:
+			sprintf(tmp, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+			break;
+		default:
+			if (!(ptr[2] & 1)) {
+				sprintf(tmp, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+				break;
+			} else
+				return "invalid command";
+	}
+
+
+	return tmp;
+}
+
+void
+Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir)
+{
+	char tmp[132];
+	u_char *ptr;
+
+	ptr = skb->data;
+
+	if (ptr[0] & 1 || !(ptr[1] & 1))
+		debugl1(sp, "Addres not LAPD");
+	else {
+		sprintf(tmp, "%s %s: %s%c (sapi %d, tei %d)",
+			(dir ? "<-" : "->"), buf, l2frames(ptr),
+			((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
+		debugl1(sp, tmp);
+	}
+}
+
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov