summaryrefslogtreecommitdiffstats
path: root/drivers/isdn/hysdn
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isdn/hysdn')
-rw-r--r--drivers/isdn/hysdn/.cvsignore2
-rw-r--r--drivers/isdn/hysdn/Makefile24
-rw-r--r--drivers/isdn/hysdn/boardergo.c467
-rw-r--r--drivers/isdn/hysdn/boardergo.h117
-rw-r--r--drivers/isdn/hysdn/hysdn_boot.c420
-rw-r--r--drivers/isdn/hysdn/hysdn_defs.h229
-rw-r--r--drivers/isdn/hysdn/hysdn_init.c243
-rw-r--r--drivers/isdn/hysdn/hysdn_net.c378
-rw-r--r--drivers/isdn/hysdn/hysdn_pof.h95
-rw-r--r--drivers/isdn/hysdn/hysdn_procconf.c485
-rw-r--r--drivers/isdn/hysdn/hysdn_procfs.c502
-rw-r--r--drivers/isdn/hysdn/hysdn_proclog.c481
-rw-r--r--drivers/isdn/hysdn/hysdn_sched.c202
-rw-r--r--drivers/isdn/hysdn/ince1pc.h132
14 files changed, 3777 insertions, 0 deletions
diff --git a/drivers/isdn/hysdn/.cvsignore b/drivers/isdn/hysdn/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/drivers/isdn/hysdn/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/drivers/isdn/hysdn/Makefile b/drivers/isdn/hysdn/Makefile
new file mode 100644
index 000000000..626a6deaa
--- /dev/null
+++ b/drivers/isdn/hysdn/Makefile
@@ -0,0 +1,24 @@
+SUB_DIRS :=
+MOD_SUB_DIRS :=
+ALL_SUB_DIRS :=
+
+L_OBJS :=
+LX_OBJS :=
+M_OBJS :=
+MX_OBJS :=
+O_OBJS :=
+OX_OBJS :=
+L_TARGET :=
+O_TARGET :=
+
+ifeq ($(CONFIG_PROC_FS),y)
+ ifeq ($(CONFIG_HYSDN),y)
+ M_OBJS += hysdn.o
+ O_TARGET += hysdn.o
+ O_OBJS += hysdn_procconf.o hysdn_proclog.o boardergo.o hysdn_boot.o hysdn_sched.o hysdn_net.o
+ OX_OBJS += hysdn_init.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c
new file mode 100644
index 000000000..578e4a14d
--- /dev/null
+++ b/drivers/isdn/hysdn/boardergo.c
@@ -0,0 +1,467 @@
+/* $Id: boardergo.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, specific routines for ergo type boards.
+ *
+ * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
+ * DPRAM interface and layout with only minor differences all related
+ * stuff is done here, not in separate modules.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: boardergo.c,v $
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include "hysdn_defs.h"
+#include "boardergo.h"
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/***************************************************/
+/* The cards interrupt handler. Called from system */
+/***************************************************/
+static void
+ergo_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+ hysdn_card *card = dev_id; /* parameter from irq */
+ tErgDpram *dpr;
+ ulong flags;
+ uchar volatile b;
+
+ if (!card)
+ return; /* error -> spurious interrupt */
+ if (!card->irq_enabled)
+ return; /* other device interrupting or irq switched off */
+
+ save_flags(flags);
+ cli(); /* no further irqs allowed */
+
+ if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
+ restore_flags(flags); /* restore old state */
+ return; /* no interrupt requested by E1 */
+ }
+ /* clear any pending ints on the board */
+ dpr = card->dpram;
+ b = dpr->ToPcInt; /* clear for ergo */
+ b |= dpr->ToPcIntMetro; /* same for metro */
+ b |= dpr->ToHyInt; /* and for champ */
+
+ /* start kernel task immediately after leaving all interrupts */
+ if (!card->hw_lock) {
+ queue_task(&card->irq_queue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+ restore_flags(flags);
+} /* ergo_interrupt */
+
+/******************************************************************************/
+/* ergo_irq_bh is the function called by the immediate kernel task list after */
+/* being activated with queue_task and no interrupts active. This task is the */
+/* only one handling data transfer from or to the card after booting. The task */
+/* may be queued from everywhere (interrupts included). */
+/******************************************************************************/
+static void
+ergo_irq_bh(hysdn_card * card)
+{
+ tErgDpram *dpr;
+ int again;
+ ulong flags;
+
+ if (card->state != CARD_STATE_RUN)
+ return; /* invalid call */
+
+ dpr = card->dpram; /* point to DPRAM */
+
+ save_flags(flags);
+ cli();
+ if (card->hw_lock) {
+ restore_flags(flags); /* hardware currently unavailable */
+ return;
+ }
+ card->hw_lock = 1; /* we now lock the hardware */
+
+ do {
+ sti(); /* reenable other ints */
+ again = 0; /* assume loop not to be repeated */
+
+ if (!dpr->ToHyFlag) {
+ /* we are able to send a buffer */
+
+ if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
+ ERG_TO_HY_BUF_SIZE)) {
+ dpr->ToHyFlag = 1; /* enable tx */
+ again = 1; /* restart loop */
+ }
+ } /* we are able to send a buffer */
+ if (dpr->ToPcFlag) {
+ /* a message has arrived for us, handle it */
+
+ if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
+ dpr->ToPcFlag = 0; /* we worked the data */
+ again = 1; /* restart loop */
+ }
+ } /* a message has arrived for us */
+ cli(); /* no further ints */
+ if (again) {
+ dpr->ToHyInt = 1;
+ dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
+ } else
+ card->hw_lock = 0; /* free hardware again */
+ } while (again); /* until nothing more to do */
+
+ restore_flags(flags);
+} /* ergo_irq_bh */
+
+
+/*********************************************************/
+/* stop the card (hardware reset) and disable interrupts */
+/*********************************************************/
+static void
+ergo_stopcard(hysdn_card * card)
+{
+ ulong flags;
+ uchar val;
+
+ hysdn_net_release(card); /* first release the net device if existing */
+ save_flags(flags);
+ cli();
+ val = bytein(card->iobase + PCI9050_INTR_REG); /* get actual value */
+ val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1); /* mask irq */
+ byteout(card->iobase + PCI9050_INTR_REG, val);
+ card->irq_enabled = 0;
+ byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET); /* reset E1 processor */
+ card->state = CARD_STATE_UNUSED;
+ card->err_log_state = ERRLOG_STATE_OFF; /* currently no log active */
+
+ restore_flags(flags);
+} /* ergo_stopcard */
+
+/**************************************************************************/
+/* enable or disable the cards error log. The event is queued if possible */
+/**************************************************************************/
+static void
+ergo_set_errlog_state(hysdn_card * card, int on)
+{
+ ulong flags;
+
+ if (card->state != CARD_STATE_RUN) {
+ card->err_log_state = ERRLOG_STATE_OFF; /* must be off */
+ return;
+ }
+ save_flags(flags);
+ cli();
+
+ if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
+ ((card->err_log_state == ERRLOG_STATE_ON) && on)) {
+ restore_flags(flags);
+ return; /* nothing to do */
+ }
+ if (on)
+ card->err_log_state = ERRLOG_STATE_START; /* request start */
+ else
+ card->err_log_state = ERRLOG_STATE_STOP; /* request stop */
+
+ restore_flags(flags);
+ queue_task(&card->irq_queue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+} /* ergo_set_errlog_state */
+
+/******************************************/
+/* test the cards RAM and return 0 if ok. */
+/******************************************/
+static const char TestText[36] = "This Message is filler, why read it";
+
+static int
+ergo_testram(hysdn_card * card)
+{
+ tErgDpram *dpr = card->dpram;
+
+ memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable)); /* clear all Traps */
+ dpr->ToHyInt = 1; /* E1 INTR state forced */
+
+ memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText));
+ if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText)))
+ return (-1);
+
+ memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText));
+ if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText)))
+ return (-1);
+
+ return (0);
+} /* ergo_testram */
+
+/*****************************************************************************/
+/* this function is intended to write stage 1 boot image to the cards buffer */
+/* this is done in two steps. First the 1024 hi-words are written (offs=0), */
+/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the */
+/* PCI-write-buffers flushed and the card is taken out of reset. */
+/* The function then waits for a reaction of the E1 processor or a timeout. */
+/* Negative return values are interpreted as errors. */
+/*****************************************************************************/
+static int
+ergo_writebootimg(struct HYSDN_CARD *card, uchar * buf, ulong offs)
+{
+ uchar *dst;
+ tErgDpram *dpram;
+ int cnt = (BOOT_IMG_SIZE >> 2); /* number of words to move and swap (byte order!) */
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
+
+ dst = card->dpram; /* pointer to start of DPRAM */
+ dst += (offs + ERG_DPRAM_FILL_SIZE); /* offset in the DPRAM */
+ while (cnt--) {
+ *dst++ = *(buf + 1); /* high byte */
+ *dst++ = *buf; /* low byte */
+ dst += 2; /* point to next longword */
+ buf += 2; /* buffer only filled with words */
+ }
+
+ /* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
+ /* flush the PCI-write-buffer and take the E1 out of reset */
+ if (offs) {
+ memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE); /* fill the DPRAM still not cleared */
+ dpram = card->dpram; /* get pointer to dpram structure */
+ dpram->ToHyNoDpramErrLog = 0xFF; /* write a dpram register */
+ while (!dpram->ToHyNoDpramErrLog); /* reread volatile register to flush PCI */
+
+ byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN); /* start E1 processor */
+ /* the interrupts are still masked */
+
+ sti();
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
+
+ if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write bootldr no answer");
+ return (-ERR_BOOTIMG_FAIL);
+ }
+ } /* start_boot_img */
+ return (0); /* successfull */
+} /* ergo_writebootimg */
+
+/********************************************************************************/
+/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
+/* using the boot spool mechanism. If everything works fine 0 is returned. In */
+/* case of errors a negative error value is returned. */
+/********************************************************************************/
+static int
+ergo_writebootseq(struct HYSDN_CARD *card, uchar * buf, int len)
+{
+ tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
+ uchar *dst;
+ uchar buflen;
+ int nr_write;
+ uchar tmp_rdptr;
+ uchar wr_mirror;
+ int i;
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
+
+ dst = sp->Data; /* point to data in spool structure */
+ buflen = sp->Len; /* maximum len of spooled data */
+ wr_mirror = sp->WrPtr; /* only once read */
+ sti();
+
+ /* try until all bytes written or error */
+ i = 0x1000; /* timeout value */
+ while (len) {
+
+ /* first determine the number of bytes that may be buffered */
+ do {
+ tmp_rdptr = sp->RdPtr; /* first read the pointer */
+ i--; /* decrement timeout */
+ } while (i && (tmp_rdptr != sp->RdPtr)); /* wait for stable pointer */
+
+ if (!i) {
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write boot seq timeout");
+ return (-ERR_BOOTSEQ_FAIL); /* value not stable -> timeout */
+ }
+ if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
+ nr_write += buflen; /* now we got number of free bytes - 1 in buffer */
+
+ if (!nr_write)
+ continue; /* no free bytes in buffer */
+
+ if (nr_write > len)
+ nr_write = len; /* limit if last few bytes */
+ i = 0x1000; /* reset timeout value */
+
+ /* now we know how much bytes we may put in the puffer */
+ len -= nr_write; /* we savely could adjust len before output */
+ while (nr_write--) {
+ *(dst + wr_mirror) = *buf++; /* output one byte */
+ if (++wr_mirror >= buflen)
+ wr_mirror = 0;
+ sp->WrPtr = wr_mirror; /* announce the next byte to E1 */
+ } /* while (nr_write) */
+
+ } /* while (len) */
+
+ return (0);
+} /* ergo_writebootseq */
+
+/***********************************************************************************/
+/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
+/* boot process. If the process has been successfull 0 is returned otherwise a */
+/* negative error code is returned. */
+/***********************************************************************************/
+static int
+ergo_waitpofready(struct HYSDN_CARD *card)
+{
+ tErgDpram *dpr = card->dpram; /* pointer to DPRAM structure */
+ int timecnt = 10000 / 50; /* timeout is 10 secs max. */
+ ulong flags;
+ int msg_size;
+ int i;
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: waiting for pof ready");
+
+ while (timecnt--) {
+ /* wait until timeout */
+
+ if (dpr->ToPcFlag) {
+ /* data has arrived */
+
+ if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
+ (dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
+ (dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
+ ((*(ulong *) dpr->ToPcBuf) != RDY_MAGIC))
+ break; /* an error occured */
+
+ /* Check for additional data delivered during SysReady */
+ msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
+ if (msg_size > 0)
+ if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
+ break;
+
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "ERGO: pof boot success");
+
+ save_flags(flags);
+ cli();
+
+ card->state = CARD_STATE_RUN; /* now card is running */
+ /* enable the cards interrupt */
+ byteout(card->iobase + PCI9050_INTR_REG,
+ bytein(card->iobase + PCI9050_INTR_REG) |
+ (PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
+ card->irq_enabled = 1; /* we are ready to receive interrupts */
+
+ dpr->ToPcFlag = 0; /* reset data indicator */
+ dpr->ToHyInt = 1;
+ dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
+
+ restore_flags(flags);
+ if ((i = hysdn_net_create(card))) {
+ ergo_stopcard(card);
+ card->state = CARD_STATE_BOOTERR;
+ return (i);
+ }
+ return (0); /* success */
+ } /* data has arrived */
+ sti();
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((50 * HZ) / 1000); /* Timeout 50ms */
+ } /* wait until timeout */
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: pof boot ready timeout");
+ return (-ERR_POF_TIMEOUT);
+} /* ergo_waitpofready */
+
+
+
+/************************************************************************************/
+/* release the cards hardware. Before releasing do a interrupt disable and hardware */
+/* reset. Also unmap dpram. */
+/* Use only during module release. */
+/************************************************************************************/
+static void
+ergo_releasehardware(hysdn_card * card)
+{
+ ergo_stopcard(card); /* first stop the card if not already done */
+ free_irq(card->irq, card); /* release interrupt */
+ release_region(card->iobase + PCI9050_INTR_REG, 1); /* release all io ports */
+ release_region(card->iobase + PCI9050_USER_IO, 1);
+ vfree(card->dpram);
+ card->dpram = NULL; /* release shared mem */
+} /* ergo_releasehardware */
+
+
+/*********************************************************************************/
+/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
+/* value is returned. */
+/* Use only during module init. */
+/*********************************************************************************/
+int
+ergo_inithardware(hysdn_card * card)
+{
+ if (check_region(card->iobase + PCI9050_INTR_REG, 1) ||
+ check_region(card->iobase + PCI9050_USER_IO, 1))
+ return (-1); /* ports already in use */
+
+ card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
+ if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE)))
+ return (-1);
+
+ request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN");
+ request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN");
+ ergo_stopcard(card); /* disable interrupts */
+ if (request_irq(card->irq, ergo_interrupt, SA_SHIRQ, "HYSDN", card)) {
+ ergo_releasehardware(card); /* return the aquired hardware */
+ return (-1);
+ }
+ /* success, now setup the function pointers */
+ card->stopcard = ergo_stopcard;
+ card->releasehardware = ergo_releasehardware;
+ card->testram = ergo_testram;
+ card->writebootimg = ergo_writebootimg;
+ card->writebootseq = ergo_writebootseq;
+ card->waitpofready = ergo_waitpofready;
+ card->set_errlog_state = ergo_set_errlog_state;
+ card->irq_queue.next = 0;
+ card->irq_queue.sync = 0;
+ card->irq_queue.data = card; /* init task queue for interrupt */
+ card->irq_queue.routine = (void *) (void *) ergo_irq_bh;
+
+ return (0);
+} /* ergo_inithardware */
diff --git a/drivers/isdn/hysdn/boardergo.h b/drivers/isdn/hysdn/boardergo.h
new file mode 100644
index 000000000..0e2c3f678
--- /dev/null
+++ b/drivers/isdn/hysdn/boardergo.h
@@ -0,0 +1,117 @@
+/* $Id: boardergo.h,v 1.1 2000/02/10 19:44:30 werner Exp $
+
+ * Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: boardergo.h,v $
+ * Revision 1.1 2000/02/10 19:44:30 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+
+/************************************************/
+/* defines for the dual port memory of the card */
+/************************************************/
+#define ERG_DPRAM_PAGE_SIZE 0x2000 /* DPRAM occupies a 8K page */
+#define BOOT_IMG_SIZE 4096
+#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
+
+#define ERG_TO_HY_BUF_SIZE 0x0E00 /* 3072 bytes buffer size to card */
+#define ERG_TO_PC_BUF_SIZE 0x0E00 /* 3072 bytes to PC, too */
+
+/* following DPRAM layout copied from OS2-driver boarderg.h */
+typedef struct ErgDpram_tag {
+/*0000 */ uchar ToHyBuf[ERG_TO_HY_BUF_SIZE];
+/*0E00 */ uchar ToPcBuf[ERG_TO_PC_BUF_SIZE];
+
+ /*1C00 */ uchar bSoftUart[SIZE_RSV_SOFT_UART];
+ /* size 0x1B0 */
+
+ /*1DB0 *//* tErrLogEntry */ uchar volatile ErrLogMsg[64];
+ /* size 64 bytes */
+ /*1DB0 ulong ulErrType; */
+ /*1DB4 ulong ulErrSubtype; */
+ /*1DB8 ulong ucTextSize; */
+ /*1DB9 ulong ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
+ /*1DF0 */
+
+/*1DF0 */ word volatile ToHyChannel;
+/*1DF2 */ word volatile ToHySize;
+ /*1DF4 */ uchar volatile ToHyFlag;
+ /* !=0: msg for Hy waiting */
+ /*1DF5 */ uchar volatile ToPcFlag;
+ /* !=0: msg for PC waiting */
+/*1DF6 */ word volatile ToPcChannel;
+/*1DF8 */ word volatile ToPcSize;
+ /*1DFA */ uchar bRes1DBA[0x1E00 - 0x1DFA];
+ /* 6 bytes */
+
+/*1E00 */ uchar bRestOfEntryTbl[0x1F00 - 0x1E00];
+/*1F00 */ ulong TrapTable[62];
+ /*1FF8 */ uchar bRes1FF8[0x1FFB - 0x1FF8];
+ /* low part of reset vetor */
+/*1FFB */ uchar ToPcIntMetro;
+ /* notes:
+ * - metro has 32-bit boot ram - accessing
+ * ToPcInt and ToHyInt would be the same;
+ * so we moved ToPcInt to 1FFB.
+ * Because on the PC side both vars are
+ * readonly (reseting on int from E1 to PC),
+ * we can read both vars on both cards
+ * without destroying anything.
+ * - 1FFB is the high byte of the reset vector,
+ * so E1 side should NOT change this byte
+ * when writing!
+ */
+/*1FFC */ uchar volatile ToHyNoDpramErrLog;
+ /* note: ToHyNoDpramErrLog is used to inform
+ * boot loader, not to use DPRAM based
+ * ErrLog; when DOS driver is rewritten
+ * this becomes obsolete
+ */
+/*1FFD */ uchar bRes1FFD;
+ /*1FFE */ uchar ToPcInt;
+ /* E1_intclear; on CHAMP2: E1_intset */
+ /*1FFF */ uchar ToHyInt;
+ /* E1_intset; on CHAMP2: E1_intclear */
+} tErgDpram;
+
+/**********************************************/
+/* PCI9050 controller local register offsets: */
+/* copied from boarderg.c */
+/**********************************************/
+#define PCI9050_INTR_REG 0x4C /* Interrupt register */
+#define PCI9050_USER_IO 0x51 /* User I/O register */
+
+ /* bitmask for PCI9050_INTR_REG: */
+#define PCI9050_INTR_REG_EN1 0x01 /* 1= enable (def.), 0= disable */
+#define PCI9050_INTR_REG_POL1 0x02 /* 1= active high (def.), 0= active low */
+#define PCI9050_INTR_REG_STAT1 0x04 /* 1= intr. active, 0= intr. not active (def.) */
+#define PCI9050_INTR_REG_ENPCI 0x40 /* 1= PCI interrupts enable (def.) */
+
+ /* bitmask for PCI9050_USER_IO: */
+#define PCI9050_USER_IO_EN3 0x02 /* 1= disable , 0= enable (def.) */
+#define PCI9050_USER_IO_DIR3 0x04 /* 1= output (def.), 0= input */
+#define PCI9050_USER_IO_DAT3 0x08 /* 1= high (def.) , 0= low */
+
+#define PCI9050_E1_RESET ( PCI9050_USER_IO_DIR3) /* 0x04 */
+#define PCI9050_E1_RUN (PCI9050_USER_IO_DAT3|PCI9050_USER_IO_DIR3) /* 0x0C */
diff --git a/drivers/isdn/hysdn/hysdn_boot.c b/drivers/isdn/hysdn/hysdn_boot.c
new file mode 100644
index 000000000..030c40de6
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_boot.c
@@ -0,0 +1,420 @@
+/* $Id: hysdn_boot.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, specific routines for booting and pof handling.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: hysdn_boot.c,v $
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/vmalloc.h>
+#include <linux/malloc.h>
+#include <asm/uaccess.h>
+
+#include "hysdn_defs.h"
+#include "hysdn_pof.h"
+
+/********************************/
+/* defines for pof read handler */
+/********************************/
+#define POF_READ_FILE_HEAD 0
+#define POF_READ_TAG_HEAD 1
+#define POF_READ_TAG_DATA 2
+
+/************************************************************/
+/* definition of boot specific data area. This data is only */
+/* needed during boot and so allocated dynamically. */
+/************************************************************/
+struct boot_data {
+ word Cryptor; /* for use with Decrypt function */
+ word Nrecs; /* records remaining in file */
+ uchar pof_state; /* actual state of read handler */
+ uchar is_crypted; /* card data is crypted */
+ int BufSize; /* actual number of bytes bufferd */
+ int last_error; /* last occured error */
+ word pof_recid; /* actual pof recid */
+ ulong pof_reclen; /* total length of pof record data */
+ ulong pof_recoffset; /* actual offset inside pof record */
+ union {
+ uchar BootBuf[BOOT_BUF_SIZE]; /* buffer as byte count */
+ tPofRecHdr PofRecHdr; /* header for actual record/chunk */
+ tPofFileHdr PofFileHdr; /* header from POF file */
+ tPofTimeStamp PofTime; /* time information */
+ } buf;
+};
+
+/*****************************************************/
+/* start decryption of sucessive POF file chuncks. */
+/* */
+/* to be called at start of POF file reading, */
+/* before starting any decryption on any POF record. */
+/*****************************************************/
+void
+StartDecryption(struct boot_data *boot)
+{
+ boot->Cryptor = CRYPT_STARTTERM;
+} /* StartDecryption */
+
+
+/***************************************************************/
+/* decrypt complete BootBuf */
+/* NOTE: decryption must be applied to all or none boot tags - */
+/* to HI and LO boot loader and (all) seq tags, because */
+/* global Cryptor is started for whole POF. */
+/***************************************************************/
+void
+DecryptBuf(struct boot_data *boot, int cnt)
+{
+ uchar *bufp = boot->buf.BootBuf;
+
+ while (cnt--) {
+ boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
+ *bufp++ ^= (uchar) boot->Cryptor;
+ }
+} /* DecryptBuf */
+
+/********************************************************************************/
+/* pof_handle_data executes the required actions dependant on the active record */
+/* id. If successfull 0 is returned, a negative value shows an error. */
+/********************************************************************************/
+static int
+pof_handle_data(hysdn_card * card, int datlen)
+{
+ struct boot_data *boot = card->boot; /* pointer to boot specific data */
+ long l;
+ uchar *imgp;
+ int img_len;
+
+ /* handle the different record types */
+ switch (boot->pof_recid) {
+
+ case TAG_TIMESTMP:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
+ break;
+
+ case TAG_CBOOTDTA:
+ DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
+ case TAG_BOOTDTA:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+ (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
+ datlen, boot->pof_recoffset);
+
+ if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
+ boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */
+ return (boot->last_error);
+ }
+ imgp = boot->buf.BootBuf; /* start of buffer */
+ img_len = datlen; /* maximum length to transfer */
+
+ l = POF_BOOT_LOADER_OFF_IN_PAGE -
+ (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
+ if (l > 0) {
+ /* buffer needs to be truncated */
+ imgp += l; /* advance pointer */
+ img_len -= l; /* adjust len */
+ }
+ /* at this point no special handling for data wrapping over buffer */
+ /* is necessary, because the boot image always will be adjusted to */
+ /* match a page boundary inside the buffer. */
+ /* The buffer for the boot image on the card is filled in 2 cycles */
+ /* first the 1024 hi-words are put in the buffer, then the low 1024 */
+ /* word are handled in the same way with different offset. */
+
+ if (img_len > 0) {
+ /* data available for copy */
+ if ((boot->last_error =
+ card->writebootimg(card, imgp,
+ (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
+ return (boot->last_error);
+ }
+ break; /* end of case boot image hi/lo */
+
+ case TAG_CABSDATA:
+ DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
+ case TAG_ABSDATA:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+ (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
+ datlen, boot->pof_recoffset);
+
+ if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0))
+ return (boot->last_error); /* error writing data */
+
+ if (boot->pof_recoffset + datlen >= boot->pof_reclen)
+ return (card->waitpofready(card)); /* data completely spooled, wait for ready */
+
+ break; /* end of case boot seq data */
+
+ default:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
+ datlen, boot->pof_recoffset);
+
+ break; /* simply skip record */
+ } /* switch boot->pof_recid */
+
+ return (0);
+} /* pof_handle_data */
+
+
+/******************************************************************************/
+/* pof_write_buffer is called when the buffer has been filled with the needed */
+/* number of data bytes. The number delivered is additionally supplied for */
+/* verification. The functions handles the data and returns the needed number */
+/* of bytes for the next action. If the returned value is 0 or less an error */
+/* occured and booting must be aborted. */
+/******************************************************************************/
+int
+pof_write_buffer(hysdn_card * card, int datlen)
+{
+ struct boot_data *boot = card->boot; /* pointer to boot specific data */
+
+ if (!boot)
+ return (-EFAULT); /* invalid call */
+ if (boot->last_error < 0)
+ return (boot->last_error); /* repeated error */
+
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: got %d bytes ", datlen);
+
+ switch (boot->pof_state) {
+ case POF_READ_FILE_HEAD:
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: checking file header");
+
+ if (datlen != sizeof(tPofFileHdr)) {
+ boot->last_error = -EPOF_INTERNAL;
+ break;
+ }
+ if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
+ boot->last_error = -EPOF_BAD_MAGIC;
+ break;
+ }
+ /* Setup the new state and vars */
+ boot->Nrecs = (word) (boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
+ boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
+ boot->last_error = sizeof(tPofRecHdr); /* new length */
+ break;
+
+ case POF_READ_TAG_HEAD:
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: checking tag header");
+
+ if (datlen != sizeof(tPofRecHdr)) {
+ boot->last_error = -EPOF_INTERNAL;
+ break;
+ }
+ boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */
+ boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */
+ boot->pof_recoffset = 0; /* no starting offset */
+
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
+ boot->pof_recid, boot->pof_reclen);
+
+ boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */
+ if (boot->pof_reclen < BOOT_BUF_SIZE)
+ boot->last_error = boot->pof_reclen; /* limit size */
+ else
+ boot->last_error = BOOT_BUF_SIZE; /* maximum */
+
+ if (!boot->last_error) { /* no data inside record */
+ boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
+ boot->last_error = sizeof(tPofRecHdr); /* new length */
+ }
+ break;
+
+ case POF_READ_TAG_DATA:
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: getting tag data");
+
+ if (datlen != boot->last_error) {
+ boot->last_error = -EPOF_INTERNAL;
+ break;
+ }
+ if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
+ return (boot->last_error); /* an error occured */
+
+ boot->pof_recoffset += datlen;
+ if (boot->pof_recoffset >= boot->pof_reclen) {
+ boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
+ boot->last_error = sizeof(tPofRecHdr); /* new length */
+ } else {
+ if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
+ boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */
+ else
+ boot->last_error = BOOT_BUF_SIZE; /* maximum */
+ }
+ break;
+
+ default:
+ boot->last_error = -EPOF_INTERNAL; /* unknown state */
+ break;
+ } /* switch (boot->pof_state) */
+
+ return (boot->last_error);
+} /* pof_write_buffer */
+
+
+/*******************************************************************************/
+/* pof_write_open is called when an open for boot on the cardlog device occurs. */
+/* The function returns the needed number of bytes for the next operation. If */
+/* the returned number is less or equal 0 an error specified by this code */
+/* occurred. Additionally the pointer to the buffer data area is set on success */
+/*******************************************************************************/
+int
+pof_write_open(hysdn_card * card, uchar ** bufp)
+{
+ struct boot_data *boot; /* pointer to boot specific data */
+
+ if (card->boot) {
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF open: already opened for boot");
+ return (-ERR_ALREADY_BOOT); /* boot already active */
+ }
+ /* error no mem available */
+ if (!(boot = kmalloc(sizeof(struct boot_data), GFP_KERNEL))) {
+ if (card->debug_flags & LOG_MEM_ERR)
+ hysdn_addlog(card, "POF open: unable to allocate mem");
+ return (-EFAULT);
+ }
+ card->boot = boot;
+ card->state = CARD_STATE_BOOTING;
+ memset(boot, 0, sizeof(struct boot_data));
+
+ card->stopcard(card); /* first stop the card */
+ if (card->testram(card)) {
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF open: DPRAM test failure");
+ boot->last_error = -ERR_BOARD_DPRAM;
+ card->state = CARD_STATE_BOOTERR; /* show boot error */
+ return (boot->last_error);
+ }
+ boot->BufSize = 0; /* Buffer is empty */
+ boot->pof_state = POF_READ_FILE_HEAD; /* read file header */
+ StartDecryption(boot); /* if POF File should be encrypted */
+
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF open: success");
+
+ *bufp = boot->buf.BootBuf; /* point to buffer */
+ return (sizeof(tPofFileHdr));
+} /* pof_write_open */
+
+/********************************************************************************/
+/* pof_write_close is called when an close of boot on the cardlog device occurs. */
+/* The return value must be 0 if everything has happened as desired. */
+/********************************************************************************/
+int
+pof_write_close(hysdn_card * card)
+{
+ struct boot_data *boot = card->boot; /* pointer to boot specific data */
+
+ if (!boot)
+ return (-EFAULT); /* invalid call */
+
+ card->boot = NULL; /* no boot active */
+ kfree(boot);
+
+ if (card->state == CARD_STATE_RUN)
+ card->set_errlog_state(card, 1); /* activate error log */
+
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF close: success");
+
+ return (0);
+} /* pof_write_close */
+
+/*********************************************************************************/
+/* EvalSysrTokData checks additional records delivered with the Sysready Message */
+/* when POF has been booted. A return value of 0 is used if no error occured. */
+/*********************************************************************************/
+int
+EvalSysrTokData(hysdn_card * card, uchar * cp, int len)
+{
+ u_char *p;
+ u_char crc;
+
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "SysReady Token data length %d", len);
+
+ if (len < 2) {
+ hysdn_addlog(card, "SysReady Token Data to short");
+ return (1);
+ }
+ for (p = cp, crc = 0; p < (cp + len - 2); p++)
+ if ((crc & 0x80))
+ crc = (((u_char) (crc << 1)) + 1) + *p;
+ else
+ crc = ((u_char) (crc << 1)) + *p;
+ crc = ~crc;
+ if (crc != *(cp + len - 1)) {
+ hysdn_addlog(card, "SysReady Token Data invalid CRC");
+ return (1);
+ }
+ len--; /* dont check CRC byte */
+ while (len > 0) {
+
+ if (*cp == SYSR_TOK_END)
+ return (0); /* End of Token stream */
+
+ if (len < (*(cp + 1) + 2)) {
+ hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
+ return (1);
+ }
+ switch (*cp) {
+ case SYSR_TOK_B_CHAN: /* 1 */
+ if (*(cp + 1) != 1)
+ return (1); /* length invalid */
+ card->bchans = *(cp + 2);
+ break;
+
+ case SYSR_TOK_FAX_CHAN: /* 2 */
+ if (*(cp + 1) != 1)
+ return (1); /* length invalid */
+ card->faxchans = *(cp + 2);
+ break;
+
+ case SYSR_TOK_MAC_ADDR: /* 3 */
+ if (*(cp + 1) != 6)
+ return (1); /* length invalid */
+ memcpy(card->mac_addr, cp + 2, 6);
+ break;
+
+ default:
+ hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
+ break;
+ }
+ len -= (*(cp + 1) + 2); /* adjust len */
+ cp += (*(cp + 1) + 2); /* and pointer */
+ }
+
+ hysdn_addlog(card, "no end token found");
+ return (1);
+} /* EvalSysrTokData */
diff --git a/drivers/isdn/hysdn/hysdn_defs.h b/drivers/isdn/hysdn/hysdn_defs.h
new file mode 100644
index 000000000..cac76cb26
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_defs.h
@@ -0,0 +1,229 @@
+/* $Id: hysdn_defs.h,v 1.1 2000/02/10 19:44:30 werner Exp $
+
+ * Linux driver for HYSDN cards, global definitions and exported vars and functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: hysdn_defs.h,v $
+ * Revision 1.1 2000/02/10 19:44:30 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#include <linux/hysdn_if.h>
+#include <linux/interrupt.h>
+#include <linux/tqueue.h>
+#include <linux/skbuff.h>
+
+/****************************/
+/* storage type definitions */
+/****************************/
+#define uchar unsigned char
+#define uint unsigned int
+#define ulong unsigned long
+#define word unsigned short
+
+#include "ince1pc.h"
+
+/************************************************/
+/* constants and bits for debugging/log outputs */
+/************************************************/
+#define LOG_MAX_LINELEN 120
+#define DEB_OUT_SYSLOG 0x80000000 /* output to syslog instead of proc fs */
+#define LOG_MEM_ERR 0x00000001 /* log memory errors like kmalloc failure */
+#define LOG_POF_OPEN 0x00000010 /* log pof open and close activities */
+#define LOG_POF_RECORD 0x00000020 /* log pof record parser */
+#define LOG_POF_WRITE 0x00000040 /* log detailed pof write operation */
+#define LOG_POF_CARD 0x00000080 /* log pof related card functions */
+#define LOG_CNF_LINE 0x00000100 /* all conf lines are put to procfs */
+#define LOG_CNF_DATA 0x00000200 /* non comment conf lines are shown with channel */
+#define LOG_CNF_MISC 0x00000400 /* additional conf line debug outputs */
+#define LOG_SCHED_ASYN 0x00001000 /* debug schedulers async tx routines */
+#define LOG_PROC_OPEN 0x00100000 /* open and close from procfs are logged */
+#define LOG_PROC_ALL 0x00200000 /* all actions from procfs are logged */
+#define LOG_NET_INIT 0x00010000 /* network init and deinit logging */
+
+#define DEF_DEB_FLAGS 0x7fff000f /* everything is logged to procfs */
+
+/**********************************/
+/* proc filesystem name constants */
+/**********************************/
+#define PROC_SUBDIR_NAME "hysdn"
+#define PROC_CONF_BASENAME "cardconf"
+#define PROC_LOG_BASENAME "cardlog"
+
+/************************/
+/* PCI constant defines */
+/************************/
+#define PCI_VENDOR_ID_HYPERCOPE 0x1365
+#define PCI_DEVICE_ID_PLX 0x9050 /* all DPRAM cards use the same id */
+
+/*****************************/
+/* sub ids determining cards */
+/*****************************/
+#define PCI_SUB_ID_OLD_ERGO 0x0104
+#define PCI_SUB_ID_ERGO 0x0106
+#define PCI_SUB_ID_METRO 0x0107
+#define PCI_SUB_ID_CHAMP2 0x0108
+#define PCI_SUB_ID_PLEXUS 0x0109
+
+/***********************************/
+/* PCI 32 bit parms for IO and MEM */
+/***********************************/
+#define PCI_REG_PLX_MEM_BASE 0
+#define PCI_REG_PLX_IO_BASE 1
+#define PCI_REG_MEMORY_BASE 3
+
+/**************/
+/* card types */
+/**************/
+#define BD_NONE 0U
+#define BD_PERFORMANCE 1U
+#define BD_VALUE 2U
+#define BD_PCCARD 3U
+#define BD_ERGO 4U
+#define BD_METRO 5U
+#define BD_CHAMP2 6U
+#define BD_PLEXUS 7U
+
+/******************************************************/
+/* defined states for cards shown by reading cardconf */
+/******************************************************/
+#define CARD_STATE_UNUSED 0 /* never been used or booted */
+#define CARD_STATE_BOOTING 1 /* booting is in progress */
+#define CARD_STATE_BOOTERR 2 /* a previous boot was aborted */
+#define CARD_STATE_RUN 3 /* card is active */
+
+/*******************************/
+/* defines for error_log_state */
+/*******************************/
+#define ERRLOG_STATE_OFF 0 /* error log is switched off, nothing to do */
+#define ERRLOG_STATE_ON 1 /* error log is switched on, wait for data */
+#define ERRLOG_STATE_START 2 /* start error logging */
+#define ERRLOG_STATE_STOP 3 /* stop error logging */
+
+/*******************************/
+/* data structure for one card */
+/*******************************/
+typedef struct HYSDN_CARD {
+
+ /* general variables for the cards */
+ int myid; /* own driver card id */
+ uchar bus; /* pci bus the card is connected to */
+ uchar devfn; /* slot+function bit encoded */
+ word subsysid; /* PCI subsystem id */
+ uchar brdtype; /* type of card */
+ uint bchans; /* number of available B-channels */
+ uint faxchans; /* number of available fax-channels */
+ uchar mac_addr[6]; /* MAC Address read from card */
+ uint irq; /* interrupt number */
+ uint iobase; /* IO-port base address */
+ ulong plxbase; /* PLX memory base */
+ ulong membase; /* DPRAM memory base */
+ ulong memend; /* DPRAM memory end */
+ void *dpram; /* mapped dpram */
+ int state; /* actual state of card -> CARD_STATE_** */
+ struct HYSDN_CARD *next; /* pointer to next card */
+
+ /* data areas for the /proc file system */
+ void *proclog; /* pointer to proclog filesystem specific data */
+ void *procconf; /* pointer to procconf filesystem specific data */
+
+ /* debugging and logging */
+ uchar err_log_state; /* actual error log state of the card */
+ ulong debug_flags; /* tells what should be debugged and where */
+ void (*set_errlog_state) (struct HYSDN_CARD *, int);
+
+ /* interrupt handler + interrupt synchronisation */
+ struct tq_struct irq_queue; /* interrupt task queue */
+ uchar volatile irq_enabled; /* interrupt enabled if != 0 */
+ uchar volatile hw_lock; /* hardware is currently locked -> no access */
+
+ /* boot process */
+ void *boot; /* pointer to boot private data */
+ int (*writebootimg) (struct HYSDN_CARD *, uchar *, ulong);
+ int (*writebootseq) (struct HYSDN_CARD *, uchar *, int);
+ int (*waitpofready) (struct HYSDN_CARD *);
+ int (*testram) (struct HYSDN_CARD *);
+
+ /* scheduler for data transfer (only async parts) */
+ uchar async_data[256]; /* async data to be sent (normally for config) */
+ word volatile async_len; /* length of data to sent */
+ word volatile async_channel; /* channel number for async transfer */
+ int volatile async_busy; /* flag != 0 sending in progress */
+ int volatile net_tx_busy; /* a network packet tx is in progress */
+
+ /* network interface */
+ void *netif; /* pointer to network structure */
+
+ /* init and deinit stopcard for booting, too */
+ void (*stopcard) (struct HYSDN_CARD *);
+ void (*releasehardware) (struct HYSDN_CARD *);
+} hysdn_card;
+
+
+/*****************/
+/* exported vars */
+/*****************/
+extern int cardmax; /* number of found cards */
+extern hysdn_card *card_root; /* pointer to first card */
+
+
+
+/*************************/
+/* im/exported functions */
+/*************************/
+extern int printk(const char *fmt,...);
+extern char *hysdn_getrev(const char *);
+
+/* hysdn_procconf.c */
+extern int hysdn_procconf_init(void); /* init proc config filesys */
+extern void hysdn_procconf_release(void); /* deinit proc config filesys */
+
+/* hysdn_proclog.c */
+extern int hysdn_proclog_init(hysdn_card *); /* init proc log entry */
+extern void hysdn_proclog_release(hysdn_card *); /* deinit proc log entry */
+extern void put_log_buffer(hysdn_card *, char *); /* output log data */
+extern void hysdn_addlog(hysdn_card *, char *,...); /* output data to log */
+extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int); /* output card log */
+
+/* boardergo.c */
+extern int ergo_inithardware(hysdn_card * card); /* get hardware -> module init */
+
+/* hysdn_boot.c */
+extern int pof_write_close(hysdn_card *); /* close proc file after writing pof */
+extern int pof_write_open(hysdn_card *, uchar **); /* open proc file for writing pof */
+extern int pof_write_buffer(hysdn_card *, int); /* write boot data to card */
+extern int EvalSysrTokData(hysdn_card *, uchar *, int); /* Check Sysready Token Data */
+
+/* hysdn_sched.c */
+extern int hysdn_sched_tx(hysdn_card *, uchar *, word volatile *, word volatile *,
+ word);
+extern int hysdn_sched_rx(hysdn_card *, uchar *, word, word);
+extern int hysdn_tx_cfgline(hysdn_card *, uchar *, word); /* send one cfg line */
+
+/* hysdn_net.c */
+extern char *hysdn_net_revision;
+extern int hysdn_net_create(hysdn_card *); /* create a new net device */
+extern int hysdn_net_release(hysdn_card *); /* delete the device */
+extern char *hysdn_net_getname(hysdn_card *); /* get name of net interface */
+extern void hysdn_tx_netack(hysdn_card *); /* acknowledge a packet tx */
+extern struct sk_buff *hysdn_tx_netget(hysdn_card *); /* get next network packet */
+extern void hysdn_rx_netpkt(hysdn_card *, uchar *, word); /* rxed packet from network */
diff --git a/drivers/isdn/hysdn/hysdn_init.c b/drivers/isdn/hysdn/hysdn_init.c
new file mode 100644
index 000000000..eb3fed4d3
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_init.c
@@ -0,0 +1,243 @@
+/* $Id: hysdn_init.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, init functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: hysdn_init.c,v $
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_init_revision = "$Revision: 1.1 $";
+int cardmax; /* number of found cards */
+hysdn_card *card_root = NULL; /* pointer to first card */
+
+/**********************************************/
+/* table assigning PCI-sub ids to board types */
+/* the last entry contains all 0 */
+/**********************************************/
+static struct {
+ word subid; /* PCI sub id */
+ uchar cardtyp; /* card type assigned */
+} pci_subid_map[] = {
+
+ {
+ PCI_SUB_ID_METRO, BD_METRO
+ },
+ {
+ PCI_SUB_ID_CHAMP2, BD_CHAMP2
+ },
+ {
+ PCI_SUB_ID_ERGO, BD_ERGO
+ },
+ {
+ PCI_SUB_ID_OLD_ERGO, BD_ERGO
+ },
+ {
+ 0, 0
+ } /* terminating entry */
+};
+
+/*********************************************************************/
+/* search_cards searches for available cards in the pci config data. */
+/* If a card is found, the card structure is allocated and the cards */
+/* ressources are reserved. cardmax is incremented. */
+/*********************************************************************/
+static void
+search_cards(void)
+{
+ struct pci_dev *akt_pcidev = NULL;
+ hysdn_card *card, *card_last;
+ uchar irq;
+ int i;
+
+ card_root = NULL;
+ card_last = NULL;
+ while ((akt_pcidev = pci_find_device(PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_PLX,
+ akt_pcidev)) != NULL) {
+
+ if (!(card = kmalloc(sizeof(hysdn_card), GFP_KERNEL))) {
+ printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
+ return;
+ }
+ memset(card, 0, sizeof(hysdn_card));
+ card->myid = cardmax; /* set own id */
+ card->bus = akt_pcidev->bus->number;
+ card->devfn = akt_pcidev->devfn; /* slot + function */
+ pcibios_read_config_word(card->bus, card->devfn, PCI_SUBSYSTEM_ID, &card->subsysid);
+ pcibios_read_config_byte(card->bus, card->devfn, PCI_INTERRUPT_LINE, &irq);
+ card->irq = irq;
+ card->iobase = akt_pcidev->resource[ PCI_REG_PLX_IO_BASE].start & PCI_BASE_ADDRESS_IO_MASK;
+ card->plxbase = akt_pcidev->resource[ PCI_REG_PLX_MEM_BASE].start;
+ card->membase = akt_pcidev->resource[ PCI_REG_MEMORY_BASE].start;
+ card->brdtype = BD_NONE; /* unknown */
+ card->debug_flags = DEF_DEB_FLAGS; /* set default debug */
+ card->faxchans = 0; /* default no fax channels */
+ card->bchans = 2; /* and 2 b-channels */
+ for (i = 0; pci_subid_map[i].subid; i++)
+ if (pci_subid_map[i].subid == card->subsysid) {
+ card->brdtype = pci_subid_map[i].cardtyp;
+ break;
+ }
+ if (card->brdtype != BD_NONE) {
+ if (ergo_inithardware(card)) {
+ printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
+ kfree(card);
+ continue;
+ }
+ } else {
+ printk(KERN_WARNING "HYSDN: unknown card id 0x%04x\n", card->subsysid);
+ kfree(card); /* release mem */
+ continue;
+ }
+ cardmax++;
+ card->next = NULL; /*end of chain */
+ if (card_last)
+ card_last->next = card; /* pointer to next card */
+ else
+ card_root = card;
+ card_last = card; /* new chain end */
+ } /* device found */
+} /* search_cards */
+
+/************************************************************************************/
+/* free_resources frees the acquired PCI resources and returns the allocated memory */
+/************************************************************************************/
+static void
+free_resources(void)
+{
+ hysdn_card *card;
+
+ while (card_root) {
+ card = card_root;
+ if (card->releasehardware)
+ card->releasehardware(card); /* free all hardware resources */
+ card_root = card_root->next; /* remove card from chain */
+ kfree(card); /* return mem */
+
+ } /* while card_root */
+} /* free_resources */
+
+/**************************************************************************/
+/* stop_cards disables (hardware resets) all cards and disables interrupt */
+/**************************************************************************/
+static void
+stop_cards(void)
+{
+ hysdn_card *card;
+
+ card = card_root; /* first in chain */
+ while (card) {
+ if (card->stopcard)
+ card->stopcard(card);
+ card = card->next; /* remove card from chain */
+ } /* while card */
+} /* stop_cards */
+
+
+/****************************************************************************/
+/* The module startup and shutdown code. Only compiled when used as module. */
+/* Using the driver as module is always advisable, because the booting */
+/* image becomes smaller and the driver code is only loaded when needed. */
+/* Additionally newer versions may be activated without rebooting. */
+/****************************************************************************/
+#ifdef CONFIG_MODULES
+
+/******************************************************/
+/* extract revision number from string for log output */
+/******************************************************/
+char *
+hysdn_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "???";
+ return rev;
+}
+
+
+/****************************************************************************/
+/* init_module is called once when the module is loaded to do all necessary */
+/* things like autodetect... */
+/* If the return value of this function is 0 the init has been successfull */
+/* and the module is added to the list in /proc/modules, otherwise an error */
+/* is assumed and the module will not be kept in memory. */
+/****************************************************************************/
+int
+init_module(void)
+{
+ char tmp[50];
+
+ strcpy(tmp, hysdn_init_revision);
+ printk(KERN_NOTICE "HYSDN: module Rev: %s loaded\n", hysdn_getrev(tmp));
+ strcpy(tmp, hysdn_net_revision);
+ printk(KERN_NOTICE "HYSDN: network interface Rev: %s \n", hysdn_getrev(tmp));
+ if (!pci_present()) {
+ printk(KERN_ERR "HYSDN: no PCI bus present, module not loaded\n");
+ return (-1);
+ }
+ search_cards();
+ printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
+
+ if (hysdn_procconf_init()) {
+ free_resources(); /* proc file_sys not created */
+ return (-1);
+ }
+ return (0); /* no error */
+} /* init_module */
+
+
+/***********************************************************************/
+/* cleanup_module is called when the module is released by the kernel. */
+/* The routine is only called if init_module has been successfull and */
+/* the module counter has a value of 0. Otherwise this function will */
+/* not be called. This function must release all resources still allo- */
+/* cated as after the return from this function the module code will */
+/* be removed from memory. */
+/***********************************************************************/
+void
+cleanup_module(void)
+{
+
+ stop_cards();
+ hysdn_procconf_release();
+ free_resources();
+ printk(KERN_NOTICE "HYSDN: module unloaded\n");
+} /* cleanup_module */
+
+#endif /* CONFIG_MODULES */
diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
new file mode 100644
index 000000000..1a3d21012
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_net.c
@@ -0,0 +1,378 @@
+/* $Id: hysdn_net.c,v 1.3 2000/02/14 19:24:12 werner Exp $
+
+ * Linux driver for HYSDN cards, net (ethernet type) handling routines.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This net module has been inspired by the skeleton driver from
+ * Donald Becker (becker@CESDIS.gsfc.nasa.gov)
+ *
+ * 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.
+ *
+ * $Log: hysdn_net.c,v $
+ * Revision 1.3 2000/02/14 19:24:12 werner
+ *
+ * Removed superflous file
+ *
+ * Revision 1.2 2000/02/13 17:32:19 werner
+ *
+ * Added support for new network layer of 2.3.43 and 44 kernels and tested driver.
+ *
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inetdevice.h>
+
+#include "hysdn_defs.h"
+
+/* store the actual version for log reporting */
+char *hysdn_net_revision = "$Revision: 1.3 $";
+
+#define MAX_SKB_BUFFERS 20 /* number of buffers for keeping TX-data */
+
+/****************************************************************************/
+/* structure containing the complete network data. The structure is aligned */
+/* in a way that both, the device and statistics are kept inside it. */
+/* for proper access, the device structure MUST be the first var/struct */
+/* inside the definition. */
+/****************************************************************************/
+struct net_local {
+ struct net_device netdev; /* the network device */
+ struct net_device_stats stats;
+ /* additional vars may be added here */
+ char dev_name[9]; /* our own device name */
+
+ /* Tx control lock. This protects the transmit buffer ring
+ * state along with the "tx full" state of the driver. This
+ * means all netif_queue flow control actions are protected
+ * by this lock as well.
+ */
+ spinlock_t lock;
+ struct sk_buff *skbs[MAX_SKB_BUFFERS]; /* pointers to tx-skbs */
+ int in_idx, out_idx; /* indexes to buffer ring */
+ int sk_count; /* number of buffers currently in ring */
+
+ int is_open; /* flag controlling module locking */
+}; /* net_local */
+
+
+/*****************************************************/
+/* Get the current statistics for this card. */
+/* This may be called with the card open or closed ! */
+/*****************************************************/
+static struct net_device_stats *
+net_get_stats(struct net_device *dev)
+{
+ return (&((struct net_local *) dev)->stats);
+} /* net_device_stats */
+
+/*********************************************************************/
+/* Open/initialize the board. This is called (in the current kernel) */
+/* sometime after booting when the 'ifconfig' program is run. */
+/* This routine should set everything up anew at each open, even */
+/* registers that "should" only need to be set once at boot, so that */
+/* there is non-reboot way to recover if something goes wrong. */
+/*********************************************************************/
+static int
+net_open(struct net_device *dev)
+{
+ struct in_device *in_dev;
+ hysdn_card *card = dev->priv;
+ int i;
+
+ if (!((struct net_local *) dev)->is_open)
+ MOD_INC_USE_COUNT; /* increment only if interface is actually down */
+ ((struct net_local *) dev)->is_open = 1; /* device actually open */
+
+ netif_start_queue(dev); /* start tx-queueing */
+
+ /* Fill in the MAC-level header (if not already set) */
+ if (!card->mac_addr[0]) {
+ for (i = 0; i < ETH_ALEN - sizeof(ulong); i++)
+ dev->dev_addr[i] = 0xfc;
+ if ((in_dev = dev->ip_ptr) != NULL) {
+ struct in_ifaddr *ifa = in_dev->ifa_list;
+ if (ifa != NULL)
+ memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ulong)), &ifa->ifa_local, sizeof(ulong));
+ }
+ } else
+ memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
+
+ return (0);
+} /* net_open */
+
+/*******************************************/
+/* flush the currently occupied tx-buffers */
+/* must only be called when device closed */
+/*******************************************/
+static void
+flush_tx_buffers(struct net_local *nl)
+{
+
+ while (nl->sk_count) {
+ dev_kfree_skb(nl->skbs[nl->out_idx++]); /* free skb */
+ if (nl->out_idx >= MAX_SKB_BUFFERS)
+ nl->out_idx = 0; /* wrap around */
+ nl->sk_count--;
+ }
+} /* flush_tx_buffers */
+
+
+/*********************************************************************/
+/* close/decativate the device. The device is not removed, but only */
+/* deactivated. */
+/*********************************************************************/
+static int
+net_close(struct net_device *dev)
+{
+
+ netif_stop_queue(dev); /* disable queueing */
+
+ if (((struct net_local *) dev)->is_open)
+ MOD_DEC_USE_COUNT; /* adjust module counter */
+ ((struct net_local *) dev)->is_open = 0;
+ flush_tx_buffers((struct net_local *) dev);
+
+ return (0); /* success */
+} /* net_close */
+
+/************************************/
+/* send a packet on this interface. */
+/* new style for kernel >= 2.3.33 */
+/************************************/
+static int
+net_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) dev;
+
+ spin_lock_irq(&lp->lock);
+
+ lp->skbs[lp->in_idx++] = skb; /* add to buffer list */
+ if (lp->in_idx >= MAX_SKB_BUFFERS)
+ lp->in_idx = 0; /* wrap around */
+ lp->sk_count++; /* adjust counter */
+ dev->trans_start = jiffies;
+
+ /* If we just used up the very last entry in the
+ * TX ring on this device, tell the queueing
+ * layer to send no more.
+ */
+ if (lp->sk_count >= MAX_SKB_BUFFERS)
+ netif_stop_queue(dev);
+
+ /* When the TX completion hw interrupt arrives, this
+ * is when the transmit statistics are updated.
+ */
+
+ spin_unlock_irq(&lp->lock);
+
+ if (lp->sk_count <= 3) {
+ queue_task(&((hysdn_card *) dev->priv)->irq_queue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+ return (0); /* success */
+} /* net_send_packet */
+
+
+
+/***********************************************************************/
+/* acknowlegde a packet send. The network layer will be informed about */
+/* completion */
+/***********************************************************************/
+void
+hysdn_tx_netack(hysdn_card * card)
+{
+ struct net_local *lp = card->netif;
+
+ if (!lp)
+ return; /* non existing device */
+
+
+ if (!lp->sk_count)
+ return; /* error condition */
+
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
+
+ dev_kfree_skb(lp->skbs[lp->out_idx++]); /* free skb */
+ if (lp->out_idx >= MAX_SKB_BUFFERS)
+ lp->out_idx = 0; /* wrap around */
+
+ if (lp->sk_count-- == MAX_SKB_BUFFERS) /* dec usage count */
+ netif_start_queue((struct net_device *) lp);
+} /* hysdn_tx_netack */
+
+/*****************************************************/
+/* we got a packet from the network, go and queue it */
+/*****************************************************/
+void
+hysdn_rx_netpkt(hysdn_card * card, uchar * buf, word len)
+{
+ struct net_local *lp = card->netif;
+ struct sk_buff *skb;
+
+ if (!lp)
+ return; /* non existing device */
+
+ lp->stats.rx_bytes += len;
+
+ skb = dev_alloc_skb(len);
+ if (skb == NULL) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+ lp->netdev.name);
+ lp->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = &lp->netdev;
+
+ /* copy the data */
+ memcpy(skb_put(skb, len), buf, len);
+
+ /* determine the used protocol */
+ skb->protocol = eth_type_trans(skb, &lp->netdev);
+
+ netif_rx(skb);
+ lp->stats.rx_packets++; /* adjust packet count */
+
+} /* hysdn_rx_netpkt */
+
+/*****************************************************/
+/* return the pointer to a network packet to be send */
+/*****************************************************/
+struct sk_buff *
+hysdn_tx_netget(hysdn_card * card)
+{
+ struct net_local *lp = card->netif;
+
+ if (!lp)
+ return (NULL); /* non existing device */
+
+ if (!lp->sk_count)
+ return (NULL); /* nothing available */
+
+ return (lp->skbs[lp->out_idx]); /* next packet to send */
+} /* hysdn_tx_netget */
+
+
+/*******************************************/
+/* init function called by register device */
+/*******************************************/
+static int
+net_init(struct net_device *dev)
+{
+ /* setup the function table */
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit = net_send_packet;
+ dev->get_stats = net_get_stats;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ return (0); /* success */
+} /* net_init */
+
+/*****************************************************************************/
+/* hysdn_net_create creates a new net device for the given card. If a device */
+/* already exists, it will be deleted and created a new one. The return value */
+/* 0 announces success, else a negative error code will be returned. */
+/*****************************************************************************/
+int
+hysdn_net_create(hysdn_card * card)
+{
+ struct net_device *dev;
+ int i;
+
+ hysdn_net_release(card); /* release an existing net device */
+ if ((dev = kmalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
+ if (card->debug_flags & LOG_NET_INIT)
+ return (-ENOMEM);
+ }
+ memset(dev, 0, sizeof(struct net_local)); /* clean the structure */
+
+ spin_lock_init(&((struct net_local *) dev)->lock);
+
+ /* initialise necessary or informing fields */
+ dev->base_addr = card->iobase; /* IO address */
+ dev->irq = card->irq; /* irq */
+ dev->init = net_init; /* the init function of the device */
+ dev->name = ((struct net_local *) dev)->dev_name; /* device name */
+ if ((i = register_netdev(dev))) {
+ printk(KERN_WARNING "HYSDN: unable to create network device\n");
+ kfree(dev);
+ return (i);
+ }
+ dev->priv = card; /* remember pointer to own data structure */
+ card->netif = dev; /* setup the local pointer */
+
+ if (card->debug_flags & LOG_NET_INIT)
+ hysdn_addlog(card, "network device created");
+ return (0); /* and return success */
+} /* hysdn_net_create */
+
+/***************************************************************************/
+/* hysdn_net_release deletes the net device for the given card. The return */
+/* value 0 announces success, else a negative error code will be returned. */
+/***************************************************************************/
+int
+hysdn_net_release(hysdn_card * card)
+{
+ struct net_device *dev = card->netif;
+
+ if (!dev)
+ return (0); /* non existing */
+
+ card->netif = NULL; /* clear out pointer */
+ dev->stop(dev); /* close the device */
+
+ flush_tx_buffers((struct net_local *) dev); /* empty buffers */
+
+ unregister_netdev(dev); /* release the device */
+ kfree(dev); /* release the memory allocated */
+ if (card->debug_flags & LOG_NET_INIT)
+ hysdn_addlog(card, "network device deleted");
+
+ return (0); /* always successfull */
+} /* hysdn_net_release */
+
+/*****************************************************************************/
+/* hysdn_net_getname returns a pointer to the name of the network interface. */
+/* if the interface is not existing, a "-" is returned. */
+/*****************************************************************************/
+char *
+hysdn_net_getname(hysdn_card * card)
+{
+ struct net_device *dev = card->netif;
+
+ if (!dev)
+ return ("-"); /* non existing */
+
+ return (dev->name);
+} /* hysdn_net_getname */
diff --git a/drivers/isdn/hysdn/hysdn_pof.h b/drivers/isdn/hysdn/hysdn_pof.h
new file mode 100644
index 000000000..619c07897
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_pof.h
@@ -0,0 +1,95 @@
+/* $Id: hysdn_pof.h,v 1.1 2000/02/10 19:44:30 werner Exp $
+
+ * Linux driver for HYSDN cards, definitions used for handling pof-files.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: hysdn_pof.h,v $
+ * Revision 1.1 2000/02/10 19:44:30 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+/************************/
+/* POF specific defines */
+/************************/
+#define BOOT_BUF_SIZE 0x1000 /* =4096, maybe moved to other h file */
+#define CRYPT_FEEDTERM 0x8142
+#define CRYPT_STARTTERM 0x81a5
+ /* max. timeout time in seconds
+ * from end of booting to POF is ready
+ */
+#define POF_READY_TIME_OUT_SEC 10
+
+/**********************************/
+/* defines for 1.stage boot image */
+/**********************************/
+
+/* the POF file record containing the boot loader image
+ * has 2 pages a 16KB:
+ * 1. page contains the high 16-bit part of the 32-bit E1 words
+ * 2. page contains the low 16-bit part of the 32-bit E1 words
+ *
+ * In each 16KB page we assume the start of the boot loader code
+ * in the highest 2KB part (at offset 0x3800);
+ * the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
+ */
+
+#define POF_BOOT_LOADER_PAGE_SIZE 0x4000 /* =16384U */
+#define POF_BOOT_LOADER_TOTAL_SIZE (2U*POF_BOOT_LOADER_PAGE_SIZE)
+
+#define POF_BOOT_LOADER_CODE_SIZE 0x0800 /* =2KB =2048U */
+
+ /* offset in boot page, where loader code may start */
+ /* =0x3800= 14336U */
+#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
+
+
+/*--------------------------------------POF file record structs------------*/
+typedef struct PofFileHdr_tag { /* Pof file header */
+/*00 */ ulong Magic __attribute__((packed));
+/*04 */ ulong N_PofRecs __attribute__((packed));
+/*08 */
+} tPofFileHdr;
+
+typedef struct PofRecHdr_tag { /* Pof record header */
+/*00 */ word PofRecId __attribute__((packed));
+/*02 */ ulong PofRecDataLen __attribute__((packed));
+/*06 */
+} tPofRecHdr;
+
+typedef struct PofTimeStamp_tag {
+/*00 */ ulong UnixTime __attribute__((packed));
+ /*04 */ uchar DateTimeText[0x28] __attribute__((packed));
+ /* =40 */
+/*2C */
+} tPofTimeStamp;
+
+ /* tPofFileHdr.Magic value: */
+#define TAGFILEMAGIC 0x464F501AUL
+ /* tPofRecHdr.PofRecId values: */
+#define TAG_ABSDATA 0x1000 /* abs. data */
+#define TAG_BOOTDTA 0x1001 /* boot data */
+#define TAG_COMMENT 0x0020
+#define TAG_SYSCALL 0x0021
+#define TAG_FLOWCTRL 0x0022
+#define TAG_TIMESTMP 0x0010 /* date/time stamp of version */
+#define TAG_CABSDATA 0x1100 /* crypted abs. data */
+#define TAG_CBOOTDTA 0x1101 /* crypted boot data */
diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c
new file mode 100644
index 000000000..81e1b84f0
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_procconf.c
@@ -0,0 +1,485 @@
+/* $Id: hysdn_procconf.c,v 1.2 2000/02/14 19:23:03 werner Exp $
+
+ * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This 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.
+ *
+ * $Log: hysdn_procconf.c,v $
+ * Revision 1.2 2000/02/14 19:23:03 werner
+ *
+ * Changed handling of proc filesystem tables to a more portable version
+ *
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_procconf_revision = "$Revision: 1.2 $";
+
+#define INFO_OUT_LEN 80 /* length of info line including lf */
+
+/********************************************************/
+/* defines and data structure for conf write operations */
+/********************************************************/
+#define CONF_STATE_DETECT 0 /* waiting for detect */
+#define CONF_STATE_CONF 1 /* writing config data */
+#define CONF_STATE_POF 2 /* writing pof data */
+#define CONF_LINE_LEN 80 /* 80 chars max */
+
+struct conf_writedata {
+ hysdn_card *card; /* card the device is connected to */
+ int buf_size; /* actual number of bytes in the buffer */
+ int needed_size; /* needed size when reading pof */
+ int state; /* actual interface states from above constants */
+ uchar conf_line[CONF_LINE_LEN]; /* buffered conf line */
+ word channel; /* active channel number */
+ uchar *pof_buffer; /* buffer when writing pof */
+};
+
+/***********************************************************************/
+/* process_line parses one config line and transfers it to the card if */
+/* necessary. */
+/* if the return value is negative an error occured. */
+/***********************************************************************/
+static int
+process_line(struct conf_writedata *cnf)
+{
+ uchar *cp = cnf->conf_line;
+ int i;
+
+ if (cnf->card->debug_flags & LOG_CNF_LINE)
+ hysdn_addlog(cnf->card, "conf line: %s", cp);
+
+ if (*cp == '-') { /* option */
+ cp++; /* point to option char */
+
+ if (*cp++ != 'c')
+ return (0); /* option unknown or used */
+ i = 0; /* start value for channel */
+ while ((*cp <= '9') && (*cp >= '0'))
+ i = i * 10 + *cp++ - '0'; /* get decimal number */
+ if (i > 65535) {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "conf channel invalid %d", i);
+ return (-ERR_INV_CHAN); /* invalid channel */
+ }
+ cnf->channel = i & 0xFFFF; /* set new channel number */
+ return (0); /* success */
+ } /* option */
+ if (*cp == '*') { /* line to send */
+ if (cnf->card->debug_flags & LOG_CNF_DATA)
+ hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
+ return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
+ cnf->channel)); /* send the line without * */
+ } /* line to send */
+ return (0);
+} /* process_line */
+
+/*************************/
+/* dummy file operations */
+/*************************/
+static loff_t
+hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
+{
+ return -ESPIPE;
+} /* hysdn_dummy_lseek */
+
+/***********************************/
+/* conf file operations and tables */
+/***********************************/
+
+/****************************************************/
+/* write conf file -> boot or send cfg line to card */
+/****************************************************/
+static ssize_t
+hysdn_conf_write(struct file *file, const char *buf, size_t count, loff_t * off)
+{
+ struct conf_writedata *cnf;
+ int i;
+ uchar ch, *cp;
+
+ if (&file->f_pos != off) /* fs error check */
+ return (-ESPIPE);
+ if (!count)
+ return (0); /* nothing to handle */
+
+ if (!(cnf = file->private_data))
+ return (-EFAULT); /* should never happen */
+
+ if (cnf->state == CONF_STATE_DETECT) { /* auto detect cnf or pof data */
+ if (copy_from_user(&ch, buf, 1)) /* get first char for detect */
+ return (-EFAULT);
+
+ if (ch == 0x1A) {
+ /* we detected a pof file */
+ if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
+ return (cnf->needed_size); /* an error occured -> exit */
+ cnf->buf_size = 0; /* buffer is empty */
+ cnf->state = CONF_STATE_POF; /* new state */
+ } else {
+ /* conf data has been detected */
+ cnf->buf_size = 0; /* buffer is empty */
+ cnf->state = CONF_STATE_CONF; /* requested conf data write */
+ if (cnf->card->state != CARD_STATE_RUN)
+ return (-ERR_NOT_BOOTED);
+ cnf->conf_line[CONF_LINE_LEN - 1] = 0; /* limit string length */
+ cnf->channel = 4098; /* default channel for output */
+ }
+ } /* state was auto detect */
+ if (cnf->state == CONF_STATE_POF) { /* pof write active */
+ i = cnf->needed_size - cnf->buf_size; /* bytes still missing for write */
+ if (i <= 0)
+ return (-EINVAL); /* size error handling pof */
+
+ if (i < count)
+ count = i; /* limit requested number of bytes */
+ if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
+ return (-EFAULT); /* error while copying */
+ cnf->buf_size += count;
+
+ if (cnf->needed_size == cnf->buf_size) {
+ cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size); /* write data */
+ if (cnf->needed_size <= 0) {
+ cnf->card->state = CARD_STATE_BOOTERR; /* show boot error */
+ return (cnf->needed_size); /* an error occured */
+ }
+ cnf->buf_size = 0; /* buffer is empty again */
+ }
+ }
+ /* pof write active */
+ else { /* conf write active */
+
+ if (cnf->card->state != CARD_STATE_RUN) {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "cnf write denied -> not booted");
+ return (-ERR_NOT_BOOTED);
+ }
+ i = (CONF_LINE_LEN - 1) - cnf->buf_size; /* bytes available in buffer */
+ if (i > 0) {
+ /* copy remaining bytes into buffer */
+
+ if (count > i)
+ count = i; /* limit transfer */
+ if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
+ return (-EFAULT); /* error while copying */
+
+ i = count; /* number of chars in buffer */
+ cp = cnf->conf_line + cnf->buf_size;
+ while (i) {
+ /* search for end of line */
+ if ((*cp < ' ') && (*cp != 9))
+ break; /* end of line found */
+ cp++;
+ i--;
+ } /* search for end of line */
+
+ if (i) {
+ /* delimiter found */
+ *cp++ = 0; /* string termination */
+ count -= (i - 1); /* subtract remaining bytes from count */
+ while ((i) && (*cp < ' ') && (*cp != 9)) {
+ i--; /* discard next char */
+ count++; /* mark as read */
+ cp++; /* next char */
+ }
+ cnf->buf_size = 0; /* buffer is empty after transfer */
+ if ((i = process_line(cnf)) < 0) /* handle the line */
+ count = i; /* return the error */
+ }
+ /* delimiter found */
+ else {
+ cnf->buf_size += count; /* add chars to string */
+ if (cnf->buf_size >= CONF_LINE_LEN - 1) {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
+ return (-ERR_CONF_LONG);
+ }
+ } /* not delimited */
+
+ }
+ /* copy remaining bytes into buffer */
+ else {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "cnf line too long");
+ return (-ERR_CONF_LONG);
+ }
+ } /* conf write active */
+
+ return (count);
+} /* hysdn_conf_write */
+
+/*******************************************/
+/* read conf file -> output card info data */
+/*******************************************/
+static ssize_t
+hysdn_conf_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+ char *cp;
+ int i;
+
+ if (off != &file->f_pos) /* fs error check */
+ return -ESPIPE;
+
+ if (file->f_mode & FMODE_READ) {
+ if (!(cp = file->private_data))
+ return (-EFAULT); /* should never happen */
+ i = strlen(cp); /* get total string length */
+ if (*off < i) {
+ /* still bytes to transfer */
+ cp += *off; /* point to desired data offset */
+ i -= *off; /* remaining length */
+ if (i > count)
+ i = count; /* limit length to transfer */
+ if (copy_to_user(buf, cp, i))
+ return (-EFAULT); /* copy error */
+ *off += i; /* adjust offset */
+ } else
+ return (0);
+ } else
+ return (-EPERM); /* no permission to read */
+
+ return (i);
+} /* hysdn_conf_read */
+
+/******************/
+/* open conf file */
+/******************/
+static int
+hysdn_conf_open(struct inode *ino, struct file *filep)
+{
+ hysdn_card *card;
+ struct proc_dir_entry *pd;
+ struct conf_writedata *cnf;
+ char *cp, *tmp;
+
+ MOD_INC_USE_COUNT; /* lock module */
+
+ /* now search the addressed card */
+ card = card_root;
+ while (card) {
+ pd = card->procconf;
+ if (pd->low_ino == (ino->i_ino & 0xFFFF))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card) {
+ MOD_DEC_USE_COUNT; /* unlock module */
+ return (-ENODEV); /* device is unknown/invalid */
+ }
+ if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+ hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
+ filep->f_uid, filep->f_gid, filep->f_mode);
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write boot file or conf line */
+
+ if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
+ MOD_DEC_USE_COUNT;
+ return (-EFAULT);
+ }
+ cnf->card = card;
+ cnf->buf_size = 0; /* nothing buffered */
+ cnf->state = CONF_STATE_DETECT; /* start auto detect */
+ filep->private_data = cnf;
+
+ } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+ /* read access -> output card info data */
+
+ if (!(tmp = (char *) kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
+ MOD_DEC_USE_COUNT;
+ return (-EFAULT); /* out of memory */
+ }
+ filep->private_data = tmp; /* start of string */
+
+ /* first output a headline */
+ sprintf(tmp, "id bus slot type irq iobase dp-mem b-chans fax-chans state device");
+ cp = tmp; /* start of string */
+ while (*cp)
+ cp++;
+ while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+ *cp++ = ' ';
+ *cp++ = '\n';
+
+ /* and now the data */
+ sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08x %7d %9d %3d %s",
+ card->myid,
+ card->bus,
+ PCI_SLOT(card->devfn),
+ card->brdtype,
+ card->irq,
+ card->iobase,
+ card->membase,
+ card->bchans,
+ card->faxchans,
+ card->state,
+ hysdn_net_getname(card));
+ while (*cp)
+ cp++;
+ while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+ *cp++ = ' ';
+ *cp++ = '\n';
+ *cp = 0; /* end of string */
+ } else { /* simultaneous read/write access forbidden ! */
+ MOD_DEC_USE_COUNT; /* unlock module */
+ return (-EPERM); /* no permission this time */
+ }
+ return (0);
+} /* hysdn_conf_open */
+
+/***************************/
+/* close a config file. */
+/***************************/
+static int
+hysdn_conf_close(struct inode *ino, struct file *filep)
+{
+ hysdn_card *card;
+ struct conf_writedata *cnf;
+ int retval = 0;
+ struct proc_dir_entry *pd;
+
+ /* search the addressed card */
+ card = card_root;
+ while (card) {
+ pd = card->procconf;
+ if (pd->low_ino == (ino->i_ino & 0xFFFF))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card) {
+ return (-ENODEV); /* device is unknown/invalid */
+ }
+ if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+ hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
+ filep->f_uid, filep->f_gid, filep->f_mode);
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write boot file or conf line */
+ if (filep->private_data) {
+ cnf = filep->private_data;
+
+ if (cnf->state == CONF_STATE_POF)
+ retval = pof_write_close(cnf->card); /* close the pof write */
+ kfree(filep->private_data); /* free allocated memory for buffer */
+
+ } /* handle write private data */
+ } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+ /* read access -> output card info data */
+
+ if (filep->private_data)
+ kfree(filep->private_data); /* release memory */
+ }
+ MOD_DEC_USE_COUNT; /* reduce usage count */
+ return (retval);
+} /* hysdn_conf_close */
+
+/******************************************************/
+/* table for conf filesystem functions defined above. */
+/******************************************************/
+static struct file_operations conf_fops =
+{
+ hysdn_dummy_lseek,
+ hysdn_conf_read,
+ hysdn_conf_write,
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ hysdn_conf_open,
+ NULL, /* flush */
+ hysdn_conf_close,
+ NULL /* fsync */
+};
+
+static struct inode_operations conf_inode_operations;
+
+/*****************************/
+/* hysdn subdir in /proc/net */
+/*****************************/
+struct proc_dir_entry *hysdn_proc_entry = NULL;
+
+/*******************************************************************************/
+/* hysdn_procconf_init is called when the module is loaded and after the cards */
+/* have been detected. The needed proc dir and card config files are created. */
+/* The log init is called at last. */
+/*******************************************************************************/
+int
+hysdn_procconf_init(void)
+{
+ hysdn_card *card;
+ uchar conf_name[20];
+
+ hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
+ if (!hysdn_proc_entry) {
+ printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
+ return (-1);
+ }
+ card = card_root; /* point to first card */
+ while (card) {
+
+ sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+ if ((card->procconf = (void *) create_proc_entry(conf_name,
+ S_IFREG | S_IRUGO | S_IWUSR,
+ hysdn_proc_entry)) != NULL) {
+ memset(&conf_inode_operations, 0, sizeof(struct inode_operations));
+ conf_inode_operations.default_file_ops = &conf_fops;
+
+ ((struct proc_dir_entry *) card->procconf)->ops = &conf_inode_operations;
+ hysdn_proclog_init(card); /* init the log file entry */
+ }
+ card = card->next; /* next entry */
+ }
+
+ printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procconf_revision));
+ return (0);
+} /* hysdn_procconf_init */
+
+/*************************************************************************************/
+/* hysdn_procconf_release is called when the module is unloaded and before the cards */
+/* resources are released. The module counter is assumed to be 0 ! */
+/*************************************************************************************/
+void
+hysdn_procconf_release(void)
+{
+ hysdn_card *card;
+ uchar conf_name[20];
+
+ card = card_root; /* start with first card */
+ while (card) {
+
+ sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+ if (card->procconf)
+ remove_proc_entry(conf_name, hysdn_proc_entry);
+
+ hysdn_proclog_release(card); /* init the log file entry */
+
+ card = card->next; /* point to next card */
+ }
+
+ remove_proc_entry(PROC_SUBDIR_NAME, proc_net);
+} /* hysdn_procfs_release */
diff --git a/drivers/isdn/hysdn/hysdn_procfs.c b/drivers/isdn/hysdn/hysdn_procfs.c
new file mode 100644
index 000000000..d70d350e9
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_procfs.c
@@ -0,0 +1,502 @@
+/* $Id: hysdn_procfs.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, /proc/net filesystem log functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: hysdn_procfs.c,v $
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_procfs_revision = "$Revision: 1.1 $";
+
+#define INFO_OUT_LEN 80 /* length of info line including lf */
+
+/*************************************************/
+/* structure keeping ascii log for device output */
+/*************************************************/
+struct log_data {
+ struct log_data *next;
+ ulong usage_cnt; /* number of files still to work */
+ void *proc_ctrl; /* pointer to own control procdata structure */
+ char log_start[2]; /* log string start (final len aligned by size) */
+};
+
+/**********************************************/
+/* structure holding proc entrys for one card */
+/**********************************************/
+struct procdata {
+ struct proc_dir_entry *log; /* log entry */
+ char log_name[15]; /* log filename */
+ struct log_data *log_head, *log_tail; /* head and tail for queue */
+ int if_used; /* open count for interface */
+ wait_queue_head_t rd_queue;
+};
+
+/********************************************/
+/* put an log buffer into the log queue. */
+/* This buffer will be kept until all files */
+/* opened for read got the contents. */
+/* Flushes buffers not longer in use. */
+/********************************************/
+void
+put_log_buffer(hysdn_card * card, char *cp)
+{
+ struct log_data *ib;
+ struct procdata *pd = card->procfs;
+ int flags;
+
+ if (!pd)
+ return;
+ if (!cp)
+ return;
+ if (!*cp)
+ return;
+ if (pd->if_used <= 0)
+ return; /* no open file for read */
+
+ if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+ return; /* no memory */
+ strcpy(ib->log_start, cp); /* set output string */
+ ib->next = NULL;
+ ib->proc_ctrl = pd; /* point to own control structure */
+ save_flags(flags);
+ cli();
+ ib->usage_cnt = pd->if_used;
+ if (!pd->log_head)
+ pd->log_head = ib; /* new head */
+ else
+ pd->log_tail->next = ib; /* follows existing messages */
+ pd->log_tail = ib; /* new tail */
+ restore_flags(flags);
+
+ /* delete old entrys */
+ while (pd->log_head->next) {
+ if ((pd->log_head->usage_cnt <= 0) &&
+ (pd->log_head->next->usage_cnt <= 0)) {
+ ib = pd->log_head;
+ pd->log_head = pd->log_head->next;
+ kfree(ib);
+ } else
+ break;
+ } /* pd->log_head->next */
+ wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */
+} /* put_log_buffer */
+
+
+/*************************/
+/* dummy file operations */
+/*************************/
+static loff_t
+hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
+{
+ return -ESPIPE;
+} /* hysdn_dummy_lseek */
+
+/**********************************/
+/* log file operations and tables */
+/**********************************/
+
+/****************************************/
+/* write log file -> set log level bits */
+/****************************************/
+static ssize_t
+hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off)
+{
+ int retval;
+ hysdn_card *card = (hysdn_card *) file->private_data;
+
+ if (&file->f_pos != off) /* fs error check */
+ return (-ESPIPE);
+
+ if ((retval = pof_boot_write(card, buf, count)) < 0)
+ retval = -EFAULT; /* an error occured */
+
+ return (retval);
+} /* hysdn_log_write */
+
+/******************/
+/* read log file */
+/******************/
+static ssize_t
+hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+ struct log_data *inf;
+ int len;
+ word ino;
+ struct procdata *pd;
+ hysdn_card *card;
+
+ if (!*((struct log_data **) file->private_data)) {
+ if (file->f_flags & O_NONBLOCK)
+ return (-EAGAIN);
+
+ /* sorry, but we need to search the card */
+ ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
+ card = card_root;
+ while (card) {
+ pd = card->procfs;
+ if (pd->log->low_ino == ino)
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (card)
+ interruptible_sleep_on(&(pd->rd_queue));
+ else
+ return (-EAGAIN);
+
+ }
+ if (!(inf = *((struct log_data **) file->private_data)))
+ return (0);
+
+ inf->usage_cnt--; /* new usage count */
+ (struct log_data **) file->private_data = &inf->next; /* next structure */
+ if ((len = strlen(inf->log_start)) <= count) {
+ if (copy_to_user(buf, inf->log_start, len))
+ return -EFAULT;
+ file->f_pos += len;
+ return (len);
+ }
+ return (0);
+} /* hysdn_log_read */
+
+/******************/
+/* open log file */
+/******************/
+static int
+hysdn_log_open(struct inode *ino, struct file *filep)
+{
+ hysdn_card *card;
+ struct procdata *pd;
+ ulong flags;
+
+ MOD_INC_USE_COUNT; /* lock module */
+ card = card_root;
+ while (card) {
+ pd = card->procfs;
+ if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card) {
+ MOD_DEC_USE_COUNT; /* unlock module */
+ return (-ENODEV); /* device is unknown/invalid */
+ }
+ filep->private_data = card; /* remember our own card */
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> boot pof data */
+ if (pof_boot_open(card)) {
+ MOD_DEC_USE_COUNT; /* unlock module */
+ return (-EPERM); /* no permission this time */
+ }
+ } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+
+ /* read access -> log/debug read */
+ save_flags(flags);
+ cli();
+ pd->if_used++;
+ if (pd->log_head)
+ (struct log_data **) filep->private_data = &(pd->log_tail->next);
+ else
+ (struct log_data **) filep->private_data = &(pd->log_head);
+ restore_flags(flags);
+
+ } else { /* simultaneous read/write access forbidden ! */
+ MOD_DEC_USE_COUNT; /* unlock module */
+ return (-EPERM); /* no permission this time */
+ }
+ return (0);
+} /* hysdn_log_open */
+
+/*******************************************************************************/
+/* close a cardlog file. If the file has been opened for exclusive write it is */
+/* assumed as pof data input and the pof loader is noticed about. */
+/* Otherwise file is handled as log output. In this case the interface usage */
+/* count is decremented and all buffers are noticed of closing. If this file */
+/* was the last one to be closed, all buffers are freed. */
+/*******************************************************************************/
+static int
+hysdn_log_close(struct inode *ino, struct file *filep)
+{
+ struct log_data *inf;
+ struct procdata *pd;
+ hysdn_card *card;
+ int flags, retval = 0;
+
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write debug completely written */
+ retval = 0; /* success */
+ } else {
+ /* read access -> log/debug read, mark one further file as closed */
+
+ pd = NULL;
+ save_flags(flags);
+ cli();
+ inf = *((struct log_data **) filep->private_data); /* get first log entry */
+ if (inf)
+ pd = (struct procdata *) inf->proc_ctrl; /* still entries there */
+ else {
+ /* no info available -> search card */
+ card = card_root;
+ while (card) {
+ pd = card->procfs;
+ if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (card)
+ pd = card->procfs; /* pointer to procfs ctrl */
+ }
+ if (pd)
+ pd->if_used--; /* decrement interface usage count by one */
+
+ while (inf) {
+ inf->usage_cnt--; /* decrement usage count for buffers */
+ inf = inf->next;
+ }
+ restore_flags(flags);
+
+ if (pd)
+ if (pd->if_used <= 0) /* delete buffers if last file closed */
+ while (pd->log_head) {
+ inf = pd->log_head;
+ pd->log_head = pd->log_head->next;
+ kfree(inf);
+ }
+ } /* read access */
+
+ MOD_DEC_USE_COUNT;
+ return (retval);
+} /* hysdn_log_close */
+
+/*************************************************/
+/* select/poll routine to be able using select() */
+/*************************************************/
+static unsigned int
+hysdn_log_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = 0;
+ word ino;
+ hysdn_card *card;
+ struct procdata *pd;
+
+ if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
+ return (mask); /* no polling for write supported */
+
+ /* we need to search the card */
+ ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
+ card = card_root;
+ while (card) {
+ pd = card->procfs;
+ if (pd->log->low_ino == ino)
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card)
+ return (mask); /* card not found */
+
+ poll_wait(file, &(pd->rd_queue), wait);
+
+ if (*((struct log_data **) file->private_data))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+} /* hysdn_log_poll */
+
+/**************************************************/
+/* table for log filesystem functions defined above. */
+/**************************************************/
+static struct file_operations log_fops =
+{
+ hysdn_dummy_lseek,
+ hysdn_log_read,
+ hysdn_log_write,
+ NULL, /* readdir */
+ hysdn_log_poll, /* poll */
+ NULL, /*hysdn_log_ioctl, *//* ioctl */
+ NULL, /* mmap */
+ hysdn_log_open,
+ NULL, /* flush */
+ hysdn_log_close,
+ NULL /* fsync */
+};
+
+struct inode_operations log_inode_operations =
+{
+ &log_fops, /* log proc file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+/*****************************************/
+/* Output info data to the cardinfo file */
+/*****************************************/
+static int
+info_read(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+ char tmp[INFO_OUT_LEN * 11 + 2];
+ int i;
+ char *cp;
+ hysdn_card *card;
+
+ sprintf(tmp, "id bus slot type irq iobase plx-mem dp-mem boot device");
+ cp = tmp; /* start of string */
+ while (*cp)
+ cp++;
+ while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+ *cp++ = ' ';
+ *cp++ = '\n';
+
+ card = card_root; /* start of list */
+ while (card) {
+ sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08x 0x%08x",
+ card->myid,
+ card->bus,
+ PCI_SLOT(card->devfn),
+ card->brdtype,
+ card->irq,
+ card->iobase,
+ card->plxbase,
+ card->membase);
+ card = card->next;
+ while (*cp)
+ cp++;
+ while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+ *cp++ = ' ';
+ *cp++ = '\n';
+ }
+
+ i = cp - tmp;
+ *start = buffer;
+ if (offset + length > i) {
+ length = i - offset;
+ *eof = 1;
+ } else if (offset > i) {
+ length = 0;
+ *eof = 1;
+ }
+ cp = tmp + offset;
+
+ if (length > 0) {
+ /* start_bh_atomic(); */
+ memcpy(buffer, cp, length);
+ /* end_bh_atomic(); */
+ return length;
+ }
+ return 0;
+} /* info_read */
+
+/*****************************/
+/* hysdn subdir in /proc/net */
+/*****************************/
+static struct proc_dir_entry *hysdn_proc_entry = NULL;
+static struct proc_dir_entry *hysdn_info_entry = NULL;
+
+/***************************************************************************************/
+/* hysdn_procfs_init is called when the module is loaded and after the cards have been */
+/* detected. The needed proc dir and card entries are created. */
+/***************************************************************************************/
+int
+hysdn_procfs_init(void)
+{
+ struct procdata *pd;
+ hysdn_card *card;
+
+ hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
+ if (!hysdn_proc_entry) {
+ printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
+ return (-1);
+ }
+ hysdn_info_entry = create_proc_entry("cardinfo", 0, hysdn_proc_entry);
+ if (hysdn_info_entry)
+ hysdn_info_entry->read_proc = info_read; /* read info function */
+
+ /* create all cardlog proc entries */
+
+ card = card_root; /* start with first card */
+ while (card) {
+ if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
+ memset(pd, 0, sizeof(struct procdata));
+
+ sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
+ if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL)
+ pd->log->ops = &log_inode_operations; /* set new operations table */
+
+ init_waitqueue_head(&(pd->rd_queue));
+
+ card->procfs = (void *) pd; /* remember procfs structure */
+ }
+ card = card->next; /* point to next card */
+ }
+
+ printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procfs_revision));
+ return (0);
+} /* hysdn_procfs_init */
+
+/***************************************************************************************/
+/* hysdn_procfs_release is called when the module is unloaded and before the cards */
+/* resources are released. The module counter is assumed to be 0 ! */
+/***************************************************************************************/
+void
+hysdn_procfs_release(void)
+{
+ struct procdata *pd;
+ hysdn_card *card;
+
+ card = card_root; /* start with first card */
+ while (card) {
+ if ((pd = (struct procdata *) card->procfs) != NULL) {
+ if (pd->log)
+ remove_proc_entry(pd->log_name, hysdn_proc_entry);
+ kfree(pd); /* release memory */
+ }
+ card = card->next; /* point to next card */
+ }
+
+ remove_proc_entry("cardinfo", hysdn_proc_entry);
+ remove_proc_entry(PROC_SUBDIR_NAME, proc_net);
+} /* hysdn_procfs_release */
diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c
new file mode 100644
index 000000000..aea6a96ad
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_proclog.c
@@ -0,0 +1,481 @@
+/* $Id: hysdn_proclog.c,v 1.2 2000/02/14 19:23:03 werner Exp $
+
+ * Linux driver for HYSDN cards, /proc/net filesystem log functions.
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: hysdn_proclog.c,v $
+ * Revision 1.2 2000/02/14 19:23:03 werner
+ *
+ * Changed handling of proc filesystem tables to a more portable version
+ *
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_proclog_revision = "$Revision: 1.2 $";
+
+/* the proc subdir for the interface is defined in the procconf module */
+extern struct proc_dir_entry *hysdn_proc_entry;
+
+/*************************************************/
+/* structure keeping ascii log for device output */
+/*************************************************/
+struct log_data {
+ struct log_data *next;
+ ulong usage_cnt; /* number of files still to work */
+ void *proc_ctrl; /* pointer to own control procdata structure */
+ char log_start[2]; /* log string start (final len aligned by size) */
+};
+
+/**********************************************/
+/* structure holding proc entrys for one card */
+/**********************************************/
+struct procdata {
+ struct proc_dir_entry *log; /* log entry */
+ char log_name[15]; /* log filename */
+ struct log_data *log_head, *log_tail; /* head and tail for queue */
+ int if_used; /* open count for interface */
+ int volatile del_lock; /* lock for delete operations */
+ uchar logtmp[LOG_MAX_LINELEN];
+ wait_queue_head_t rd_queue;
+};
+
+
+/**********************************************/
+/* log function for cards error log interface */
+/**********************************************/
+void
+hysdn_card_errlog(hysdn_card * card, tErrLogEntry * logp, int maxsize)
+{
+ char buf[ERRLOG_TEXT_SIZE + 40];
+
+ sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
+ put_log_buffer(card, buf); /* output the string */
+} /* hysdn_card_errlog */
+
+/***************************************************/
+/* Log function using format specifiers for output */
+/***************************************************/
+void
+hysdn_addlog(hysdn_card * card, char *fmt,...)
+{
+ struct procdata *pd = card->proclog;
+ char *cp;
+ va_list args;
+
+ if (!pd)
+ return; /* log structure non existent */
+
+ cp = pd->logtmp;
+ cp += sprintf(cp, "HYSDN: card %d ", card->myid);
+
+ va_start(args, fmt);
+ cp += vsprintf(cp, fmt, args);
+ va_end(args);
+ *cp++ = '\n';
+ *cp = 0;
+
+ if (card->debug_flags & DEB_OUT_SYSLOG)
+ printk(KERN_INFO "%s", pd->logtmp);
+ else
+ put_log_buffer(card, pd->logtmp);
+
+} /* hysdn_addlog */
+
+/********************************************/
+/* put an log buffer into the log queue. */
+/* This buffer will be kept until all files */
+/* opened for read got the contents. */
+/* Flushes buffers not longer in use. */
+/********************************************/
+void
+put_log_buffer(hysdn_card * card, char *cp)
+{
+ struct log_data *ib;
+ struct procdata *pd = card->proclog;
+ int i, flags;
+
+ if (!pd)
+ return;
+ if (!cp)
+ return;
+ if (!*cp)
+ return;
+ if (pd->if_used <= 0)
+ return; /* no open file for read */
+
+ if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+ return; /* no memory */
+ strcpy(ib->log_start, cp); /* set output string */
+ ib->next = NULL;
+ ib->proc_ctrl = pd; /* point to own control structure */
+ save_flags(flags);
+ cli();
+ ib->usage_cnt = pd->if_used;
+ if (!pd->log_head)
+ pd->log_head = ib; /* new head */
+ else
+ pd->log_tail->next = ib; /* follows existing messages */
+ pd->log_tail = ib; /* new tail */
+ i = pd->del_lock++; /* get lock state */
+ restore_flags(flags);
+
+ /* delete old entrys */
+ if (!i)
+ while (pd->log_head->next) {
+ if ((pd->log_head->usage_cnt <= 0) &&
+ (pd->log_head->next->usage_cnt <= 0)) {
+ ib = pd->log_head;
+ pd->log_head = pd->log_head->next;
+ kfree(ib);
+ } else
+ break;
+ } /* pd->log_head->next */
+ pd->del_lock--; /* release lock level */
+ wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */
+} /* put_log_buffer */
+
+
+/*************************/
+/* dummy file operations */
+/*************************/
+static loff_t
+hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
+{
+ return -ESPIPE;
+} /* hysdn_dummy_lseek */
+
+/******************************/
+/* file operations and tables */
+/******************************/
+
+/****************************************/
+/* write log file -> set log level bits */
+/****************************************/
+static ssize_t
+hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off)
+{
+ ulong u = 0;
+ int found = 0;
+ uchar *cp, valbuf[128];
+ long base = 10;
+ hysdn_card *card = (hysdn_card *) file->private_data;
+
+ if (&file->f_pos != off) /* fs error check */
+ return (-ESPIPE);
+
+ if (count > (sizeof(valbuf) - 1))
+ count = sizeof(valbuf) - 1; /* limit length */
+ if (copy_from_user(valbuf, buf, count))
+ return (-EFAULT); /* copy failed */
+
+ valbuf[count] = 0; /* terminating 0 */
+ cp = valbuf;
+ if ((count > 2) && (valbuf[0] == '0') && (valbuf[1] == 'x')) {
+ cp += 2; /* pointer after hex modifier */
+ base = 16;
+ }
+ /* scan the input for debug flags */
+ while (*cp) {
+ if ((*cp >= '0') && (*cp <= '9')) {
+ found = 1;
+ u *= base; /* adjust to next digit */
+ u += *cp++ - '0';
+ continue;
+ }
+ if (base != 16)
+ break; /* end of number */
+
+ if ((*cp >= 'a') && (*cp <= 'f')) {
+ found = 1;
+ u *= base; /* adjust to next digit */
+ u += *cp++ - 'a' + 10;
+ continue;
+ }
+ break; /* terminated */
+ }
+
+ if (found) {
+ card->debug_flags = u; /* remember debug flags */
+ hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
+ }
+ return (count);
+} /* hysdn_log_write */
+
+/******************/
+/* read log file */
+/******************/
+static ssize_t
+hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+ struct log_data *inf;
+ int len;
+ word ino;
+ struct procdata *pd;
+ hysdn_card *card;
+
+ if (!*((struct log_data **) file->private_data)) {
+ if (file->f_flags & O_NONBLOCK)
+ return (-EAGAIN);
+
+ /* sorry, but we need to search the card */
+ ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log->low_ino == ino)
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (card)
+ interruptible_sleep_on(&(pd->rd_queue));
+ else
+ return (-EAGAIN);
+
+ }
+ if (!(inf = *((struct log_data **) file->private_data)))
+ return (0);
+
+ inf->usage_cnt--; /* new usage count */
+ (struct log_data **) file->private_data = &inf->next; /* next structure */
+ if ((len = strlen(inf->log_start)) <= count) {
+ if (copy_to_user(buf, inf->log_start, len))
+ return -EFAULT;
+ file->f_pos += len;
+ return (len);
+ }
+ return (0);
+} /* hysdn_log_read */
+
+/******************/
+/* open log file */
+/******************/
+static int
+hysdn_log_open(struct inode *ino, struct file *filep)
+{
+ hysdn_card *card;
+ struct procdata *pd;
+ ulong flags;
+
+ MOD_INC_USE_COUNT; /* lock module */
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card) {
+ MOD_DEC_USE_COUNT; /* unlock module */
+ return (-ENODEV); /* device is unknown/invalid */
+ }
+ filep->private_data = card; /* remember our own card */
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write log level only */
+ } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+
+ /* read access -> log/debug read */
+ save_flags(flags);
+ cli();
+ pd->if_used++;
+ if (pd->log_head)
+ (struct log_data **) filep->private_data = &(pd->log_tail->next);
+ else
+ (struct log_data **) filep->private_data = &(pd->log_head);
+ restore_flags(flags);
+ } else { /* simultaneous read/write access forbidden ! */
+ MOD_DEC_USE_COUNT; /* unlock module */
+ return (-EPERM); /* no permission this time */
+ }
+ return (0);
+} /* hysdn_log_open */
+
+/*******************************************************************************/
+/* close a cardlog file. If the file has been opened for exclusive write it is */
+/* assumed as pof data input and the pof loader is noticed about. */
+/* Otherwise file is handled as log output. In this case the interface usage */
+/* count is decremented and all buffers are noticed of closing. If this file */
+/* was the last one to be closed, all buffers are freed. */
+/*******************************************************************************/
+static int
+hysdn_log_close(struct inode *ino, struct file *filep)
+{
+ struct log_data *inf;
+ struct procdata *pd;
+ hysdn_card *card;
+ int flags, retval = 0;
+
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write debug level written */
+ retval = 0; /* success */
+ } else {
+ /* read access -> log/debug read, mark one further file as closed */
+
+ pd = NULL;
+ save_flags(flags);
+ cli();
+ inf = *((struct log_data **) filep->private_data); /* get first log entry */
+ if (inf)
+ pd = (struct procdata *) inf->proc_ctrl; /* still entries there */
+ else {
+ /* no info available -> search card */
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (card)
+ pd = card->proclog; /* pointer to procfs log */
+ }
+ if (pd)
+ pd->if_used--; /* decrement interface usage count by one */
+
+ while (inf) {
+ inf->usage_cnt--; /* decrement usage count for buffers */
+ inf = inf->next;
+ }
+ restore_flags(flags);
+
+ if (pd)
+ if (pd->if_used <= 0) /* delete buffers if last file closed */
+ while (pd->log_head) {
+ inf = pd->log_head;
+ pd->log_head = pd->log_head->next;
+ kfree(inf);
+ }
+ } /* read access */
+
+ MOD_DEC_USE_COUNT;
+ return (retval);
+} /* hysdn_log_close */
+
+/*************************************************/
+/* select/poll routine to be able using select() */
+/*************************************************/
+static unsigned int
+hysdn_log_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = 0;
+ word ino;
+ hysdn_card *card;
+ struct procdata *pd;
+
+ if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
+ return (mask); /* no polling for write supported */
+
+ /* we need to search the card */
+ ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log->low_ino == ino)
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card)
+ return (mask); /* card not found */
+
+ poll_wait(file, &(pd->rd_queue), wait);
+
+ if (*((struct log_data **) file->private_data))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+} /* hysdn_log_poll */
+
+/**************************************************/
+/* table for log filesystem functions defined above. */
+/**************************************************/
+static struct file_operations log_fops =
+{
+ hysdn_dummy_lseek,
+ hysdn_log_read,
+ hysdn_log_write,
+ NULL, /* readdir */
+ hysdn_log_poll, /* poll */
+ NULL,
+ NULL, /* mmap */
+ hysdn_log_open,
+ NULL, /* flush */
+ hysdn_log_close,
+ NULL /* fsync */
+};
+
+struct inode_operations log_inode_operations;
+
+/***********************************************************************************/
+/* hysdn_proclog_init is called when the module is loaded after creating the cards */
+/* conf files. */
+/***********************************************************************************/
+int
+hysdn_proclog_init(hysdn_card * card)
+{
+ struct procdata *pd;
+
+ /* create a cardlog proc entry */
+
+ if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
+ memset(pd, 0, sizeof(struct procdata));
+ memset(&log_inode_operations, 0, sizeof(struct inode_operations));
+ log_inode_operations.default_file_ops = &log_fops;
+
+ sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
+ if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL)
+ pd->log->ops = &log_inode_operations; /* set new operations table */
+
+ init_waitqueue_head(&(pd->rd_queue));
+
+ card->proclog = (void *) pd; /* remember procfs structure */
+ }
+ return (0);
+} /* hysdn_proclog_init */
+
+/************************************************************************************/
+/* hysdn_proclog_release is called when the module is unloaded and before the cards */
+/* conf file is released */
+/* The module counter is assumed to be 0 ! */
+/************************************************************************************/
+void
+hysdn_proclog_release(hysdn_card * card)
+{
+ struct procdata *pd;
+
+ if ((pd = (struct procdata *) card->proclog) != NULL) {
+ if (pd->log)
+ remove_proc_entry(pd->log_name, hysdn_proc_entry);
+ kfree(pd); /* release memory */
+ card->proclog = NULL;
+ }
+} /* hysdn_proclog_release */
diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
new file mode 100644
index 000000000..0e5f9e7ba
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_sched.c
@@ -0,0 +1,202 @@
+/* $Id: hysdn_sched.c,v 1.1 2000/02/10 19:45:18 werner Exp $
+
+ * Linux driver for HYSDN cards, scheduler routines for handling exchange card <-> pc.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ * $Log: hysdn_sched.c,v $
+ * Revision 1.1 2000/02/10 19:45:18 werner
+ *
+ * Initial release
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include "hysdn_defs.h"
+
+/*****************************************************************************/
+/* hysdn_sched_rx is called from the cards handler to announce new data is */
+/* available from the card. The routine has to handle the data and return */
+/* with a nonzero code if the data could be worked (or even thrown away), if */
+/* no room to buffer the data is available a zero return tells the card */
+/* to keep the data until later. */
+/*****************************************************************************/
+int
+hysdn_sched_rx(hysdn_card * card, uchar * buf, word len, word chan)
+{
+
+ switch (chan) {
+ case CHAN_NDIS_DATA:
+ hysdn_rx_netpkt(card, buf, len); /* give packet to network handler */
+ break;
+
+ case CHAN_ERRLOG:
+ hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
+ if (card->err_log_state == ERRLOG_STATE_ON)
+ card->err_log_state = ERRLOG_STATE_START; /* start new fetch */
+ break;
+
+ default:
+ printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
+ break;
+
+ } /* switch rx channel */
+
+ return (1); /* always handled */
+} /* hysdn_sched_rx */
+
+/*****************************************************************************/
+/* hysdn_sched_tx is called from the cards handler to announce that there is */
+/* room in the tx-buffer to the card and data may be sent if needed. */
+/* If the routine wants to send data it must fill buf, len and chan with the */
+/* appropriate data and return a nonzero value. With a zero return no new */
+/* data to send is assumed. maxlen specifies the buffer size available for */
+/* sending. */
+/*****************************************************************************/
+int
+hysdn_sched_tx(hysdn_card * card, uchar * buf, word volatile *len, word volatile *chan, word maxlen)
+{
+ struct sk_buff *skb;
+
+ if (card->net_tx_busy) {
+ card->net_tx_busy = 0; /* reset flag */
+ hysdn_tx_netack(card); /* acknowledge packet send */
+ } /* a network packet has completely been transferred */
+ /* first of all async requests are handled */
+ if (card->async_busy) {
+ if (card->async_len <= maxlen) {
+ memcpy(buf, card->async_data, card->async_len);
+ *len = card->async_len;
+ *chan = card->async_channel;
+ card->async_busy = 0; /* reset request */
+ return (1);
+ }
+ card->async_busy = 0; /* in case of length error */
+ } /* async request */
+ if ((card->err_log_state == ERRLOG_STATE_START) &&
+ (maxlen >= ERRLOG_CMD_REQ_SIZE)) {
+ strcpy(buf, ERRLOG_CMD_REQ); /* copy the command */
+ *len = ERRLOG_CMD_REQ_SIZE; /* buffer length */
+ *chan = CHAN_ERRLOG; /* and channel */
+ card->err_log_state = ERRLOG_STATE_ON; /* new state is on */
+ return (1); /* tell that data should be send */
+ } /* error log start and able to send */
+ if ((card->err_log_state == ERRLOG_STATE_STOP) &&
+ (maxlen >= ERRLOG_CMD_STOP_SIZE)) {
+ strcpy(buf, ERRLOG_CMD_STOP); /* copy the command */
+ *len = ERRLOG_CMD_STOP_SIZE; /* buffer length */
+ *chan = CHAN_ERRLOG; /* and channel */
+ card->err_log_state = ERRLOG_STATE_OFF; /* new state is off */
+ return (1); /* tell that data should be send */
+ } /* error log start and able to send */
+ /* now handle network interface packets */
+ if ((skb = hysdn_tx_netget(card)) != NULL) {
+ if (skb->len <= maxlen) {
+ memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */
+ *len = skb->len;
+ *chan = CHAN_NDIS_DATA;
+ card->net_tx_busy = 1; /* we are busy sending network data */
+ return (1); /* go and send the data */
+ } else
+ hysdn_tx_netack(card); /* aknowledge packet -> throw away */
+ } /* send a network packet if available */
+ return (0); /* nothing to send */
+} /* hysdn_sched_tx */
+
+
+/*****************************************************************************/
+/* send one config line to the card and return 0 if successfull, otherwise a */
+/* negative error code. */
+/* The function works with timeouts perhaps not giving the greatest speed */
+/* sending the line, but this should be meaningless beacuse only some lines */
+/* are to be sent and this happens very seldom. */
+/*****************************************************************************/
+int
+hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan)
+{
+ int cnt = 50; /* timeout intervalls */
+ ulong flags;
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
+
+ save_flags(flags);
+ cli();
+ while (card->async_busy) {
+ sti();
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg delayed");
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
+ if (!--cnt) {
+ restore_flags(flags);
+ return (-ERR_ASYNC_TIME); /* timed out */
+ }
+ cli();
+ } /* wait for buffer to become free */
+
+ strcpy(card->async_data, line);
+ card->async_len = strlen(line) + 1;
+ card->async_channel = chan;
+ card->async_busy = 1; /* request transfer */
+
+ /* now queue the task */
+ queue_task(&card->irq_queue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ sti();
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg data queued");
+
+ cnt++; /* short delay */
+ cli();
+
+ while (card->async_busy) {
+ sti();
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
+ if (!--cnt) {
+ restore_flags(flags);
+ return (-ERR_ASYNC_TIME); /* timed out */
+ }
+ cli();
+ } /* wait for buffer to become free again */
+
+ restore_flags(flags);
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg data send");
+
+ return (0); /* line send correctly */
+} /* hysdn_tx_cfgline */
diff --git a/drivers/isdn/hysdn/ince1pc.h b/drivers/isdn/hysdn/ince1pc.h
new file mode 100644
index 000000000..c22974d85
--- /dev/null
+++ b/drivers/isdn/hysdn/ince1pc.h
@@ -0,0 +1,132 @@
+#ifndef __INCE1PC_H__
+#define __INCE1PC_H__
+
+/****************************************************************************
+
+ FILE: ince1pc.h
+
+ AUTHOR: M.Steinkopf
+
+ PURPOSE: common definitions for both sides of the bus:
+ - conventions both spoolers must know
+ - channel numbers agreed upon
+
+*****************************************************************************/
+
+/* basic scalar definitions have same meanning,
+ * but their declaration location depends on environment
+ */
+
+/*--------------------------------------channel numbers---------------------*/
+#define CHAN_SYSTEM 0x0001 /* system channel (spooler to spooler) */
+#define CHAN_ERRLOG 0x0005 /* error logger */
+#define CHAN_CAPI 0x0064 /* CAPI interface */
+#define CHAN_NDIS_DATA 0x1001 /* NDIS data transfer */
+
+/*--------------------------------------POF ready msg-----------------------*/
+ /* NOTE: after booting POF sends system ready message to PC: */
+#define RDY_MAGIC 0x52535953UL /* 'SYSR' reversed */
+#define RDY_MAGIC_SIZE 4 /* size in bytes */
+
+#define MAX_N_TOK_BYTES 255
+
+#define MIN_RDY_MSG_SIZE RDY_MAGIC_SIZE
+#define MAX_RDY_MSG_SIZE (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+
+#define SYSR_TOK_END 0
+#define SYSR_TOK_B_CHAN 1 /* nr. of B-Channels; DataLen=1; def: 2 */
+#define SYSR_TOK_FAX_CHAN 2 /* nr. of FAX Channels; DataLen=1; def: 0 */
+#define SYSR_TOK_MAC_ADDR 3 /* MAC-Address; DataLen=6; def: auto */
+#define SYSR_TOK_ESC 255 /* undefined data size yet */
+ /* default values, if not corrected by token: */
+#define SYSR_TOK_B_CHAN_DEF 2 /* assume 2 B-Channels */
+#define SYSR_TOK_FAX_CHAN_DEF 1 /* assume 1 FAX Channel */
+
+/* syntax of new SYSR token stream:
+ * channel: CHAN_SYSTEM
+ * msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
+ * RDY_MAGIC_SIZE <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+ * msg : 0 1 2 3 {4 5 6 ..}
+ * S Y S R MAX_N_TOK_BYTES bytes of TokenStream
+ *
+ * TokenStream := empty
+ * | {NonEndTokenChunk} EndToken RotlCRC
+ * NonEndTokenChunk:= NonEndTokenId DataLen [Data]
+ * NonEndTokenId := 0x01 .. 0xFE 1 BYTE
+ * DataLen := 0x00 .. 0xFF 1 BYTE
+ * Data := DataLen bytes
+ * EndToken := 0x00
+ * RotlCRC := special 1 byte CRC over all NonEndTokenChunk bytes
+ * s. RotlCRC algorithm
+ *
+ * RotlCRC algorithm:
+ * ucSum= 0 1 uchar
+ * for all NonEndTokenChunk bytes:
+ * ROTL(ucSum,1) rotate left by 1
+ * ucSum += Char; add current byte with swap around
+ * RotlCRC= ~ucSum; invert all bits for result
+ *
+ * note:
+ * - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
+ */
+
+/*--------------------------------------error logger------------------------*/
+ /* note: pof needs final 0 ! */
+#define ERRLOG_CMD_REQ "ERRLOG ON"
+#define ERRLOG_CMD_REQ_SIZE 10 /* with final 0 byte ! */
+#define ERRLOG_CMD_STOP "ERRLOG OFF"
+#define ERRLOG_CMD_STOP_SIZE 11 /* with final 0 byte ! */
+
+#define ERRLOG_ENTRY_SIZE 64 /* sizeof(tErrLogEntry) */
+ /* remaining text size = 55 */
+#define ERRLOG_TEXT_SIZE (ERRLOG_ENTRY_SIZE-2*4-1)
+
+typedef struct ErrLogEntry_tag {
+
+/*00 */ ulong ulErrType;
+
+/*04 */ ulong ulErrSubtype;
+
+/*08 */ uchar ucTextSize;
+
+ /*09 */ uchar ucText[ERRLOG_TEXT_SIZE];
+ /* ASCIIZ of len ucTextSize-1 */
+
+/*40 */
+} tErrLogEntry;
+
+
+#if defined(__TURBOC__)
+#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
+#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
+#endif /* */
+#endif /* */
+
+/*--------------------------------------DPRAM boot spooler------------------*/
+ /* this is the struture used between pc and
+ * hyperstone to exchange boot data
+ */
+#define DPRAM_SPOOLER_DATA_SIZE 0x20
+typedef struct DpramBootSpooler_tag {
+
+/*00 */ uchar Len;
+
+/*01 */ volatile uchar RdPtr;
+
+/*02 */ uchar WrPtr;
+
+/*03 */ uchar Data[DPRAM_SPOOLER_DATA_SIZE];
+
+/*23 */
+} tDpramBootSpooler;
+
+
+#define DPRAM_SPOOLER_MIN_SIZE 5 /* Len+RdPtr+Wrptr+2*data */
+#define DPRAM_SPOOLER_DEF_SIZE 0x23 /* current default size */
+
+/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/
+ /* at DPRAM offset 0x1C00: */
+#define SIZE_RSV_SOFT_UART 0x1B0 /* 432 bytes reserved for SoftUart */
+
+
+#endif /* __INCE1PC_H__ */