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
- Lines: 1379
- Date:
Mon Aug 4 17:34:00 1997
- Orig file:
v2.0.30/linux/drivers/isdn/hisax/isdnl1.c
- Orig date:
Wed Dec 31 16:00:00 1969
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